diff --git a/Marlin/src/sd/Sd2Card.cpp b/Marlin/src/sd/Sd2Card.cpp index f746a22773..4392c06b28 100644 --- a/Marlin/src/sd/Sd2Card.cpp +++ b/Marlin/src/sd/Sd2Card.cpp @@ -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(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; } diff --git a/Marlin/src/sd/Sd2Card.h b/Marlin/src/sd/Sd2Card.h index 6187db7016..65633aa8c1 100644 --- a/Marlin/src/sd/Sd2Card.h +++ b/Marlin/src/sd/Sd2Card.h @@ -71,7 +71,8 @@ uint8_t const SD_CARD_ERROR_CMD0 = 0x01, // timeout error for com SD_CARD_ERROR_WRITE_TIMEOUT = 0x17, // timeout occurred during write programming SD_CARD_ERROR_SCK_RATE = 0x18, // incorrect rate selected SD_CARD_ERROR_INIT_NOT_CALLED = 0x19, // init() not called - SD_CARD_ERROR_CRC = 0x20; // crc check error + SD_CARD_ERROR_CMD59 = 0x1A, // card returned an error for CMD59 (CRC_ON_OFF) + SD_CARD_ERROR_READ_CRC = 0x1B; // invalid read CRC // card types uint8_t const SD_CARD_TYPE_SD1 = 1, // Standard capacity V1 SD card @@ -196,8 +197,8 @@ class Sd2Card { bool readData(uint8_t* dst, uint16_t count); bool readRegister(uint8_t cmd, void* buf); - void chipSelectHigh(); - void chipSelectLow(); + void chipDeselect(); + void chipSelect(); void type(uint8_t value) { type_ = value; } bool waitNotBusy(uint16_t timeoutMillis); bool writeData(uint8_t token, const uint8_t* src); diff --git a/Marlin/src/sd/SdInfo.h b/Marlin/src/sd/SdInfo.h index 9fe121f168..91cb35da9e 100644 --- a/Marlin/src/sd/SdInfo.h +++ b/Marlin/src/sd/SdInfo.h @@ -54,12 +54,13 @@ uint8_t const CMD0 = 0x00, // GO_IDLE_STATE - init card in spi mode if CS low CMD24 = 0x18, // WRITE_BLOCK - write a single data block to the card CMD25 = 0x19, // WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION CMD32 = 0x20, // ERASE_WR_BLK_START - sets the address of the first block to be erased - CMD33 = 0x21, // ERASE_WR_BLK_END - sets the address of the last block of the continuous range to be erased*/ - CMD38 = 0x26, // ERASE - erase all previously selected blocks */ - CMD55 = 0x37, // APP_CMD - escape for application specific command */ - CMD58 = 0x3A, // READ_OCR - read the OCR register of a card */ - ACMD23 = 0x17, // SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be pre-erased before writing */ - ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process */ + CMD33 = 0x21, // ERASE_WR_BLK_END - sets the address of the last block of the continuous range to be erased + CMD38 = 0x26, // ERASE - erase all previously selected blocks + CMD55 = 0x37, // APP_CMD - escape for application specific command + CMD58 = 0x3A, // READ_OCR - read the OCR register of a card + CMD59 = 0x3B, // CRC_ON_OFF - enable or disable CRC checking + ACMD23 = 0x17, // SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be pre-erased before writing + ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process /** status for card in the ready state */ uint8_t const R1_READY_STATE = 0x00;