The basic idea behind talking to a SD card via SPI is fairly simple, we first setup the card so that it knows it is talking in SPI and not SDIO. From there we can read/write the data in 512 byte blocks (sectors), and the card will manage all the erase operations as required. For this implimentation I am mostly looking at the ability to read the card, so I can avoid the need to support bulk read/write operations for larger blocks.
I expose a very small set of functions about the card (Read,Write getSize). This keeps the required code small and easy to understand, and makes debugging easier. The downside to this is performance, before each operation the card needs time to setup the internal state machine. Normally in SDIO mode the clock runs continiously so the card’s state machine always has clock cycles to work with for setup, however in SPI we only send the clock when we are sending data, so the card needs some to get started.
When I initially wrote this code I kept coming across issues where the card would return invalid responses (ie the msb was set to 1) or it would ignore my commands completely, or reset to being uninitalized. If you run across this issue there are a few key things to check
This last one is vitally important. Seriously. It is not documented anywhere that I could find that you must send idle state (high) whenever you clock out data. It appears the card tries to interpret the incoming data even though it is currently sending data out. This makes sense as you can send a CMD12 while the unit is clocking out multiple blocks to abort the transfer. This however was not an issue i noticed until after i killed a sd card.
Yes, clocking in random data can kill a card. The card still enumerates,but crashes as soon as you try to read from its memory….
When ST made the HAL libraries, they took a shortcut when they coded the function for recieving data from another device. In SPI when you want to clock out data while you clock in data for a transfer. When ST coded the library, instead of just clocking out 0x00 or 0xFF as is usually done, instead their code clocks out whatever was in the array your reading into before hand. So in my case I was throwing uninitalized data out the SPI port at the SD card and no wonder it would randomly lock up. To combat this I have included a custom SPI Recieve command that does not do this, and instead just passes 0xFF out the port.
The final code is really what you would expect if you looked at any other online tutorial so I will not go into heavy details, and really most of the information you would like can be found in the specifications online.
The basic concept is:
Then you can use the following 3 commands to interact with the card:
These functions link really easily with most FAT implimentations as they are almost always built around reading/writing indivdiual sectors from the disk.
In the next section I will cover setting up the USB link so that this sd card can be passed through over usb to an operating system so you can build the slowest sd card reader ever (I get around 400kb/sec).