Backported latest improvements and fixes from the SdFat liibrary to our own version. This includes support for CRC7 validation on commands and also, made sure that even if using software SPI implementations, SD card speed is properly adjusted when transitioning from INITIALIZATION to DATA TRANSFER mode. ALL HALs implement spiInit, even in SW SPI mode, and we NEED this change to improve 25x the transfer speed of the SD card and allow implementations of USB MSD

This commit is contained in:
etagle
2017-12-21 02:29:00 -03:00
parent 7447979110
commit 9aff55ed1f
3 changed files with 168 additions and 75 deletions

View File

@@ -23,6 +23,7 @@
/**
* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
* Updated with backports of the latest SdFat library from the same author
*
* This file is part of the Arduino Sd2Card Library
*/
@@ -31,29 +32,92 @@
#if ENABLED(SDSUPPORT)
/* Enable FAST CRC computations - You can trade speed for FLASH space if
* needed by disabling the following define */
#define FAST_CRC 1
#include "Sd2Card.h"
#include "../Marlin.h"
#if ENABLED(SD_CHECK_AND_RETRY)
#ifdef FAST_CRC
static const uint8_t crctab7[] PROGMEM = {
0x00,0x09,0x12,0x1b,0x24,0x2d,0x36,0x3f,0x48,0x41,0x5a,0x53,0x6c,0x65,0x7e,0x77,
0x19,0x10,0x0b,0x02,0x3d,0x34,0x2f,0x26,0x51,0x58,0x43,0x4a,0x75,0x7c,0x67,0x6e,
0x32,0x3b,0x20,0x29,0x16,0x1f,0x04,0x0d,0x7a,0x73,0x68,0x61,0x5e,0x57,0x4c,0x45,
0x2b,0x22,0x39,0x30,0x0f,0x06,0x1d,0x14,0x63,0x6a,0x71,0x78,0x47,0x4e,0x55,0x5c,
0x64,0x6d,0x76,0x7f,0x40,0x49,0x52,0x5b,0x2c,0x25,0x3e,0x37,0x08,0x01,0x1a,0x13,
0x7d,0x74,0x6f,0x66,0x59,0x50,0x4b,0x42,0x35,0x3c,0x27,0x2e,0x11,0x18,0x03,0x0a,
0x56,0x5f,0x44,0x4d,0x72,0x7b,0x60,0x69,0x1e,0x17,0x0c,0x05,0x3a,0x33,0x28,0x21,
0x4f,0x46,0x5d,0x54,0x6b,0x62,0x79,0x70,0x07,0x0e,0x15,0x1c,0x23,0x2a,0x31,0x38,
0x41,0x48,0x53,0x5a,0x65,0x6c,0x77,0x7e,0x09,0x00,0x1b,0x12,0x2d,0x24,0x3f,0x36,
0x58,0x51,0x4a,0x43,0x7c,0x75,0x6e,0x67,0x10,0x19,0x02,0x0b,0x34,0x3d,0x26,0x2f,
0x73,0x7a,0x61,0x68,0x57,0x5e,0x45,0x4c,0x3b,0x32,0x29,0x20,0x1f,0x16,0x0d,0x04,
0x6a,0x63,0x78,0x71,0x4e,0x47,0x5c,0x55,0x22,0x2b,0x30,0x39,0x06,0x0f,0x14,0x1d,
0x25,0x2c,0x37,0x3e,0x01,0x08,0x13,0x1a,0x6d,0x64,0x7f,0x76,0x49,0x40,0x5b,0x52,
0x3c,0x35,0x2e,0x27,0x18,0x11,0x0a,0x03,0x74,0x7d,0x66,0x6f,0x50,0x59,0x42,0x4b,
0x17,0x1e,0x05,0x0c,0x33,0x3a,0x21,0x28,0x5f,0x56,0x4d,0x44,0x7b,0x72,0x69,0x60,
0x0e,0x07,0x1c,0x15,0x2a,0x23,0x38,0x31,0x46,0x4f,0x54,0x5d,0x62,0x6b,0x70,0x79
};
static uint8_t CRC7(const uint8_t* data, uint8_t n) {
uint8_t crc = 0;
while ( n > 0 ) {
crc = pgm_read_byte(&crctab7[ (crc << 1) ^ *data++ ]);
n--;
}
return (crc << 1) | 1;
}
#else
static uint8_t CRC7(const uint8_t* data, uint8_t n) {
uint8_t crc = 0;
for (uint8_t i = 0; i < n; i++) {
uint8_t d = data[i];
d ^= crc << 1;
if (d & 0x80) d ^= 9;
crc = d ^ (crc & 0x78) ^ (crc << 4) ^ ((crc >> 3) & 15);
crc &= 0x7f;
}
crc = (crc << 1) ^ (crc << 4) ^ (crc & 0x70) ^ ((crc >> 3) & 0x0f);
return crc | 1;
}
#endif
#endif
// send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// select card
chipSelectLow();
chipSelect();
// wait up to 300 ms if busy
waitNotBusy(300);
waitNotBusy( SD_WRITE_TIMEOUT );
uint8_t *pa = (uint8_t *)(&arg);
#if ENABLED(SD_CHECK_AND_RETRY)
// form message
uint8_t d[6] = {(uint8_t) (cmd | 0x40), pa[3], pa[2], pa[1], pa[0] };
// add crc
d[5] = CRC7(d, 5);
// send message
for (uint8_t k = 0; k < 6; k++ )
spiSend( d[k] );
#else
// send command
spiSend(cmd | 0x40);
// send argument
for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s);
for( int8_t i = 3; i >= 0; i-- )
spiSend( pa[i] );
// send CRC
uint8_t crc = 0xFF;
if (cmd == CMD0) crc = 0x95; // correct crc for CMD0 with arg 0
if (cmd == CMD8) crc = 0x87; // correct crc for CMD8 with arg 0x1AA
spiSend(crc);
// send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA
spiSend( cmd == CMD0 ? 0X95 : 0X87 );
#endif
// skip stuff byte for stop read
if (cmd == CMD12) spiRec();
@@ -91,14 +155,15 @@ uint32_t Sd2Card::cardSize() {
}
}
void Sd2Card::chipSelectHigh() {
void Sd2Card::chipDeselect() {
digitalWrite(chipSelectPin_, HIGH);
// insure MISO goes high impedance
spiSend( 0xFF );
}
void Sd2Card::chipSelectLow() {
#if DISABLED(SOFTWARE_SPI)
spiInit(spiRate_);
#endif // SOFTWARE_SPI
void Sd2Card::chipSelect() {
spiInit(spiRate_);
digitalWrite(chipSelectPin_, LOW);
}
@@ -142,10 +207,10 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto FAIL;
}
chipSelectHigh();
chipDeselect();
return true;
FAIL:
chipSelectHigh();
chipDeselect();
return false;
}
@@ -200,22 +265,36 @@ bool Sd2Card::init(uint8_t sckRateID, pin_t chipSelectPin) {
goto FAIL;
}
}
// check SD version
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
type(SD_CARD_TYPE_SD1);
#if ENABLED(SD_CHECK_AND_RETRY)
if (cardCommand( CMD59, 1 ) != R1_IDLE_STATE) {
error(SD_CARD_ERROR_CMD59);
goto FAIL;
}
else {
#endif
// check SD version
while (1) {
if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) {
type(SD_CARD_TYPE_SD1);
break;
}
// only need last byte of r7 response
for (uint8_t i = 0; i < 4; i++) status_ = spiRec();
if (status_ != 0xAA) {
if (status_ == 0xAA) {
type(SD_CARD_TYPE_SD2);
break;
}
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_CMD8);
goto FAIL;
}
type(SD_CARD_TYPE_SD2);
}
// initialize card and send host supports SDHC if SD2
arg = type() == SD_CARD_TYPE_SD2 ? 0x40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// check for timeout
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
@@ -233,17 +312,12 @@ bool Sd2Card::init(uint8_t sckRateID, pin_t chipSelectPin) {
// discard rest of ocr - contains allowed voltage range
for (uint8_t i = 0; i < 3; i++) spiRec();
}
chipSelectHigh();
chipDeselect();
#if DISABLED(SOFTWARE_SPI)
return setSckRate(sckRateID);
#else // SOFTWARE_SPI
UNUSED(sckRateID);
return true;
#endif // SOFTWARE_SPI
return setSckRate(sckRateID);
FAIL:
chipSelectHigh();
chipDeselect();
return false;
}
@@ -268,7 +342,7 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
if (!--retryCnt) break;
chipSelectHigh();
chipDeselect();
cardCommand(CMD12, 0); // Try sending a stop command, ignore the result.
errorCode_ = 0;
}
@@ -279,7 +353,7 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
return readData(dst, 512);
#endif
chipSelectHigh();
chipDeselect();
return false;
}
@@ -291,12 +365,13 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
* \return true for success, false for failure.
*/
bool Sd2Card::readData(uint8_t* dst) {
chipSelectLow();
chipSelect();
return readData(dst, 512);
}
#if ENABLED(SD_CHECK_AND_RETRY)
static const uint16_t crctab[] PROGMEM = {
#ifdef FAST_CRC
static const uint16_t crctab16[] PROGMEM = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
@@ -330,13 +405,30 @@ bool Sd2Card::readData(uint8_t* dst) {
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
// faster CRC-CCITT
// uses the x^16,x^12,x^5,x^1 polynomial.
static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8);
crc = pgm_read_word(&crctab16[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8);
}
return crc;
}
#else
// slower CRC-CCITT
// uses the x^16,x^12,x^5,x^1 polynomial.
static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = (uint8_t)(crc >> 8) | (crc << 8);
crc ^= data[i];
crc ^= (uint8_t)(crc & 0xff) >> 4;
crc ^= crc << 12;
crc ^= (crc & 0xff) << 5;
}
return crc;
}
#endif
#endif // SD_CHECK_AND_RETRY
bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
@@ -357,11 +449,9 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
#if ENABLED(SD_CHECK_AND_RETRY)
{
uint16_t calcCrc = CRC_CCITT(dst, count);
uint16_t recvCrc = spiRec() << 8;
recvCrc |= spiRec();
if (calcCrc != recvCrc) {
error(SD_CARD_ERROR_CRC);
uint16_t recvCrc = (spiRec() << 8) | spiRec();
if (recvCrc != CRC_CCITT(dst, count)) {
error(SD_CARD_ERROR_READ_CRC);
goto FAIL;
}
}
@@ -370,14 +460,10 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
spiRec();
spiRec();
#endif
chipSelectHigh();
// Send an additional dummy byte, required by Toshiba Flash Air SD Card
spiSend(0xFF);
chipDeselect();
return true;
FAIL:
chipSelectHigh();
// Send an additional dummy byte, required by Toshiba Flash Air SD Card
spiSend(0xFF);
chipDeselect();
return false;
}
@@ -386,7 +472,7 @@ bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
chipSelectHigh();
chipDeselect();
return false;
}
return readData(dst, 16);
@@ -406,10 +492,10 @@ bool Sd2Card::readStart(uint32_t blockNumber) {
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD18, blockNumber)) {
error(SD_CARD_ERROR_CMD18);
chipSelectHigh();
chipDeselect();
return false;
}
chipSelectHigh();
chipDeselect();
return true;
}
@@ -419,13 +505,13 @@ bool Sd2Card::readStart(uint32_t blockNumber) {
* \return true for success, false for failure.
*/
bool Sd2Card::readStop() {
chipSelectLow();
chipSelect();
if (cardCommand(CMD12, 0)) {
error(SD_CARD_ERROR_CMD12);
chipSelectHigh();
chipDeselect();
return false;
}
chipSelectHigh();
chipDeselect();
return true;
}
@@ -485,10 +571,10 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
error(SD_CARD_ERROR_WRITE_PROGRAMMING);
goto FAIL;
}
chipSelectHigh();
chipDeselect();
return true;
FAIL:
chipSelectHigh();
chipDeselect();
return false;
}
@@ -498,28 +584,33 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
* \return true for success, false for failure.
*/
bool Sd2Card::writeData(const uint8_t* src) {
chipSelectLow();
chipSelect();
// wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh();
chipDeselect();
return false;
}
chipSelectHigh();
chipDeselect();
return true;
}
// send one block of data for write block or write multiple blocks
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
spiSendBlock(token, src);
spiSend(0xFF); // dummy crc
spiSend(0xFF); // dummy crc
#if ENABLED(SD_CHECK_AND_RETRY)
uint16_t crc = CRC_CCITT( src, 512 );
#else // ENABLED(SD_CHECK_AND_RETRY)
uint16_t crc = 0xFFFF;
#endif // ENABLED(SD_CHECK_AND_RETRY)
spiSendBlock( token, src );
spiSend( crc >> 8 );
spiSend( crc & 0XFF );
status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
chipSelectHigh();
chipDeselect();
return false;
}
return true;
@@ -548,10 +639,10 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
error(SD_CARD_ERROR_CMD25);
goto FAIL;
}
chipSelectHigh();
chipDeselect();
return true;
FAIL:
chipSelectHigh();
chipDeselect();
return false;
}
@@ -561,15 +652,15 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
* \return true for success, false for failure.
*/
bool Sd2Card::writeStop() {
chipSelectLow();
chipSelect();
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL;
spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL;
chipSelectHigh();
chipDeselect();
return true;
FAIL:
error(SD_CARD_ERROR_STOP_TRAN);
chipSelectHigh();
chipDeselect();
return false;
}