SD Cards and FAT32 - Part 3


Working on my 6502 computer I have gathered a significant amount of wisdom around working with SD Cards and FAT32. I am documenting all this as much for myself as others.

Reading a sector

CMD17 is used to read a single block from the SD Card.

For SDHC or SDXC (High Capacity Cards) the address is by block, so an address of 0 reads bytes 0 to 511 and an address of 1 reads bytes 512 to 1023.

For Standard Capacity Cards the address is by byte, so an address of 0 will also read bytes 0 to 511 but an address of 1 will read bytes 1 to 512.

To read a sector on Standard Capacity Cards you will need to multiply the sector number by 512 to get the byte number address.

Send CMD17 with the address to read and the SD Card will respond with R1.
It can take several clock cycles for the data to be ready and the card will send $FF until the data is ready. Once the card is ready to send the sector data it will send a start token of $FE. Next the card will send 512 bytes of data and finally a CRC byte.

Pseudocode


Write_SPI($FF)        ; Always send an $FF byte before a CS change
SetCS(Low)            ; Sets the CS line low
Write_SPI($FF)        ; Always send an $FF byte after a CS change

Write_SPI($FF)        ; Always send an $FF byte before a command

Write_SPI($51)        ; CMD17 - Note we OR command number with $40
Write_SPI($00)        ; CMD17 - Address (MSB)
Write_SPI($00)        ; CMD17 - Address
Write_SPI($00)        ; CMD17 - Address
Write_SPI($00)        ; CMD17 - Address (LSB)
Write_SPI($FF)        ; CMD17 - No CRC needed.

Result = Read_SPI     ; Read the R1 result byte
While(Result == $FF)  ; Wait till returned byte is not $FF
  Result = Read_SPI   ; Read the R1 result byte
If (Result != $00) then Error   ; Return an error

Token = Read_SPI      ; Read the R1 result byte
While(Token == $FE)   ; Wait till returned byte is not $FE
  Token = Read_SPI    ; Read the R1 result byte

For I=0 to 511 do     ; Read the 512 bytes
  Data[I] = Read_SPI  ; Read a data byte from the SPI bus
Next                  ; End of the for loop

CRC = Read_SPI        ; Read the R1 result byte

Write_SPI($FF)        ; Always send an $FF byte before a CS change
SetCS(High)           ; Sets the CS line high
Write_SPI($FF)        ; Always send an $FF byte after a CS change

What’s next?

In part 4 we will write a sector.