@ -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 ;
}
}
}
}
# if ENABLED(SD_CHECK_AND_RETRY)
if ( cardCommand ( CMD59 , 1 ) ! = R1_IDLE_STATE ) {
error ( SD_CARD_ERROR_CMD59 ) ;
goto FAIL ;
}
# endif
// check SD version
// 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 ) ;
type ( SD_CARD_TYPE_SD1 ) ;
break ;
}
}
else {
// 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 ;
}
}