SD Card Interface class. Log raw data bytes to memory addresses of your choice, or format the card and use the FAT file system to write files.
Diff: SDCard.cpp
- Revision:
- 3:210eb67b260c
- Parent:
- 1:94c648931f84
- Child:
- 4:9a5878d316d5
--- a/SDCard.cpp Mon Aug 23 01:31:50 2010 +0000 +++ b/SDCard.cpp Mon Aug 23 07:12:13 2010 +0000 @@ -1,7 +1,12 @@ +//mbed Microcontroller Library +//SDCard Interface +//Copyright 2010 +//Thomas Hamilton + #include "SDCard.h" SDCard::SDCard(PinName mosi, PinName miso, PinName sck, PinName cs, const char* DiskName) : - FATFileSystem(DiskName), DataLines(mosi, miso, sck), ChipSelect(cs), CRCMode(1), Timeout(8192) + FATFileSystem(DiskName), DataLines(mosi, miso, sck), ChipSelect(cs), CRCMode(1), Timeout(1024) { DataLines.frequency(100000); //set universal speed @@ -14,18 +19,38 @@ //generate the command crc lookup table //(generator polynomial x^16 + x^12 + x^5 + 1 converts to decimal 69665) Initialize(); + //run card setup operations +} + +SDCard::~SDCard() +{ + delete[] CommandCRCTable; + delete[] DataCRCTable; + delete[] OCR; + delete[] CSD; + delete[] FSR; + delete[] Workspace; + //delete all card data register copies and workspaces + delete this; } unsigned char SDCard::disk_initialize() { return 0x00; } + //disc is initialized during construction unsigned char SDCard::disk_status() { return 0x00; } -unsigned char SDCard::disk_read(unsigned char* buff, unsigned long sector, unsigned char count) + //card is always initialized +unsigned char SDCard::disk_read( + unsigned char* buff, unsigned long sector, unsigned char count) { return Read((unsigned int)sector, count, buff); } -unsigned char SDCard::disk_write(const unsigned char* buff, unsigned long sector, unsigned char count) + //read operations must efficiently complete multiple sector transactions +unsigned char SDCard::disk_write( + const unsigned char* buff, unsigned long sector, unsigned char count) { return Write((unsigned int)sector, count, (unsigned char*)buff); } + //write operations must efficiently complete multiple sector transactions unsigned char SDCard::disk_sync() { return 0x00; } + //all disc functions are synchronous unsigned long SDCard::disk_sector_count() { switch (CSD[0] & 0xC0) @@ -33,37 +58,47 @@ case 0x00: return ((((CSD[6] & 0x03) << 10) | (CSD[7] << 2) | ((CSD[8] & 0xC0) >> 6)) + 1) * (1 << ((((CSD[9] & 0x03) << 1) | ((CSD[10] & 0x80) >> 7)) + 2)); + //calculate sector count as specified for version 1 cards case 0x40: return ((((CSD[7] & 0x3F) << 16) | (CSD[8] << 8) | CSD[9]) + 1) * 1024; + //calculate sector count as specified for version 2 cards default: return 0; } } + //return number of sectors on card unsigned short SDCard::disk_sector_size() { return 512; } + //fix SD card sector size to 512 for all cards unsigned long SDCard::disk_block_size() { switch (CSD[0] & 0xC0) { case 0x00: return (CSD[10] << 1) | (CSD[11] >> 7) + 1; + //calculate erase sector size for version 1 cards case 0x40: return 1; + //erase sector size is given by allocation unit for version 2 cards default: return 0; } } + //return the number of sectors in an erase sector unsigned char SDCard::Log(unsigned char Control, unsigned char Data) { static unsigned char Mode = 0x00; + //store previous operating mode to determine current behavior static unsigned short Index = 0; + //store last written byte number of current memory block if (CRCMode) { SelectCRCMode(0); } - + //CRC's are not used in raw data mode + switch (Control) { case 0x00: @@ -74,8 +109,10 @@ { DataLines.write(0xFF); } + //get through left over space, filling with 0xFF for write blocks DataLines.write(0xFF); DataLines.write(0xFF); + //get through CRC ChipSelect.write(1); if (Mode == 0x01) { @@ -85,26 +122,36 @@ { t++; } while (((DataLines.write(0xFF) & 0x11) != 0x01) && (t < Timeout)); + //get through data response token while (!DataLines.write(0xFF)); + //get through busy signal DataLines.write(0xFD); DataLines.write(0xFF); + //send stop transmission token while (!DataLines.write(0xFF)); + //get through busy signal ChipSelect.write(1); DataLines.write(0xFF); } + //finish write block else { Command(12, 0, Workspace); + //send stop transmission command ChipSelect.write(0); while (!DataLines.write(0xFF)); + //get through busy signal ChipSelect.write(1); DataLines.write(0xFF); } + //finish read block Index = 0; Mode = 0x00; + //reset index to start and mode to synced } return 0xFF; - + //control code 0 synchronizes the card + case 0x01: if (Mode != 0x01) { @@ -112,6 +159,8 @@ Command(25, 0, Workspace); Mode = 0x01; } + //if previous call was not a write operation, sync the card, start a new write + //block, and set function to write mode if (Index == 0) { ChipSelect.write(0); @@ -120,6 +169,7 @@ ChipSelect.write(1); Index++; } + //if the index is at the start, send the start block token before the byte else if (Index < 511) { ChipSelect.write(0); @@ -127,6 +177,7 @@ ChipSelect.write(1); Index++; } + //if the index is between the boundaries, simply write the byte else { ChipSelect.write(0); @@ -142,8 +193,11 @@ ChipSelect.write(1); Index = 0; } + //if the index is at the last address, get through CRC, Data response token, and + //busy signal and reset the index return 0xFF; - + //return stuff bits; control code 1 writes a byte + case 0x02: if (Mode != 0x02) { @@ -151,6 +205,8 @@ Command(18, 0, Workspace); Mode = 0x02; } + //if previous call was not a read operation, sync the card, start a new read block, + //and set function to read mode if (Index == 0) { ChipSelect.write(0); @@ -164,6 +220,7 @@ Index++; return Workspace[0]; } + //if the index is at the start, get the start block token and read the first byte else if (Index < 511) { ChipSelect.write(0); @@ -172,6 +229,7 @@ Index++; return Workspace[0]; } + //if the index is between the boundaries, simply read the byte else { ChipSelect.write(0); @@ -182,9 +240,12 @@ Index = 0; return Workspace[0]; } - + //if the index is at the last address, get through CRC and reset the index; + //control code 2 reads a byte + default: return 0xFF; + //return stuff bits } } @@ -193,41 +254,49 @@ if (!Capacity) { Command(24, Address * 512, Workspace); - if (Workspace[0]) - { return 0x04; } } else { Command(24, Address, Workspace); - if (Workspace[0]) - { return 0x04; } } + //send single block write command; addressing depends on the card version + if (Workspace[0]) + { return 0x04; } + //if a command error occurs, return parameter error DataCRC(512, Data, Workspace); + //calculate the data CRC ChipSelect.write(0); DataLines.write(0xFE); + //write start block token for (unsigned short i = 0; i < 512; i++) { DataLines.write(Data[i]); } + //write the data to the addressed card sector DataLines.write(Workspace[0]); DataLines.write(Workspace[1]); + //write the data CRC to the card t = 0; do { Workspace[0] = DataLines.write(0xFF); t++; } while (((Workspace[0] & 0x11) != 0x01) && (t < Timeout)); + //gather the data block response token while (!DataLines.write(0xFF)); + //get through the busy signal ChipSelect.write(1); DataLines.write(0xFF); if (((Workspace[0] & 0x1F) != 0x05) || (t == Timeout)) { return 0x01; } else { return 0x00; } + //if data response token indicates error, return R/W error } unsigned char SDCard::Write(unsigned int Address, unsigned char SectorCount, unsigned char* Data) { static unsigned char CurrentSectorCount = 1; + //store the last write sector count if (SectorCount != CurrentSectorCount) { Command(55, 0, Workspace); @@ -236,28 +305,30 @@ { return 0x04; } CurrentSectorCount = SectorCount; } + //set the expected number of write blocks if different from previous operations if (!Capacity) { Command(25, Address * 512, Workspace); - if (Workspace[0]) - { return 0x04; } } else { Command(25, Address, Workspace); - if (Workspace[0]) - { return 0x04; } } + if (Workspace[0]) + { return 0x04; } Workspace[4] = 0x00; for (unsigned char i = 0; i < SectorCount; i++) { DataCRC(512, &Data[i * 512], Workspace); + //calculate data crc for each passed write block ChipSelect.write(0); DataLines.write(0xFC); + //send multiple write block start token for (unsigned int j = i * 512; j < (i + 1) * 512; j++) { DataLines.write(Data[j]); } + //write each data block DataLines.write(Workspace[0]); DataLines.write(Workspace[1]); t = 0; @@ -269,6 +340,7 @@ while (!DataLines.write(0xFF)); ChipSelect.write(1); Workspace[4] |= Workspace[0]; + //record if any write errors are detected in the data response tokens if (t == Timeout) { ChipSelect.write(0); @@ -278,6 +350,7 @@ DataLines.write(0xFF); return 0x01; } + //if a block write operation gets timed out, make sure the card is synced and exit } ChipSelect.write(0); DataLines.write(0xFD); @@ -296,15 +369,15 @@ if (!Capacity) { Command(17, Address * 512, Workspace); - if (Workspace[0]) - { return 0x04; } } else { Command(17, Address, Workspace); - if (Workspace[0]) - { return 0x04; } } + //send single block read command; addressing depends on the card version + if (Workspace[0]) + { return 0x04; } + //if a command error occurs, return parameter error ChipSelect.write(0); t = 0; do @@ -312,34 +385,37 @@ t++; } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); if (t == Timeout) { ChipSelect.write(1); DataLines.write(0xFF); return 0x01; } + //get to start block token for (unsigned short i = 0; i < 512; i++) { Data[i] = DataLines.write(0xFF); } + //read the data from the addressed card sector Workspace[2] = DataLines.write(0xFF); Workspace[3] = DataLines.write(0xFF); + //read the data CRC to the card ChipSelect.write(1); DataLines.write(0xFF); DataCRC(512, Data, Workspace); + //calculate the data CRC if (CRCMode && ((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3]))) { return 0x01; } else { return 0x00; } + //if CRC is invalid, return R/W error } unsigned char SDCard::Read(unsigned int Address, unsigned char SectorCount, unsigned char* Data) { if (!Capacity) { Command(18, Address * 512, Workspace); - if (Workspace[0]) - { return 0; } } else { Command(18, Address, Workspace); - if (Workspace[0]) - { return 0; } } + if (Workspace[0]) + { return 0; } Workspace[4] = 0x00; for (unsigned char i = 0; i < SectorCount; i++) { @@ -349,6 +425,7 @@ { t++; } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); + //get to each data block start token if (t == Timeout) { ChipSelect.write(1); @@ -359,15 +436,19 @@ DataLines.write(0xFF); return 0x01; } + //if a block read operation gets timed out, make sure the card is synced and exit for (unsigned int j = i * 512; j < (i + 1) * 512; j++) { Data[j] = DataLines.write(0xFF); } + //read each data block Workspace[2] = DataLines.write(0xFF); Workspace[3] = DataLines.write(0xFF); ChipSelect.write(1); DataCRC(512, &Data[i * 512], Workspace); + //calculate data crc for each read data block Workspace[4] |= ((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3])); + //record if any invalid CRCs are detected during the transaction } Command(12, 0, Workspace); ChipSelect.write(0); @@ -385,6 +466,7 @@ do { Command(59, Mode, Workspace); + //command 59 sets card CRC mode t++; } while (Workspace[0] && (t < Timeout)); CRCMode = Mode; @@ -392,8 +474,15 @@ { return 0x01; } else { return 0x00; } + //if command times out, return error } +void SDCard::SetTimeout(unsigned int Retries) +{ + Timeout = Retries; +} + //set c=number of retries for card operations + unsigned char SDCard::Initialize() { for (unsigned char i = 0; i < 16; i++)