From 9aff55ed1fab03fd26359591848556ed98a63ce9 Mon Sep 17 00:00:00 2001 From: etagle Date: Thu, 21 Dec 2017 02:29:00 -0300 Subject: [PATCH] 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 --- Marlin/src/sd/Sd2Card.cpp | 219 +++++++++++++++++++++++++++----------- Marlin/src/sd/Sd2Card.h | 7 +- Marlin/src/sd/SdInfo.h | 13 +-- 3 files changed, 166 insertions(+), 73 deletions(-) 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; } } + +#if ENABLED(SD_CHECK_AND_RETRY) + if (cardCommand( CMD59, 1 ) != R1_IDLE_STATE) { + error(SD_CARD_ERROR_CMD59); + goto FAIL; + } +#endif + // check SD version - if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) { + while (1) { + if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) { type(SD_CARD_TYPE_SD1); + break; } - else { + // 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;