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 * Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman * 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 * This file is part of the Arduino Sd2Card Library
*/ */
@ -31,29 +32,92 @@
#if ENABLED(SDSUPPORT) #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 "Sd2Card.h"
#include "../Marlin.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 // send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// select card // select card
chipSelectLow(); chipSelect();
// wait up to 300 ms if busy // 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 // send command
spiSend(cmd | 0x40); spiSend(cmd | 0x40);
// send argument // 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 // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA
uint8_t crc = 0xFF; spiSend( cmd == CMD0 ? 0X95 : 0X87 );
if (cmd == CMD0) crc = 0x95; // correct crc for CMD0 with arg 0 #endif
if (cmd == CMD8) crc = 0x87; // correct crc for CMD8 with arg 0x1AA
spiSend(crc);
// skip stuff byte for stop read // skip stuff byte for stop read
if (cmd == CMD12) spiRec(); if (cmd == CMD12) spiRec();
@ -91,14 +155,15 @@ uint32_t Sd2Card::cardSize() {
} }
} }
void Sd2Card::chipSelectHigh() { void Sd2Card::chipDeselect() {
digitalWrite(chipSelectPin_, HIGH); digitalWrite(chipSelectPin_, HIGH);
// insure MISO goes high impedance
spiSend( 0xFF );
} }
void Sd2Card::chipSelectLow() { void Sd2Card::chipSelect() {
#if DISABLED(SOFTWARE_SPI) spiInit(spiRate_);
spiInit(spiRate_);
#endif // SOFTWARE_SPI
digitalWrite(chipSelectPin_, LOW); digitalWrite(chipSelectPin_, LOW);
} }
@ -142,10 +207,10 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
error(SD_CARD_ERROR_ERASE_TIMEOUT); error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto FAIL; goto FAIL;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
FAIL: FAIL:
chipSelectHigh(); chipDeselect();
return false; return false;
} }
@ -200,22 +265,36 @@ bool Sd2Card::init(uint8_t sckRateID, pin_t chipSelectPin) {
goto FAIL; goto FAIL;
} }
} }
// check SD version
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) { #if ENABLED(SD_CHECK_AND_RETRY)
type(SD_CARD_TYPE_SD1); 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 // only need last byte of r7 response
for (uint8_t i = 0; i < 4; i++) status_ = spiRec(); 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); error(SD_CARD_ERROR_CMD8);
goto FAIL; goto FAIL;
} }
type(SD_CARD_TYPE_SD2);
} }
// initialize card and send host supports SDHC if SD2 // initialize card and send host supports SDHC if SD2
arg = type() == SD_CARD_TYPE_SD2 ? 0x40000000 : 0; arg = type() == SD_CARD_TYPE_SD2 ? 0x40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) { while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// check for timeout // check for timeout
if (((uint16_t)millis() - t0) > SD_INIT_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 // discard rest of ocr - contains allowed voltage range
for (uint8_t i = 0; i < 3; i++) spiRec(); for (uint8_t i = 0; i < 3; i++) spiRec();
} }
chipSelectHigh(); chipDeselect();
#if DISABLED(SOFTWARE_SPI) return setSckRate(sckRateID);
return setSckRate(sckRateID);
#else // SOFTWARE_SPI
UNUSED(sckRateID);
return true;
#endif // SOFTWARE_SPI
FAIL: FAIL:
chipSelectHigh(); chipDeselect();
return false; return false;
} }
@ -268,7 +342,7 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
if (!--retryCnt) break; if (!--retryCnt) break;
chipSelectHigh(); chipDeselect();
cardCommand(CMD12, 0); // Try sending a stop command, ignore the result. cardCommand(CMD12, 0); // Try sending a stop command, ignore the result.
errorCode_ = 0; errorCode_ = 0;
} }
@ -279,7 +353,7 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
return readData(dst, 512); return readData(dst, 512);
#endif #endif
chipSelectHigh(); chipDeselect();
return false; return false;
} }
@ -291,12 +365,13 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
* \return true for success, false for failure. * \return true for success, false for failure.
*/ */
bool Sd2Card::readData(uint8_t* dst) { bool Sd2Card::readData(uint8_t* dst) {
chipSelectLow(); chipSelect();
return readData(dst, 512); return readData(dst, 512);
} }
#if ENABLED(SD_CHECK_AND_RETRY) #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, 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 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, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 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) { static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
uint16_t crc = 0; uint16_t crc = 0;
for (size_t i = 0; i < n; i++) { 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; 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 #endif // SD_CHECK_AND_RETRY
bool Sd2Card::readData(uint8_t* dst, uint16_t count) { 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) #if ENABLED(SD_CHECK_AND_RETRY)
{ {
uint16_t calcCrc = CRC_CCITT(dst, count); uint16_t recvCrc = (spiRec() << 8) | spiRec();
uint16_t recvCrc = spiRec() << 8; if (recvCrc != CRC_CCITT(dst, count)) {
recvCrc |= spiRec(); error(SD_CARD_ERROR_READ_CRC);
if (calcCrc != recvCrc) {
error(SD_CARD_ERROR_CRC);
goto FAIL; goto FAIL;
} }
} }
@ -370,14 +460,10 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
spiRec(); spiRec();
spiRec(); spiRec();
#endif #endif
chipSelectHigh(); chipDeselect();
// Send an additional dummy byte, required by Toshiba Flash Air SD Card
spiSend(0xFF);
return true; return true;
FAIL: FAIL:
chipSelectHigh(); chipDeselect();
// Send an additional dummy byte, required by Toshiba Flash Air SD Card
spiSend(0xFF);
return false; return false;
} }
@ -386,7 +472,7 @@ bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf); uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) { if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG); error(SD_CARD_ERROR_READ_REG);
chipSelectHigh(); chipDeselect();
return false; return false;
} }
return readData(dst, 16); return readData(dst, 16);
@ -406,10 +492,10 @@ bool Sd2Card::readStart(uint32_t blockNumber) {
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD18, blockNumber)) { if (cardCommand(CMD18, blockNumber)) {
error(SD_CARD_ERROR_CMD18); error(SD_CARD_ERROR_CMD18);
chipSelectHigh(); chipDeselect();
return false; return false;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
} }
@ -419,13 +505,13 @@ bool Sd2Card::readStart(uint32_t blockNumber) {
* \return true for success, false for failure. * \return true for success, false for failure.
*/ */
bool Sd2Card::readStop() { bool Sd2Card::readStop() {
chipSelectLow(); chipSelect();
if (cardCommand(CMD12, 0)) { if (cardCommand(CMD12, 0)) {
error(SD_CARD_ERROR_CMD12); error(SD_CARD_ERROR_CMD12);
chipSelectHigh(); chipDeselect();
return false; return false;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
} }
@ -485,10 +571,10 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
error(SD_CARD_ERROR_WRITE_PROGRAMMING); error(SD_CARD_ERROR_WRITE_PROGRAMMING);
goto FAIL; goto FAIL;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
FAIL: FAIL:
chipSelectHigh(); chipDeselect();
return false; return false;
} }
@ -498,28 +584,33 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
* \return true for success, false for failure. * \return true for success, false for failure.
*/ */
bool Sd2Card::writeData(const uint8_t* src) { bool Sd2Card::writeData(const uint8_t* src) {
chipSelectLow(); chipSelect();
// wait for previous write to finish // wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) { if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE); error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh(); chipDeselect();
return false; return false;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
} }
// send one block of data for write block or write multiple blocks // send one block of data for write block or write multiple blocks
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
spiSendBlock(token, src);
spiSend(0xFF); // dummy crc #if ENABLED(SD_CHECK_AND_RETRY)
spiSend(0xFF); // dummy crc 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(); status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE); error(SD_CARD_ERROR_WRITE);
chipSelectHigh(); chipDeselect();
return false; return false;
} }
return true; return true;
@ -548,10 +639,10 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
error(SD_CARD_ERROR_CMD25); error(SD_CARD_ERROR_CMD25);
goto FAIL; goto FAIL;
} }
chipSelectHigh(); chipDeselect();
return true; return true;
FAIL: FAIL:
chipSelectHigh(); chipDeselect();
return false; return false;
} }
@ -561,15 +652,15 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
* \return true for success, false for failure. * \return true for success, false for failure.
*/ */
bool Sd2Card::writeStop() { bool Sd2Card::writeStop() {
chipSelectLow(); chipSelect();
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL; if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL;
spiSend(STOP_TRAN_TOKEN); spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL; if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL;
chipSelectHigh(); chipDeselect();
return true; return true;
FAIL: FAIL:
error(SD_CARD_ERROR_STOP_TRAN); error(SD_CARD_ERROR_STOP_TRAN);
chipSelectHigh(); chipDeselect();
return false; return false;
} }

