Go Down

Topic: SD/MMC From the ground up (Read 54292 times) previous topic - next topic


A number of people have asked if the information in the thread "SD card read/write with Arduino" be summarised for newcomers.

Well here it is. After reading this you should be able to:
  • Add an SD/MMC card to your Arduino.
  • Read data from and write data to it using the uFat and DevicePrint libraries.
  • Understand a few of the issues related to the process.

This is my own personal take on the subject and I can only offer descriptions of and advice on using the code which I developed, so apologies if this comes over like an advert at times - it's truly not meant to be. I hope that opinions and advice about other code available will be added in subsequent posts and that this thread can be a focal point for those wising to take advantage of the work that has been contributed by the Arduino community at large.

On with the show.

MMC cards are a microcontroller's best friend. They can speak a serial protocol called SPI which is natively supported in the microcontroller hardware. I love 'em. You only need 4 IO lines to transmit commands and data to the card and receive data back. These connections go by a number of names but this is the set which will service you best:

/CS  (not card select)
CLK  (clock)
MOSI (master out, slave in)
MISO (master in, slave out)

Master in all cases is the Arduino. Digital pins 10 through 13 are the dedicated SPI connections. If your application already uses these you'll have to move things around! All the SD/MMC enabled boards I've seen use these pins. The information here should be applicable to anyone's SD/MMC enabled hardware.

Here's the MMC pinout...

...and here's how it needs to be connected:

MMC   Arduino
1       10
2       11
3      gnd
4      3v3
5       13
6      gnd
7       12

MMC Cards require between 2.7 and 3.6 volts to operate. Low power varieties are also available that work down to 1.8V, though these are special and more rare. You can power a card from the Arduino's 3v3 output if it's available on your particular Arduino variety. There will need to be some level conversion on the /CS, CLK and MOSI lines, as these output 5V. As the Arduino regards 2.4V and above as a logic high, no level conversion needs to be done on the MISO line. There are some boards out there which operate at 3.3V natively, and for these boards all lines may be connected directly.

We do any neccessary 5V level conversion in the simplest way possible: with a voltage divider. There is great information elsewhere, linked below, so I won't go into any more detail than this:

VDD --[R1]-- v --[R2]-- GND

The voltage v can be calculated by using the following formula:

v = (VDD*R2) / (R1+R2)

In most cases you should use R1 = 1K8 and R2 = 3K3. Tapping the voltage at the centre of the resistor/resistor connection yields ~ 3.24V. Actual values will vary a little depending on the accuracy of the resistors.

Some cards can cope with voltages out of the specified range, and some cannot. I have cards which have functioned happily as high as 4.3V, and one in particular which developed a high fever followed by sudden explosive death when faced with the same levels... Ahem. The less said about that the better.

The product standard dictates the signal levels that you should aim for in your circuit.

Input HIGH voltage:
  • min:  0.625 * VDD
  • max:  VDD + 0.3
Input LOW voltage:
  • min:  VSS - 0.3
  • max:  0.25 * VDD[/tt]

Here is what I consider to be the gold standard schematic produced by agent_orange, to be found with other good stuff at the start of the parent thread.

There are 3 kinds of socket available. MMC, SD and floppy-drive edge connector :D
  • MMC sockets have 7 connectors and map 1:1 to the diagram above.
  • SD sockets can have up to 12 or 13 pins, but only the main 7 need to be used. The remainder are for detection of card insertion and write protect status of compatible SD cards. agent_orange's schematic shows a socket of this type.
  • Floppy edge-connector sockets are best for MMC cards (SD cards are thicker with ridges protecting the contacts) but are undoubtedly the cheapest! Check out the ingenious hack at Up All Night Robotics, linked below.

There are many excellent introductions to the SPI protocol and I don't think that a discussion of it here would benefit anyone except the most inquisitive. There are a couple of links at the end of the post which should satisfy these curious cats.

It might not even help to discuss the MMC's command protocol over and above the fact that data transfers happen in 512 byte blocks. This can be changed, but I don't think there's a compelling reason to do this, especially as this most usually matches the sector size of the card.




Once the hardware is in place you'll need some software to drive it. As I said previously I'm hoping that people will contribute their code and experiences to this thread - I have only ever used one library to drive the card transfers. This was something that I adapted from the wonderful code of the SD2IEC project. I can't talk about 'The Roland Library' or any other as I don't know them. They should all be pretty much interchangable though.

For persistent storage on a card feel free to invent your own system. Reading and writing to the raw sectors is usually only a function call away. This is the least-cost entry into the storage arena. It's simple and reliable and you are in full control. But you will pay the price when it comes to moving the data around. You'll require raw sector access to the card from your computer which is certainly possible but inconvenient.

If you want to be able to log data and have it transferrable to your computer, then you'll need to  be able to use the file system to some extent or other. This is a whole order of magnitude more complex than even the low level card protocols. Cards may be formatted with a number of filing systems, though generally you'll see one of the FAT (tm, probably) family.

