Calling All SPI SD Card Wizards: Troubleshooting SD Card Access with SPI
Updated: Feb 11, 2022
Full data reading is now working! Progress Updates posted at the bottom...
I am currently trying to get an SD Card reader working with my 6502 system. I have plenty of research and experimenting to do yet, but I am posting this in case anyone catches something obvious that I'm missing. Short version: I am getting results of 05 when making many of the SPI-based calls to the SD Card reader.
To the right are the readers and SD Cards that I am using. The readers are HiLetgo MicroSD Card readers. I have a variety of Micro SD Cards. I am connecting the SD Card reader to my SPI card (see image further down in post).
For 6502 assembly code, I started with a posting from George Foot, titled Reading SD Cards on a 6502+6522 Computer. Using his code as a starting point, I created this 6502 assembly code (as of 04 January 2022). This code is adapted to my configuration where I am using an SPI card with external shift registers. At a high level, the code performs the following actions:
Starts the card initialization process by cycling the SPI clock for 80+ full clock cycles with MOSI high.
With CS set for the SD Card reader, sends CMD0. Expecting result of 01. I typically get expected results from this (for card #1 or card #2).
Sends CMD8. Expecting result of 01, unless the card is really old. I receive 05.
Send CMD55 (expecting back 01), followed by CMD41 (expecting back 00). Again, I get 05 as a result for many of these calls.
Regardless of errors, I attempt to read a sector (of course, with no luck).
Examples of sequencing of commands and specific values being sent and received are below. This is debug output captured on my 6502 VGA.
As seen below, I am connecting the SD Card reader to my SPI card. The SPI card has a series of OEB/CS connections for different SPI devices. Connections for the SPI clock, MOSI, and MISO are also provided from the card pin headers. The SD Card is connected to 5V power (the reader has a built-in 3.3V level shifter, as I understand it).
The SD Cards are formatted with either FAT32 or FAT, based on their capacity. Each card contains two files with six characters in each file.
As far as I can tell, the SPI communication looks OK. Below are some quick captures. These screen captures were from a test with SD Card #3 showing the initial clock cycling (for initialization), sending CMD0, and receiving an incorrect response of C0 (not 01). This first command, CMD0, works fine on both of the other SD Cards. I have been validating with the scope that the values I believe I am sending and receiving from 6502 assembly code are actually seen on the wire.
According to this article, if CMD41 fails, CMD1 can be sent to complete the initialization. I receive 05 (not the expected 01) from CMD1, even if sent multiple times consecutively.
I saw in this post that CS/OE should be toggled between commands. I have tried that also.
Google, Google, Google :) --reading other tutorials, references, data sheets to see what I might be missing.
Connect an SD Card reader to an Arduino and use existing Arduino libraries to test. I can then review the code to see how communication is being handled. I can also use a scope and/or logic analyzer to see how the Arduino code might approach the communication differently from what I'm doing.
References I am Using
StackExchange: What is the correct command sequence for microSD card initialization in SPI?
u/tmrob pointed me to a very nice write-up on rjhcoding.com. From that article, I gathered that I should be sending some junk bytes before and after changing the CS/OE. I added those extra junk bytes and have been able to clear CMD0, CMD8, and CMD55 - using my 8GB card. I have not been able to get past CMD41 yet. I'm trying to determine why CMD41 returns 05 and then looping back to CMD55 changes its return to 05 also. My guess is this is related to the paired sequencing of 55+41; I must have some bits being sent in the 55+41 sequence that shouldn't be there (or are missing).
With my 32GB card, I get stuck on CMD0 with a return of C0, indicating some kind of issue, possibly a voltage/shift issue. I have a second, identical card; I get the same results.
With my 1GB card, I get past CMD0 but then get 05 on CMD8. I believe this is correct behavior, as this is an older card (non-HC). I will need to try CMD1 on this card.
With regard to the 55+41 sequence problem, it seems like I'm getting an early response from the SD Card during my CM41 command. It's like it thinks it's received enough command packets to send a response (click the image below for a larger view).
SPI Card schematic:
u/gfoot360 suggested that I keep MOSI high when reading bytes. Voila! I now have the full initialization working!
I have only tested my 8GB card so far. I am curious to see if my 32GB card will now work. I still need to modify the initialization to support my older 1GB card.
Thanks again to u/tmrob and u/gfoot360 for the help!!
The following video shows current state. I have not yet started work on actually reading data from the SD Card, so disregard the last bits that show data being read (I don't know if it's valid yet or not).
I have updated the posted assembly code with the version used in the above video. This code still is really messy, so please don't judge me. :)
In the process of getting this to work, I have identified some room for improvement with my SPI Card, lol. The biggest thing is the default values for CS/OE and other shift register control signals. I should probably invert the signals going into many of these. This would allow me to cleanup my 6502 code so that I don't have to constantly include the flag to keep a signal high and only include the flag when I really need that signal changed. In other words, set the flag 20% of the time, not 80% of the time. The other issue is having easier control of MOSI. Since I have the signal for MOSI coming out of a shift register, I have to deal with the shift register to simply bring MOSI high (say, for 8 SPI clock cycles). Maybe an OR gate with the shift register output and a VIA output would provide a handy way to manually bring up the value of MOSI without messing with the shift register.
With regard to the SPI Card, I might also be creating far more work than it's worth. Just using a VIA for SPI communication might actually be easier. I'm OK with this, since I wanted to learn shift registers. I can tell you that the SPI Card has helped me do that. :)
Helpful Tools for Troubleshooting
Troubleshooting SPI and SD Card communication can be a bit tedious (and frustrating) at times. A few things that helped me maintain my sanity:
The high-resolution VGA output on my 6502 allowed me to print plenty of debug data to the screen. While possible with my 6502 onboard two-line LCD, that would have been quite painful. A quarter of the VGA screen can been seen in the video above.
My oscilloscope is able to interpret SPI. I found this very helpful when getting basic SPI communication started with the SD Card. Once I got into long sequences of SPI communication before seeing a failure, the oscilloscope was not as helpful.
Finally, I used a logic analyzer to debug the longer SPI sequences. My specific analyzer is a Digilent Digital Discovery with the Digilent WaveForms software. This also can be seen in the video above.
While I have the initialization working, I still need to actually read data from the SD Card. :)
I have been able to test the above initialization code with multiple SD Cards, including the 1GB, 8GB, and 32GB ones shown earlier. All three are initializing fine, which is great to see!
Now I need to get code in place to read data. Mark Hindsbo has a nice write-up on working with the FAT file system from 6502 assembly. He appears to leave us with a cliffhanger when getting to actually reading data into memory. He suggests this for his next blog post, but I wasn't able to locate another post. As I read through all the details to utilize the FAT file system, I am thinking it is overkill for what I need (for now). I am currently researching ways to use my SD Card for binary storage, like a ROM, without using a file system -- using raw instead of FAT.
Reading raw data:
I can now effectively read data on the SD Card, with no file system in play. Essentially, I can treat the SD Card as one big data storage ROM. Now, I can go back to working on game music since I have plenty of storage space. :)