PIC Projects

Working with SD/MMC cards

Reading from and writing to the card

The goal of this experiment is to develop a series of low-level routines for reading information from an SD/MMC card. These routines will be used in other projects later. I found a pretty good description of a similar project on a website designed my Michael Dworkin, which I took as a prototype. His tutorial (written in German) written for a 64Mb MMC card contains a schematic and sample C code. In my case the hardware is slightly different: I used a 32Mb Kingston SD card (available now-days for $2 in surplus stores), a different microcontroller, and rewrote all the code in assembly language. Also, I fixed some (obvious) typos in the schematic.

Schematic Test

The test on the right photo above shows a dialog with a Hyper Terminal program running under Windows XP. This program is just used to print some messages generated by the PIC. The embedded code initializes the card, writes on it a block of 512 characters (255 characters A followed by 255 characters B, following by single C and D characters). After this the code reads the same block and dumps the output to the Hyper Terminal. The PC is connected to the board via a serial cable. As it follows from the image above, the read characters match the written ones, which is an evidence of a correct implementation of the read/write routines.

The PIC was selected to satisfy the following property: it should have disjoint pins for the crystal, UART TX/RX, and SPI input/outputs. The one I used (PIC16F687) is a smaller one in terms of the number of pins. It has a 2Kb flash memory, which is more than sufficient for our purposes. The MMC socket is 3M EI11122A. It has more pins than needed for our experiment, that are just not used and configured for output to prevent an oscillation. The board is connected to a PIC programmer (the right board on the right photo below) vi an ICSP cable. This way it is very convenient to modify the code, which one certainly does need to do a lot of times. The schematic is assembled on a PCB to achieve a possibly higher MMC clocking frequency (about 5MHz out of maximum 20). The 3.3V LDO I had is for surface mounting and it is assembled on the back side of the PCB. Also, some resistors are mounted on the back after several experiments, but the Eagle layout available for download below has a fix for that.

Board layout Assembled board

The card is connected to the PIC by using the SPI interface, which is well described on the Internet. Check a Guide on using the MMC cards, for example. The PIC has a built-in SPI module that generates all needed waveforms and make working with SPI easy. Similarly, the embedded USART module makes it easy to communicate with PC via a serial interface. I used a 9600 baud speed, which actually does not matter much.

Using a different card causes several changes in software. First, the card should be clocked on a frequency not exceeding 320KHz during the initialization (sending CMD0 and CMD1 commands). After an acceptance of those commands the clocking frequency can be increased. Second, it is still unclear to me how long should one wait for a card response. The longest response is expected after sending the CMD1 command (up to several dozens of milliseconds). However, I found that the READ_SINGLE_BLOCK instruction (CMD17) takes more clocks to respond than other commands. On the test photo above the characters appearing in front of the messages (e.g., "o1" for CMD0) is the card response. It turns out that the 0 response for setting the block length and writing a block comes after two clocks, whereas the one for the read command comes after 3 clocks. For other cards this might be different. So, I just wait for up to 8 clocks after the instruction. If an expected response is received, the corresponding subroutine returns a 0, otherwise a 1 is returned. It looks that in Michael's experiments the response was always obtained after two clocks. If one eliminates sending the card response to the PC, the waiting time should be increased. I ended up with waiting for a response for at most 80 cycles after the command was sent.

Each MMC instruction requires sending of 6 bytes to the card, in theory. The first and the last byte specify the command and the CRC, respectively, whereas the middle 4 bytes specify an address within the card in byte units. However, in practice one more dummy byte should be sent to the card at the beginning of each instruction. I do not know why this is not mentioned in the MMC documents. It is my greatest gratitude to Michael for figuring this out. I took it form his C code. Without sending of the "undocumented" dummy byte the card does not accept the commands.

Anyway, using the card in the SPI mode assumes exchange of information in blocks. The default block size is 512 bytes, which is very convenient since it matches the sector size of the card's file system (more on this in the next experiment). The supplied routines can read from an arbitrary address within the card without a relation to the file system.

Downloads


Last modified:Mon, Jan 23, 2023.

15151