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.
SDCard.cpp
- Committer:
- Blaze513
- Date:
- 2010-07-18
- Revision:
- 0:f3870f76a890
- Child:
- 1:94c648931f84
File content as of revision 0:f3870f76a890:
#include "SDCard.h" SDCard::SDCard(PinName mosi, PinName miso, PinName sck, PinName cs) : DataLines(mosi, miso, sck), ChipSelect(cs), CRCMode(1) { DataLines.frequency(100000); //set universal speed ChipSelect.write(1); //chip select is active low GenerateCRCTable(1, 137, CommandCRCTable); //generate the command crc lookup table //(generator polynomial x^7 + x^3 + 1 converts to decimal 137) GenerateCRCTable(2, 69665, DataCRCTable); //generate the command crc lookup table //(generator polynomial x^16 + x^12 + x^5 + 1 converts to decimal 69665) Initialize(); //send card initialization sequence } bool SDCard::Write(unsigned int Address, unsigned char* Data) { if (Capacity) { for (unsigned int j = 0; j < 8192; j++) { Command(24, Address, Workspace); if (Workspace[0] == 0x00) { break; } if (j == 8191) { return 0; } } } else { for (unsigned int j = 0; j < 8192; j++) { Command(24, Address * 512, Workspace);///////////implement block length if (Workspace[0] == 0x00) { break; } if (j == 8191) { return 0; } } } ChipSelect.write(0); DataLines.write(0xFE); //start data block token for (unsigned short j = 0; j < 512; j++) { DataLines.write(Data[j]); //write the data } DataCRC(512, Data, Workspace); DataLines.write(Workspace[1]); DataLines.write(Workspace[2]); for (unsigned int j = 0; j < 8192; j++) { Workspace[0] = DataLines.write(0xFF); if ((Workspace[0] & 0x1F) == 0x05) { break; } } while (!DataLines.write(0xFF)); ChipSelect.write(1); DataLines.write(0xFF); if ((Workspace[0] & 0x1F) == 0x05) { return 1; } else { for (unsigned int j = 0; j < 8192; j++) { Command(13, 0, Workspace); if (Workspace[0] == 0x00) { break; } } return 0; } } bool SDCard::Read(unsigned int Address, unsigned char* Data) { if (Capacity) { for (unsigned int j = 0; j < 8192; j++) { Command(17, Address, Workspace); if (Workspace[0] == 0x00) { break; } if (j == 8191) { return 0; } } } else { for (unsigned int j = 0; j < 8192; j++) { Command(17, Address * 512, Workspace);///////////implement block length if (Workspace[0] == 0x00) { break; } if (j == 8191) { return 0; } } } ChipSelect.write(0); for (unsigned int j = 0; j < 8192; j++) { if (DataLines.write(0xFF) == 0xFE) { break; } } for (unsigned short j = 0; j < 512; j++) { Data[j] = DataLines.write(0xFF); } Workspace[3] = DataLines.write(0xFF); Workspace[4] = DataLines.write(0xFF); ChipSelect.write(1); DataLines.write(0xFF); DataCRC(512, Data, Workspace); if ((Workspace[1] == Workspace[3]) && (Workspace[2] == Workspace[4])) { return 1; } else { return 0; } } bool SDCard::Initialize() { for (unsigned char j = 0; j < 16; j++) { DataLines.write(0xFF); } //perform specified power up sequence for (unsigned int j = 0; j < 8192; j++) { Command(0, 0, Workspace); //send command 0 to put the card into SPI mode if (Workspace[0] == 0x01) //check for idle mode { break; } if (j == 8191) { return 0; } } for (unsigned int j = 0; j < 8192; j++) { Command(59, 1, Workspace); //send command 59 to turn on CRCs if (Workspace[0] == 0x01) { break; } if (j == 8191) { return 0; } } for (unsigned int j = 0; j < 8192; j++) { Command(8, 426, Workspace); //voltage bits are 0x01 for 2.7V - 3.6V, //check pattern 0xAA, [00,00,01,AA] = 426 if ((Workspace[0] == 0x05) || ((Workspace[0] == 0x01) && ((Workspace[3] & 0x0F) == 0x01) && (Workspace[4] == 0xAA))) //check version, voltage acceptance, and check pattern { break; } if (j == 8191) { return 0; } } Version = Workspace[0] == 0x01; //store card version for (unsigned int j = 0; j < 8192; j++) { Command(58, 0, Workspace); //check the OCR if ((Workspace[0] == 0x01) && ((Workspace[2] & 0x20) || (Workspace[2] & 0x10))) //check for correct operating voltage 3.3V { break; } if (j == 8191) { return 0; } } for (unsigned int j = 0; j < 8192; j++) { Command(55, 0, Workspace); //specify application-specific command Command(41, 1073741824, Workspace); //specify host supports high capacity cards //[40,00,00,00] = 1073741824 if (Workspace[0] == 0x00) //check if card is ready { break; } if (j == 8191) { return 0; } } for (unsigned int j = 0; j < 8192; j++) { Command(58, 0, Workspace); //check the OCR again if ((Workspace[0] == 0x00) && (Workspace[1] & 0x80)) { break; } if (j == 8191) { return 0; } } for (unsigned char j = 0; j < 4; j++) { OCR[j] = Workspace[j + 1]; } //record OCR Capacity = (OCR[0] & 0x40) == 0x40; //record capacity for (unsigned int j = 0; j < 8192; j++) { Command(9, 0, Workspace); //read the card-specific data register ChipSelect.write(0); for (unsigned int k = 0; k < 8192; k++) { if (DataLines.write(0xFF) == 0xFE) { break; } } //get to the start-data-block token for (unsigned char k = 0; k < 16; k++) { CSD[k] = DataLines.write(0xFF); } Workspace[3] = DataLines.write(0xFF); Workspace[4] = DataLines.write(0xFF); ChipSelect.write(1); DataLines.write(0xFF); DataCRC(16, CSD, Workspace); //calculate the CSD data CRC Workspace[0] = 0; for (unsigned char k = 0; k < 15; k++) { Workspace[0] = CommandCRCTable[Workspace[0]] ^ CSD[k]; } Workspace[0] = CommandCRCTable[Workspace[0]] | 0x01; if ((Workspace[0] == CSD[15]) && (Workspace[1] == Workspace[3]) && (Workspace[2] == Workspace[4])) { break; } if (j == 8191) { return 0; } } if (((CSD[3] & 0x07) > 2) || ((CSD[3] & 0x7F) > 0x32)) { DataLines.frequency(25000000); //maximum speed is given at 25MHz } else { Workspace[0] = 1; for (unsigned char j = 0; j < (CSD[3] & 0x07); j++) { Workspace[0] *= 10; //the first three bits are a power of ten multiplier for speed } switch (CSD[3] & 0x78) { case 0x08: DataLines.frequency(Workspace[0] * 100000); break; case 0x10: DataLines.frequency(Workspace[0] * 120000); break; case 0x18: DataLines.frequency(Workspace[0] * 140000); break; case 0x20: DataLines.frequency(Workspace[0] * 150000); break; case 0x28: DataLines.frequency(Workspace[0] * 200000); break; case 0x30: DataLines.frequency(Workspace[0] * 250000); break; case 0x38: DataLines.frequency(Workspace[0] * 300000); break; case 0x40: DataLines.frequency(Workspace[0] * 350000); break; case 0x48: DataLines.frequency(Workspace[0] * 400000); break; case 0x50: DataLines.frequency(Workspace[0] * 450000); break; case 0x58: DataLines.frequency(Workspace[0] * 500000); break; case 0x60: DataLines.frequency(Workspace[0] * 550000); break; case 0x68: DataLines.frequency(Workspace[0] * 600000); break; case 0x70: DataLines.frequency(Workspace[0] * 700000); break; case 0x78: DataLines.frequency(Workspace[0] * 800000); break; default: break; //read the CSD card speed bits and speed up card operations } } if (!Version) { for (unsigned int j = 0; j < 8192; j++) { Command(16, 512, Workspace); //set data-block length to 512 bytes if (Workspace[0] == 0x00) { break; } if (j == 8191) { return 0; } } } ////////////////////////////////////////////implement data block sizing later return 1; } void SDCard::Command(unsigned char Index, unsigned int Argument, unsigned char* Response) { ChipSelect.write(0); //assert chip select low to synchronize command DataLines.write(0x40 | Index); //the index is assumed valid, commands start with "01b" DataLines.write(((char*)&Argument)[3]); DataLines.write(((char*)&Argument)[2]); DataLines.write(((char*)&Argument)[1]); DataLines.write(((char*)&Argument)[0]); //send the argument bytes in order from MSB to LSB (mbed is little endian) if (CRCMode) { DataLines.write(CommandCRC(&Index, &Argument)); } else { DataLines.write(0x00); } //send the command CRC for (unsigned int j = 0; j < 8192; j++) { Response[0] = DataLines.write(0xFF); //clock the card high to let it run operations, the first byte will be //busy (all high), the response will be sent some time later if (!(Response[0] & 0x80)) //check for a response by testing if the first bit is low { break; } } if ((Index == 8) || (Index == 13) || (Index == 58)) { for (unsigned char j = 1; j < 5; j++) { Response[j] = DataLines.write(0xFF); } //get the rest of the response } ChipSelect.write(1); //assert chip select high to synchronize command DataLines.write(0xFF); //clock the deselected card high to complete processing for some cards } char SDCard::CommandCRC(unsigned char* IndexPtr, unsigned int* ArgumentPtr) { return CommandCRCTable[ CommandCRCTable[ CommandCRCTable[ CommandCRCTable[ CommandCRCTable[ *IndexPtr | 0x40 ] ^ ((char*)ArgumentPtr)[3] ] ^ ((char*)ArgumentPtr)[2] ] ^ ((char*)ArgumentPtr)[1] ] ^ ((char*)ArgumentPtr)[0] ] | 0x01; //using a CRC table, the CRC result of a byte is equal to the byte in the table at the //address equal to the input byte, a message CRC is obtained by successively XORing these //with the message bytes } void SDCard::DataCRC(unsigned short Length, unsigned char* Data, unsigned char* Result) { Result[1] = 0x00; Result[2] = 0x00; //initialize result carrier for (int i = 0; i < Length; i++) //step through each byte of the data to be checked { Result[0] = Result[1]; //record current crc lookup for both bytes Result[1] = DataCRCTable[2 * Result[0]] ^ Result[2]; //new fist byte result is XORed with old second byte result Result[2] = DataCRCTable[(2 * Result[0]) + 1] ^ Data[i]; //new second byte result is XORed with new data byte } for (int i = 0; i < 2; i++) //the final result must be XORed with two 0x00 bytes. { Result[0] = Result[1]; Result[1] = DataCRCTable[2 * Result[0]] ^ Result[2]; Result[2] = DataCRCTable[(2 * Result[0]) + 1]; } } void SDCard::GenerateCRCTable(unsigned char Size, unsigned long long Generator, unsigned char* Table) { unsigned char Index[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; //this will hold information from the generator; the position indicates the //order of the encountered 1, the value indicates its position in the //generator, the 9th entry indicates the number of 1's encountered for (int i = 0; i < 64; i++) { if (((char*)&Generator)[7] & 0x80) { break; } Generator = Generator << 1; //shift generator so that the first bit is high } for (unsigned char k = 0; k < Size; k++) { Table[k] = 0x00; //initialize the first CRC bytes } for (unsigned char i = 0; i < 8; i++) //increment through each generator bit { if ((0x80 >> i) & ((unsigned char*)&Generator)[7]) //if a 1 is encountered in the generator { Index[Index[8]] = i; Index[8]++; //record its order and location and increment the counter } for (unsigned char j = 0; j < (0x01 << i); j++) //each bit increases the number of xor operations by a power of 2 { for (unsigned char k = 0; k < Size; k++) //we need to perform operations for each byte in the CRC result { Table[(Size * ((0x01 << i) + j)) + k] = Table[(Size * j) + k]; //each new power is equal to all previous entries with an added xor //on the leftmost bit and each succeeding 1 on the generator for (unsigned char l = 0; l < Index[8]; l++) //increment through the encountered generator 1s { Table[(Size * ((0x01 << i) + j)) + k] ^= (((unsigned char*)&Generator)[7-k] << (i + 1 - Index[l])); Table[(Size * ((0x01 << i) + j)) + k] ^= (((unsigned char*)&Generator)[6-k] >> (7 - i + Index[l])); //xor the new bit and the new generator 1s } } } } }