View file

@ -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_WRITE_TIMEOUT = 0x17, // timeout occurred during write programming
SD_CARD_ERROR_SCK_RATE = 0x18, // incorrect rate selected SD_CARD_ERROR_SCK_RATE = 0x18, // incorrect rate selected
SD_CARD_ERROR_INIT_NOT_CALLED = 0x19, // init() not called 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 // card types
uint8_t const SD_CARD_TYPE_SD1 = 1, // Standard capacity V1 SD card 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 readData(uint8_t* dst, uint16_t count);
bool readRegister(uint8_t cmd, void* buf); bool readRegister(uint8_t cmd, void* buf);
void chipSelectHigh(); void chipDeselect();
void chipSelectLow(); void chipSelect();
void type(uint8_t value) { type_ = value; } void type(uint8_t value) { type_ = value; }
bool waitNotBusy(uint16_t timeoutMillis); bool waitNotBusy(uint16_t timeoutMillis);
bool writeData(uint8_t token, const uint8_t* src); bool writeData(uint8_t token, const uint8_t* src);

View file

@ -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 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 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 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*/ 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 */ CMD38 = 0x26, // ERASE - erase all previously selected blocks
CMD55 = 0x37, // APP_CMD - escape for application specific command */ CMD55 = 0x37, // APP_CMD - escape for application specific command
CMD58 = 0x3A, // READ_OCR - read the OCR register of a card */ 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 */ CMD59 = 0x3B, // CRC_ON_OFF - enable or disable CRC checking
ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process */ 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 */ /** status for card in the ready state */
uint8_t const R1_READY_STATE = 0x00; uint8_t const R1_READY_STATE = 0x00;