Electronic – Problems reading data from a microSD card

microcontrollerpicsdspi

I am trying to read a block of data from a standard capacity micro SD card, but the received data does not make any sense. Here is what I'm doing:

  • Initializing the card
  • Reading data
    • Setting CS low
    • Sending CMD16 to set block length
    • Sending CMD17 with start address
    • Waiting for start byte (0xfe)
    • Receiving bytes and printing them to the serial terminal
    • Receiving CRC
    • Setting CS high
    • Sending 8 extra clocks (?)

The data I'm trying to read is the FAT16 boot sector, as shown below:
Card contents
And the data I'm getting out is this:
enter image description here

At this point, I can't even begin to think of what is going wrong. I don't think it's a transmission error, because the data out is always the same. I can also read the boot sector signature 0x55 0xAA every time, even if I set the start address to 510 and read two bytes, they come back perfectly. The problem is that I can't read other bytes.

I also tried swapping cards and that didn't help either.

What am I missing?

EDIT: the function in question is below.

bit sd_read_block(unsigned long size, unsigned long start_addr)
{
    unsigned long i;
    unsigned char r;

    SD_CS = 0;

    r = sd_card_cmd(16, size, 0);

    sprintf(usb_uart_tx_buffer, "CMD16 R Token: %02x.\r\n", r);
    usb_uart_tx(usb_uart_tx_buffer);

    r = sd_card_cmd(17, start_addr, 0);

    sprintf(usb_uart_tx_buffer, "CMD17 R Token: %02x.\r\n", r);
    usb_uart_tx(usb_uart_tx_buffer);

    // wait for start token
    r = 0;
    while (spi_rx_byte() != 0xfe) {
        if (r++ > 25) {
            SD_CS = 1;
            return r;
        }
    }

    for (i = 0; i < size; i++) {
        if (i % 20 == 0) {
            UART1_TX(0x0d);
            UART1_TX(0x0a);
        }
        sprintf(usb_uart_tx_buffer, "%02x ", spi_rx_byte());
        usb_uart_tx(usb_uart_tx_buffer);
    }

    // CRC
    spi_rx_byte();
    spi_rx_byte();

    // extra clocks
    r = spi_rx_byte();

    SD_CS = 1;

    return 0;
}

Best Answer

It turns out that I was reading the MBR, which is located at the address 0 on the SD card. To find the location of the boot sector, one needs to read the appropriate entry in the MBR. The entries start at the address 0x01be and are 16 bytes each. The point of interest in the entry lies at the offset 0x08, is 4 bytes long and is called an LBA. [Wikipedia] To get the address of the boot sector location, one would multiply the LBA by the size of a sector (512 bytes). [Microchip forum]

For example:

In the output above, the four entries look like this:

00 02 10 00 06 2f ef af 8d 00 00 00 73 0f 3a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

As you can see, only one entry is used. The LBA in this case is 8d 00 00 00 and is in little-endian format, so in big-endian it would be 0x8d or 141 in decimal. Multiply this by 512 and you get 72192 (0x11a00). This is the address of the sector where the FAT starts.