NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » SD card protocol

January 02, 2011
by Hexorg
Hexorg's Avatar

Hey everyone! I'm trying to interface with the SD card via SPI, I found code pbfy0 posted that he used to get a sound the card, but it was a little bit hard to follow, and check sum responses were hard coded. I'm using his base but trying to write a more reusable for future needs code. I'm following the "Part 1 Phisical Layer Simplified Specifications V.2" datasheet that I found somewhere online. Also, the code uses literals like CMD8, which just equals 8.

So, the datasheet tells what all kinds of commands and how to do stuff. It says that first you need to send at least 74 clock pulses in order to make sure SD card has enough juice in it. I'm sending spi data using this function

uint8_t SPI(uint8_t d)
{
    uint8_t received = 0;
    SPDR = d;
    while (!(SPSR & (1<<SPIF)));
    received = SPDR;
    return received;
}

So I just call SPI(0xFF) a few times in a loop. Then I need to set SD card into idle state. I'm sending commads using this function:

void Command(uint8_t command, uint32_t argument)
{
    uint8_t header, footer;
    header = 0x40; // set start bit to 0 and transmission bit to 1
    header |= (command & 0x3F); // 0x3F = 0b00111111, masking first 6 bits of the 
                                // command just in case
    footer = (CRC7(header, argument)<<1) | 0x01; // bits 7..1 is CRC, LSB is 1 - end bit
                                                 // CRC7() function is tested multiple times 
                                                 // with different input. it works
    SPI(header);
    SPI((uint8_t) (argument >> 24));
    SPI((uint8_t) (argument >> 16));
    SPI((uint8_t) (argument >> 8));
    SPI((uint8_t) argument);
    SPI(footer);
    SPI(0xFF); // sending a few clock pulses just in case, 
               // datasheet says there are 8-64 clock pulses between the 
               // acceptance of command and transmission of response if any
}

After the idle state is issued, we neeed to pull chip-select to low, and issue CMD0 again to put SD card into the SPI mode. after that we need to put the card in idle state again, it doesn't matter what chip-select is, and after that issue a CMD8 - SEND_INTERFACE_CONDITIONS command, with arguments to it being bits 11:8 - a voltage level (set to 0b0001 for 2.7-3.6V), and bits 7..0 is a check pattern that will echo in response. According to the datasheet the response will be "R7" which mostly will echo accepted voltage and check pattern. here's how I check it:

uint32_t R7(uint8_t pattern)
{
    uint8_t buf, header, footer, check_pattern;
    uint32_t status;
    while ((buf = SPI(0xFF)) == 0x00); // wait for not 0
    header = buf;
    status = SPI(0xFF)<<16;
    status |= SPI(0xFF)<<8;
    status |= SPI(0xFF);
    check_pattern = SPI(0xFF);
    footer = SPI(0xFF);
    footer >>= 1; // shift footer right to have just CRC7 there
    if (footer != CRC7(header, ((status<<8) | check_pattern)))
        return 0xffffffff;
    if (check_pattern != pattern)
        return 0xfffffffe;
    return status;

}

However, when I try to get the responce, It keeps waiting for anything non 0 from the card, unless I tab the connections, then it returns 0xffffffff which is a CRC miss match.... Here is the whole MMC_init():

int MMC_Init()
{
    uint8_t i;
    uint32_t r7resp;
    sdcard_select(1);
    for (i=0; i<16; i++) SPI(0xFF); // send 10*8=80 clock pulses
    Command(CMD0, 0); // go into idle state

    sdcard_select(0);   
    Command(CMD0, 0); // set into SPI mode
    sdcard_select(1);

    Command(CMD0, 0); //required
    Command(CMD8, 0x1AA); // 1 is for voltage range, AA is a check pattern
    lcd_line_three();
    lcd_write_string(PSTR("Trying to get R7"));
    r7resp = R7(0xAA);
    if(r7resp == 1) // 0xAA is a check pattern
    {
        lcd_line_four();
        lcd_write_string(PSTR("Voltage accepted"));
    }
    else {
    lcd_line_four();
    lcd_write_int16(r7resp);
    }
    return 0;
}

Could it be my connections? Card is working on computer.

January 02, 2011
by Hexorg
Hexorg's Avatar

oh, i forgot to mention that i'm using a voltage divider to get 3.2V VCC for that card. I'm using it this way:

+5V --- 120 Ohm --+-- 220 Ohm --- GND
                  |
                  + to SD's VCC

5*(220/(120+220)) = 3.23V, with maximum current of 5/120 = 41.6mA. In the datasheet I found that SD card uses around 15mA most of the time, so I doubt my power supply is any problem. I also use 330 ohm resistors at any input to the card line, like pbfy0 suggested in his post.

January 02, 2011
by Hexorg
Hexorg's Avatar

Ah, well, I found many errors in what I was doing. I'll post a full code here when I finish, but for now maybe someone will know the answer...

I initialized the card in spi mode correctly, and set the voltages by CMD8, it even echoed the check pattern and all crc7s are correct. Now i'm trying to set the block size to 24 (this is not a high capacity card so it allows for non-512 block sizes), but in R1 response I keep getting 1, which means card is idle. How do i bring it up from an idle state?

Post a Reply

Please log in to post a reply.

Did you know that 20 LEDs can be controlled from 11 microcontroller pins, to make a twinkling heart outline? Learn more...