On my old website, the article “MMC/SD Card and FAT Tutorial” was really popular (it still holds almost top ranking on Google). However, the information there is so old that most of it is obsolete. Elm-Chan’s FatFs library has been updated many times since then, rendering some of the old webpage I wrote obsolete. But I will put up some of the old content here so you may still see it.
Before you see the old content…
Please visit these newer resources:
(the old) MMC/SD Card and FAT Tutorial
In this tutorial, I will show you how to read and write to a SD card using an AVR microcontroller. There are some prerequisite knowledge you need to have before you can follow this tutorial:
- to use an IDE like AVR Studio to write and compile code in C (and knows how to use AVR Libc, WinAVR, etc)
- to burn fuses and hex files to AVR chips using an AVR programmer
- to use the GPIOs pins, the UART port, and the SPI port on an AVR chip
- to use Hyperterminal (or something like it)
- to use a serial port, either a USB to TTL serial converter, or a MAX232
- to level shift signals between 3.3V and 5V devices on a SPI bus
- to use a voltage regulator to get a 3.3V power supply
- to breadboard, wire, solder, and other misc stuff
Super Important Relevent Links:
MicroSD cards are cheap, but if you need a card reader, the surface-mounted holder would cost $4 + shipping, and although I can probably solder that, I still would like to do testing on a breadboard. Sparkfun does sell a breakout board for their microSD holder for $15 + shipping, but that’s a little too expensive. I’ve decided to buy a 1 GB microSD card that came with the microSD to SD adapter for $10 at a local place, and solder pin headers to the adapter so I can easily insert it into a breadboard.
The disadvantage is that there is no mechanical switch that can tell your microcontroller whether or not a card is in place, also there’s no springy push-to-eject mechanism. I can live without either, since the software can detect the card (through error handling) and pulling out a microSD card isn’t that much of a pain to begin with.
MMC and SD cards have near identical interfaces, SD cards have two additional connectors which are not important to us. MMC and SD cards have a SPI interface which makes it very easy for hobbyists to interface with these cards with low cost equipment.
Connect the SD card to the microcontroller following this table and picture (draw your own schematics using this table since connections on different chips may vary).
|Pin on SD card||Function||Connect to Pin on AVR|
|MOSI (Master Out Slave In)||The master refers to the device that generates the clock (the microcontroller), the SD card is the slave. Data on this pin travels from the microcontroller to the SD card. Also known as "DI".||MOSI|
|MISO (Master In Slave Out)||Data on this pin travels from the SD card to the microcontroller. Also known as "DO".||MISO|
|SCK||Serial clock pin, also known as "CLK"||SCK|
|CS||Chip select, the SD card pays attention to the data traveling on the SPI bus when this pin is low, and ignores the data on the bus when this pin is high||Port B 3, but it can really be any pin since this one is software definable. You should use a pull-up resistor (I use a 10k and it works fine) on this pin.|
|GND||Vss, ground, 0V||Ground|
|3.3V+||Vcc, power supply pin, supply 3.3V to this pin to power the card.||Only connect to a 3.3V power supply|
Take special care because the SD card only runs on 3.3V and the SPI bus is not 5V tolerant. If you do not know how to properly level shift, make sure your ATmega644 (or whatever chip you are using) is powered with only 3.3V and never 5V, and that your programmer is using 3.3V and not 5V. On most USB programmers, you can select whether or not the programmer powers the target, do not let the programmer power the target. If you’re programmer use a buffer chip, make sure the buffer chip is powered with 3.3V and not 5V.
Wire up your AVR and SD card on the breadboard, wire up your programmer and serial port, I used a 3.3V LDO regulator to supply everything with 3.3V using the 5V from my USB port.
I’ve also found that hot-inserts (inserting the card while the power is on) can cause the AVR to reset and also mess up the USB to serial converter. I put a (rather large) 470uF capacitor between Vcc and GND and that solved the problem, it acts as a power reservoir during the sudden large current draw caused by the inserted card.
example fuse bit setup for ATmega644:
Go download FatFs and the sample projects, the link to the download is on the bottom of the page.
Once you’ve downloaded the core FatFs files, place ff.c ff.h diskio.c diskio.h integer.h into your own project folder, and include it in your project (just add them into the project in whatever IDE you are using).
WARNING: this info may be outdated, the files that you need may be slightly different, or have slightly different names. It is a good idea to look at the example projects provided, instead of following this page
Go edit ff.h , and configure the definitions to your needs. Make sure you set “_FS_TINY” to 1 to make it work on an AVR chip.
To initialize the system, you must first initialize the disk by calling disk_initialize(0), then mounting the file system by calling f_mount(0, &FATFS_Obj) where FATFS_Obj is an instance of the FATFS data structure. This instance must be declared static. The initialization will look like this:
FRESULT f_err_code; static FATFS FATFS_Obj; disk_initialize(0); f_err_code = f_mount(0, &FATFS_Obj);
From the code above, if f_err_code is not equal to 0 or FR_OK, then there is a problem. Most FatFs functions will return an error code that is not equal to 0 when something goes wrong. You can use this fact to detect the presence of a card. Be creative with this fact, my MP3 player already supports hot card insert support.
Say I wanted to write “bar” and a integer that’s equal to 5 as a decimal to the beginning of a file called “foo.txt” in the directory “moo”.
FIL fil_obj; int variableName = 5; f_open(&fil_obj, "/moo/foo.txt", FA_WRITE); f_printf(&fil_obj, "bar %d", variableName); f_close(&fil_obj);
fil_obj is an instance of the FIL struct, which is a file handle. This file handle acts as an identifier so FatFs knows which file you want to work with, having multiple FIL structs lets you have multiple open files. f_printf works exactly like how it works in stdio.h. When opening a file, you specify the permissions, such as read or write, there are also permissions such as create the file if it doesn’t exist. When you close the file, it tells the file table where the file ends so you don’t end up with a corrupted disk.
Just by knowing the above code, you can now make a datalogger or something like that. Have fun.
Things can get more complicated, go read the documentation on the FatFs website