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.

Dependencies:   mbed

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
                }
            }
        }
    }
}