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.
Dependencies: 4DGL-uLCD-SE FATFileSystem
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;
+}