This format uses tables of linked lists of cluster offsets (the File Allocation Tables or FATs) which can be used in turn to locate sectors. Clusters are a collection of multiple sectors, and the sectors per cluster count can vary depending on the formatting.
  • FAT12 is the oldest format out there. I still see cards formatted like this though, even today. It uses 12 bits to describe cluster offsets in the tables. It's limited to cards of up to 32Mb.
  • FAT16 is the most common. So common it's usually referred to as just FAT. It uses 16 bit tables and can describe the layout of cards up to 2Gb.
  • FAT32 is the daddy. If you need a 2Tb card, you'll need FAT32. As you've probably guessed it uses 32bit values for cluster offsets.

Libraries do exist for talking to a FAT formatted device in the language of files. These are excellent for the times when you need that level of access and ease of use. They do tend to weigh in quite heavily though. In order to use a full filesystem approach you'll more than likely need one of the new ATMega328s, with their upgraded RAM and flash quotas, in your board.

Another approach is to swallow some small restrictions and use the very minimum of file system in order to use raw sector access to access your data whilst still retaining the ability to move files between Arduino and computer. uFat is a library that I wrote to do just this.

Just what restrictions might be necessary to go the low FAT route?
  • The donor/target file must be contiguous. When you delete or truncate files on a computer the sectors allocated to them are returned to a pool to be re-used when you next create or expand a file. This can mean that a file occupies sectors scattered all over the card. This is known as fragmentation, and it's why we have drive defragmenters. If you write to a fragmented file using raw sector access you might write over other unrelated data on the filesystem. Or the data you wrote won't be visible when you read the file on your computer. How to get around this? Format the card, create the donor/target file on the computer containing enough data to satisfy your needs and then copy it over to the card.
  • Target files should reside in the card root directory. No subdirectories are allowed - the code to traverse subdirectories is huge compared to the rest of the library and its inclusion is largely unneccessary. The root directory can hold 512 files, more than enough for most datalogging applications I suspect.
  • No long filenames. Stick to 8.3 and all will be well. Just like directories, the LFN system is built (some may say hacked, I couldn't possibly comment, ahem) upon the existing directory structure using other directory entries to hold the additional information. Again, I don't think the additional memory usage is contributing to anything sufficiently valuable.

If you recall MMC sector transfers happen in bursts of 512 bytes. This isn't much fun if the data you're writing is of differing length packets. You'd have to keep track of your buffer usage and flush it to card at the right time. Plus, you'll need some extra help to get string output into a file instead of raw binary.

On top of (to the side of?) uFat I've reworked some code by bobemoe and produced DevicePrint, a hardware-agnostic library which enables formatted string output to any sector-based device. This library uses the newly-abstracted Print class available in Arduino-0012 and above. You get the same useful interface that you're used to using with the Serial class.

These libraries should provide you with most of your required daily intake of vitamins and minerals. If in doubt call your medical practitioner.

I think that's as much as I can say on this matter, I look forward to the conversations ahead. Huge thanks go to everyone who's contributed to my understanding of this subject and those who have helped me develop this code by using it!




fantastic summary sirmorris!
If you could add a small snippet of code, something like the "hello world" for MMC, it would be perfect :)


Don't forget mini-SD and micro-SD, both of which can also speak the SPI MMC protocol.

Someone suggested on another forum that the adapters that typically come with mini/microsd cards make pretty good sockets for the smaller cards.  You can solder wires (carefully) to the full-sized adapter...


Feb 20, 2009, 12:34 pm Last Edit: Feb 20, 2009, 01:00 pm by sirmorris Reason: 1
You're right! I should have mentioned that I'm taking MMC to mean all varieties: maxi, midi, mini, micro, nano, femto, and bob.

If you're feeling pushed for time and/or cash then you could also  solder directly to the card, wires or a pin header - its connectors have standard .1" pitch. I feel dirty for just saying that.

Hello World coming up. I'll try to abstract it to show the required software components as opposed to the use of a library.


Nice work and summary.
Is it going to be included into the playground.

Could this work with the card socket on the official arduino ethernet card?
Or is the way its hooked up wrong for this method?




It works without a problem on lady ada's wave and GPS shields. I haven't experienced the official ethernet shield but I'd be very surprised if it used different pins, as the connections are defined by the Arduino's hardware SPI pin-out which is fixed.

I will put this up in the playground, I think.


Excellent summary sirmorris!
Thats more than worth to be placed in the playground!


Thanks! I looked today and I think I'll have to spend some time working out how to edit the wiki..!


Great work, nice write-up.

It took longer to find an SD card than it did to get it working!



This pic might be helpful for some:


It can be a little bit confusing, since the pins are not exactly ordered from 1-9.


Very cool Morris you've been a big help to the community. Thanks for all the help you've provided.


I did some quick testing using the sample DevicePrint sketch, and the fastest I can get any of my cards to write is about 500ms for 512bytes.

What is the bottleneck? How fast do you have the clock set? 4mhz? As I understand it, it has to be set at 4mhz during initialization, but can be bumped up to 40mhz(?) afterwards.


I took a look at the code. You have SPCR = B01010011, and in the comments I see "clock = f/4". Maybe I misunderstand, but isn't that clock/128? Shouldn't SPR1 and SPR0 be set to 0 to get clock/4?

I changed both to 0, and enabled SPI2X in SPSR. On my best card, the writes are now 80ms per 512byte block!

Is this safe? Is there any error checking?

Go Up