Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of SDFileSystem by
Diff: SDFileSystem.cpp
- Revision:
- 11:67ddc53e3983
- Parent:
- 10:395539a1481a
- Child:
- 12:eebddab6eff2
--- a/SDFileSystem.cpp Tue Aug 12 14:58:07 2014 +0000 +++ b/SDFileSystem.cpp Thu Aug 14 22:27:07 2014 +0000 @@ -72,10 +72,10 @@ if (enabled && !m_Crc) { //Send CMD59(0x00000001) to enable CRC m_Crc = true; - writeCommand(CMD59, 0x00000001); + commandTransaction(CMD59, 0x00000001); } else if (!enabled && m_Crc) { //Send CMD59(0x00000000) to disable CRC - writeCommand(CMD59, 0x00000000); + commandTransaction(CMD59, 0x00000000); m_Crc = false; } } @@ -92,9 +92,23 @@ m_LargeFrames = enabled; } +int SDFileSystem::unmount() +{ + //Unmount the filesystem + FATFileSystem::unmount(); + + //Change the status to not initialized, and the card type to none + m_Status |= STA_NOINIT; + m_CardType = CARD_NONE; + + //Always succeeds + return 0; +} + int SDFileSystem::disk_initialize() { - char resp; + char token; + unsigned int resp; //Make sure there's a card in the socket before proceeding checkSocket(); @@ -114,26 +128,32 @@ m_Spi.write(0xFF); //Write CMD0(0x00000000) to reset the card - resp = writeCommand(CMD0, 0x00000000); - if (resp != 0x01) { + if (commandTransaction(CMD0, 0x00000000) != 0x01) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; } + //Send CMD59(0x00000001) to enable CRC if necessary + if (m_Crc) { + if (commandTransaction(CMD59, 0x00000001) != 0x01) { + //Initialization failed + m_CardType = CARD_UNKNOWN; + return m_Status; + } + } + //Write CMD8(0x000001AA) to see if this is an SDCv2 card - resp = writeCommand(CMD8, 0x000001AA); - if (resp == 0x01) { + if (commandTransaction(CMD8, 0x000001AA, &resp) == 0x01) { //This is an SDCv2 card, get the 32-bit return value and verify the voltage range/check pattern - if ((readReturn() & 0xFFF) != 0x1AA) { + if ((resp & 0xFFF) != 0x1AA) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; } //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V - resp = writeCommand(CMD58, 0x00000000); - if (resp != 0x01 || !(readReturn() & (1 << 20))) { + if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; @@ -141,24 +161,23 @@ //Send ACMD41(0x40100000) repeatedly for up to 1 second to initialize the card for (int i = 0; i < 1000; i++) { - resp = writeCommand(ACMD41, 0x40100000); - if (resp != 0x01) + token = commandTransaction(ACMD41, 0x40100000); + if (token != 0x01) break; wait_ms(1); } //Check if the card initialized - if (resp != 0x00) { + if (token != 0x00) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; } //Send CMD58(0x00000000) to read the OCR - resp = writeCommand(CMD58, 0x00000000); - if (resp == 0x00) { + if (commandTransaction(CMD58, 0x00000000, &resp) == 0x00) { //Check the CCS bit to determine if this is a high capacity card - if (readReturn() & (1 << 30)) + if (resp & (1 << 30)) m_CardType = CARD_SDHC; else m_CardType = CARD_SD; @@ -170,8 +189,7 @@ } else { //Didn't respond or illegal command, this is either an SDCv1 or MMC card //Send CMD58(0x00000000) to read the OCR, and verify that the card supports 3.2-3.3V - resp = writeCommand(CMD58, 0x00000000); - if (resp != 0x01 || !(readReturn() & (1 << 20))) { + if (commandTransaction(CMD58, 0x00000000, &resp) != 0x01 || !(resp & (1 << 20))) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; @@ -179,27 +197,27 @@ //Try to initialize the card using ACMD41(0x00100000) for 1 second for (int i = 0; i < 1000; i++) { - resp = writeCommand(ACMD41, 0x00100000); - if (resp != 0x01) + token = commandTransaction(ACMD41, 0x00100000); + if (token != 0x01) break; wait_ms(1); } //Check if the card initialized - if (resp == 0x00) { + if (token == 0x00) { //This is an SDCv1 standard capacity card m_CardType = CARD_SD; } else { //Try to initialize the card using CMD1(0x00100000) for 1 second for (int i = 0; i < 1000; i++) { - resp = writeCommand(CMD1, 0x00100000); - if (resp != 0x01) + token = commandTransaction(CMD1, 0x00100000); + if (token != 0x01) break; wait_ms(1); } //Check if the card initialized - if (resp == 0x00) { + if (token == 0x00) { //This is an MMCv3 card m_CardType = CARD_MMC; } else { @@ -210,20 +228,9 @@ } } - //Send CMD59(0x00000001) to enable CRC if necessary - if (m_Crc) { - resp = writeCommand(CMD59, 0x00000001); - if (resp != 0x00) { - //Initialization failed - m_CardType = CARD_UNKNOWN; - return m_Status; - } - } - //Send CMD16(0x00000200) to force the block size to 512B if necessary if (m_CardType != CARD_SDHC) { - resp = writeCommand(CMD16, 0x00000200); - if (resp != 0x00) { + if (commandTransaction(CMD16, 0x00000200) != 0x00) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; @@ -232,8 +239,7 @@ //Send ACMD42(0x00000000) to disconnect the internal pull-up resistor on pin 1 if necessary if (m_CardType != CARD_MMC) { - resp = writeCommand(ACMD42, 0x00000000); - if (resp != 0x00) { + if (commandTransaction(ACMD42, 0x00000000) != 0x00) { //Initialization failed m_CardType = CARD_UNKNOWN; return m_Status; @@ -264,34 +270,26 @@ return m_Status; } -int SDFileSystem::disk_read(uint8_t* buffer, uint64_t sector) +int SDFileSystem::disk_read(uint8_t* buffer, uint64_t sector, uint8_t count) { //Make sure the card is initialized before proceeding if (m_Status & STA_NOINIT) return RES_NOTRDY; - //Convert from LBA to a byte address for standard capacity cards - if (m_CardType != CARD_SDHC) - sector *= 512; - - //Try to read the block up to 3 times - for (int i = 0; i < 3; i++) { - //Send CMD17(sector) to read a single block - if (writeCommand(CMD17, sector) == 0x00) { - //Try to read the sector, and return if successful - if (readData((char*)buffer, 512)) - return RES_OK; - } else { - //The command failed - return RES_ERROR; - } + //Read a single block, or multiple blocks + if (count > 1) { + if (readBlocks((char*)buffer, sector, count)) + return RES_OK; + } else { + if (readBlock((char*)buffer, sector)) + return RES_OK; } - //The read operation failed 3 times + //The read operation failed return RES_ERROR; } -int SDFileSystem::disk_write(const uint8_t* buffer, uint64_t sector) +int SDFileSystem::disk_write(const uint8_t* buffer, uint64_t sector, uint8_t count) { //Make sure the card is initialized before proceeding if (m_Status & STA_NOINIT) @@ -301,24 +299,16 @@ if (m_Status & STA_PROTECT) return RES_WRPRT; - //Convert from LBA to a byte address for older cards - if (m_CardType != CARD_SDHC) - sector *= 512; - - //Try to write the block up to 3 times - for (int i = 0; i < 3; i++) { - //Send CMD24(sector) to write a single block - if (writeCommand(CMD24, sector) == 0x00) { - //Try to write the sector, and return if successful - if (writeData((char*)buffer)) - return RES_OK; - } else { - //The command failed - return RES_ERROR; - } + //Write a single block, or multiple blocks + if (count > 1) { + if(writeBlocks((const char*)buffer, sector, count)) + return RES_OK; + } else { + if(writeBlock((const char*)buffer, sector)) + return RES_OK; } - //The write operation failed 3 times + //The write operation failed return RES_ERROR; } @@ -340,12 +330,18 @@ return 0; //Try to read the CSD register up to 3 times - for (int i = 0; i < 3; i++) { + for (int f = 0; f < 3; f++) { + //Select the card, and wait for ready + if (!select()) + break; + //Send CMD9(0x00000000) to read the CSD register - if (writeCommand(CMD9, 0x00000000) == 0x00) { - //Receive the 16B CSD data + if (command(CMD9, 0x00000000) == 0x00) { + //Read the 16B CSD data block char csd[16]; - if (readData(csd, 16)) { + bool success = readData(csd, 16); + deselect(); + if (success) { //Calculate the sector count based on the card type if ((csd[0] >> 6) == 0x01) { //Calculate the sector count for a high capacity card @@ -360,12 +356,13 @@ } } } else { - //The command failed - return 0; + //The command failed, get out + break; } } //The read operation failed 3 times + deselect(); return 0; } @@ -418,27 +415,40 @@ //Deassert /CS m_Cs = 1; - //Send 8 dummy clocks with DI held high to disable DO (will also initiate any internal write process) + //Send 8 dummy clocks with DI held high to disable DO m_Spi.write(0xFF); } -char SDFileSystem::writeCommand(char cmd, unsigned int arg) +inline char SDFileSystem::commandTransaction(char cmd, unsigned int arg, unsigned int* resp) { - char resp; + //Select the card, and wait for ready + if (!select()) + return 0xFF; + + //Perform the command transaction + char token = command(cmd, arg, resp); + + //Deselect the card, and return the R1 response token + deselect(); + return token; +} + +char SDFileSystem::command(char cmd, unsigned int arg, unsigned int* resp) +{ + char token; //Try to send the command up to 3 times - for (int i = 0; i < 3; i++) { + for (int f = 0; f < 3; f++) { //Send CMD55(0x00000000) prior to an application specific command - if (cmd == ACMD41 || cmd == ACMD42) { - resp = writeCommand(CMD55, 0x00000000); - if (resp > 0x01) - return resp; + if (cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) { + token = command(CMD55, 0x00000000); + if (token > 0x01) + return token; + + //Some cards need a dummy byte between CMD55 and an ACMD + m_Spi.write(0xFF); } - //Select the card, and wait for ready - if (!select()) - return 0xFF; - //Prepare the command packet char cmdPacket[6]; cmdPacket[0] = cmd; @@ -452,44 +462,50 @@ cmdPacket[5] = 0x01; //Send the command packet - for (int b = 0; b < 6; b++) - m_Spi.write(cmdPacket[b]); + for (int i = 0; i < 6; i++) + m_Spi.write(cmdPacket[i]); - //Allow up to 8 bytes of delay for the command response - for (int b = 0; b < 9; b++) { - resp = m_Spi.write(0xFF); - if (!(resp & 0x80)) + //Discard the stuff byte immediately following CMD12 + if (cmd == CMD12) + m_Spi.write(0xFF); + + //Allow up to 8 bytes of delay for the R1 response token + for (int i = 0; i < 9; i++) { + token = m_Spi.write(0xFF); + if (!(token & 0x80)) break; } - //Deselect the card on errors, or if the transaction is finished - if (resp > 0x01 || !(cmd == CMD8 || cmd == CMD9 || cmd == CMD17 || cmd == CMD24 || cmd == CMD55 || cmd == CMD58)) - deselect(); + //Verify the R1 response token + if (token == 0xFF) { + //No data was received, get out early + break; + } else if (token & (1 << 3)) { + //There was a CRC error, try again + continue; + } else if (token > 0x01) { + //An error occured, get out early + break; + } - //Return the response unless there was a CRC error - if (resp == 0xFF || !(resp & (1 << 3))) - return resp; + //Handle R2 and R3/R7 response tokens + if (cmd == CMD13 && resp != NULL) { + //Read the R2 response value + *resp = m_Spi.write(0xFF); + } else if ((cmd == CMD8 || cmd == CMD58) && resp != NULL) { + //Read the R3/R7 response value + *resp = (m_Spi.write(0xFF) << 24); + *resp |= (m_Spi.write(0xFF) << 16); + *resp |= (m_Spi.write(0xFF) << 8); + *resp |= m_Spi.write(0xFF); + } + + //The command was successful + break; } - //The command failed 3 times - return 0xFF; -} - -unsigned int SDFileSystem::readReturn() -{ - unsigned int ret; - - //Read the 32-bit response value - ret = (m_Spi.write(0xFF) << 24); - ret |= (m_Spi.write(0xFF) << 16); - ret |= (m_Spi.write(0xFF) << 8); - ret |= m_Spi.write(0xFF); - - //Deselect the card - deselect(); - - //Return the response value - return ret; + //Return the R1 response token + return token; } bool SDFileSystem::readData(char* buffer, int length) @@ -497,7 +513,7 @@ char token; unsigned short crc; - //Wait for up to 200ms for the data token to arrive + //Wait for up to 200ms for the start block token to arrive for (int i = 0; i < 200; i++) { token = m_Spi.write(0xFF); if (token != 0xFF) @@ -506,10 +522,8 @@ } //Make sure the token is valid - if (token != 0xFE) { - deselect(); + if (token != 0xFE) return false; - } //Check if large frames are enabled or not if (m_LargeFrames) { @@ -539,36 +553,29 @@ crc |= m_Spi.write(0xFF); } - //Deselect the card - deselect(); - //Return the validity of the CRC16 checksum (if enabled) return (!m_Crc || crc == CRC16(buffer, length)); } -bool SDFileSystem::writeData(const char* buffer) +char SDFileSystem::writeData(const char* buffer, char token) { - //Wait for up to 500ms for the card to become ready - if (!waitReady(500)) { - //We timed out, deselect and indicate failure - deselect(); - return false; - } - - //Send the data token - m_Spi.write(0xFE); - //Calculate the CRC16 checksum for the data block (if enabled) unsigned short crc = (m_Crc) ? CRC16(buffer, 512) : 0xFFFF; + //Wait for the card to become ready + while (!m_Spi.write(0xFF)); + + //Send the start block token + m_Spi.write(token); + //Check if large frames are enabled or not if (m_LargeFrames) { //Switch to 16-bit frames for better performance m_Spi.format(16, 0); //Write the data block from the buffer - for (int b = 0; b < 512; b += 2) - m_Spi.write((buffer[b] << 8) | buffer[b + 1]); + for (int i = 0; i < 512; i += 2) + m_Spi.write((buffer[i] << 8) | buffer[i + 1]); //Send the CRC16 checksum for the data block m_Spi.write(crc); @@ -577,20 +584,247 @@ m_Spi.format(8, 0); } else { //Write the data block from the buffer - for (int b = 0; b < 512; b++) - m_Spi.write(buffer[b]); + for (int i = 0; i < 512; i++) + m_Spi.write(buffer[i]); //Send the CRC16 checksum for the data block m_Spi.write(crc >> 8); m_Spi.write(crc); } - //Receive the data response - char resp = m_Spi.write(0xFF); + //Return the data response token + return (m_Spi.write(0xFF) & 0x1F); +} + +inline bool SDFileSystem::readBlock(char* buffer, unsigned long long lba) +{ + //Try to read the block up to 3 times + for (int f = 0; f < 3; f++) { + //Select the card, and wait for ready + if (!select()) + break; + + //Send CMD17(block) to read a single block + if (command(CMD17, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) { + //Try to read the block, and deselect the card + bool success = readData(buffer, 512); + deselect(); + + //Return if successful + if (success) + return true; + } else { + //The command failed, get out + break; + } + } + + //The single block read failed + deselect(); + return false; +} + +inline bool SDFileSystem::readBlocks(char* buffer, unsigned long long lba, int count) +{ + //Try to read each block up to 3 times + for (int f = 0; f < 3;) { + //Select the card, and wait for ready + if (!select()) + break; - //Deselect the card (this will initiate the internal write process) + //Send CMD18(block) to read multiple blocks + if (command(CMD18, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) { + //Try to read all of the data blocks + do { + //Read the next block and break on errors + if (!readData(buffer, 512)) { + f++; + break; + } + + //Update the variables + lba++; + buffer += 512; + f = 0; + } while (--count); + + //Send CMD12(0x00000000) to stop the transmission + if (command(CMD12, 0x00000000) != 0x00) { + //The command failed, get out + break; + } + + //Only wait for CMD12 if the read was unsuccessful + if (count) + while (!m_Spi.write(0xFF)); + + //Deselect the card + deselect(); + + //Return if successful + if (count == 0) + return true; + } else { + //The command failed, get out + break; + } + } + + //The multiple block read failed deselect(); + return false; +} - //Return success/failure - return ((resp & 0x1F) == 0x05); +inline bool SDFileSystem::writeBlock(const char* buffer, unsigned long long lba) +{ + //Try to write the block up to 3 times + for (int f = 0; f < 3; f++) { + //Select the card, and wait for ready + if (!select()) + break; + + //Send CMD24(block) to write a single block + if (command(CMD24, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) { + //Try to write the block, and deselect the card + char token = writeData(buffer, 0xFE); + deselect(); + + //Check the data response token + if (token == 0x0A) { + //A CRC error occured, try again + continue; + } else if (token == 0x0C) { + //A write error occured, get out + break; + } + + //Send CMD13(0x00000000) to verify that the programming was successful + unsigned int resp; + if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) { + //Some manner of unrecoverable write error occured during programming, get out + break; + } + + //The data was written successfully + return true; + } else { + //The command failed, get out + break; + } + } + + //The single block write failed + deselect(); + return false; } + +inline bool SDFileSystem::writeBlocks(const char* buffer, unsigned long long lba, int count) +{ + char token; + const char* currentBuffer = buffer; + unsigned long long currentLba = lba; + int currentCount = count; + + //Try to write each block up to 3 times + for (int f = 0; f < 3;) { + //If this is an SD card, send ACMD23(count) to set the number of blocks to pre-erase + if (m_CardType != CARD_MMC) { + if (commandTransaction(ACMD23, currentCount) != 0x00) { + //The command failed, get out + break; + } + } + + //Select the card, and wait for ready + if (!select()) + break; + + //Send CMD25(block) to write multiple blocks + if (command(CMD25, (m_CardType == CARD_SDHC) ? currentLba : currentLba * 512) == 0x00) { + //Try to write all of the data blocks + do { + //Write the next block and break on errors + token = writeData(currentBuffer, 0xFC); + if (token != 0x05) { + f++; + break; + } + + //Update the variables + currentBuffer += 512; + f = 0; + } while (--currentCount); + + //Wait for the card to finish processing the last block + while (!m_Spi.write(0xFF)); + + //Finalize the transmission + if (currentCount == 0) { + //Send the stop tran token + m_Spi.write(0xFD); + + //Wait for the programming to complete, and deselect the card + while (!m_Spi.write(0xFF)); + deselect(); + + //Send CMD13(0x00000000) to verify that the programming was successful + unsigned int resp; + if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) { + //Some manner of unrecoverable write error occured during programming, get out + break; + } + + //The data was written successfully + return true; + } else { + //Send CMD12(0x00000000) to abort the transmission + if (command(CMD12, 0x00000000) != 0x00) { + //The command failed, get out + break; + } + + //Wait for CMD12 to complete, and deselect the card + while (!m_Spi.write(0xFF)); + deselect(); + + //Check the error token + if (token == 0x0A) { + //Determine the number of well written blocks if possible + unsigned int writtenBlocks = 0; + if (m_CardType != CARD_MMC) { + //Send ACMD22(0x00000000) to get the number of well written blocks + if (commandTransaction(ACMD22, 0x00000000) == 0x00) { + //Read the data + char acmdData[4]; + if (readData(acmdData, 4)) { + //Extract the number of well written blocks + writtenBlocks = acmdData[0] << 24; + writtenBlocks |= acmdData[1] << 16; + writtenBlocks |= acmdData[2] << 8; + writtenBlocks |= acmdData[3]; + } + } + } + + //Roll back the variables based on the number of well written blocks + currentBuffer = buffer + (writtenBlocks * 512); + currentLba = lba + writtenBlocks; + currentCount = count - writtenBlocks; + + //Try again + continue; + } else { + //A write error occured, get out + break; + } + } + } else { + //The command failed, get out + break; + } + } + + //The multiple block write failed + deselect(); + return false; +}