Arduino SD Library support for MMC Cards


So I’ve been fiddling a lot these days with Arduino’s SD Library. I’m doing experiments with SD Cards as part of my BOA (Brainfuck on Arduino) project and I already have a completely functional Brainfuck interpreter that runs on my Pro Mini board and uses a 23K256 (32KB) SPI RAM as BF “RAM space”.

The problem I’m trying to solve now is PROGRAM space (where the actual code will reside). So just for the kicks I”m testing SD Cards as a first “storage” alternative (after all it’s cheaper than EEPROM ICs and offers way more space than the few KB you’ll mostly get from EEPROMs).

(Click here to go directly to the MMC Problem and skip my particular project)

Arduino Pro Mini + SD Cards = Fun
Arduino Pro Mini + SD Cards = Fun

Both the RAM chip and the SD Card use SPI so they are actually sharing the bus, which greatly reduces the number of pins needed to drive both devices. Since there’s no way (nor need) to access the external RAM and the SD card at the same time I’m using a single SS (Slave Select) pin that’s connected to one of the devices and also to a transistor-based NOT gate that goes to the other, making the SS signal act as a “toggle” between both the SD Card and the RAM IC.

 

I also have my own SPI routines that give me complete control over the SS line, which is something that my “device toggle” solution requires.

Now let me tell you that I’ve tried Arduino’s libraries and the SdFat implementation simply rocks. It’s really easy to use and you can read files from SD cards formatted in FAT16 and FAT32. It supports SD1, SD2 and SDHC cards.

Making my Arduino read a text file from the SD Card was a breeze and it makes it easy to change the code that runs on the device:

Reading a file from a FAT volume with Arduino's SD library.
Running a Fibonacci (< 100) generator from http://esoteric.sange.fi/brainfuck/bf-source/prog/fibonacci.txt, stored in an SD card.

As expected, reading files from a filesystem that involves several data structures like logical volumes and partitions and tables require an awful lot of RAM. It’s also not particularly fast. A Brainfuck program that normally ran in 3 seconds when read from Arduino’s internal RAM took ~10.8 seconds when it was read from a text file inside the SD (~9.2 seconds with later SPI optimizations). That’s a hell of a penalty for just using a “high level” storage solution!

I’m now trying to directly work with block read/write operations to see if I can improve the performance with cached bock reads before ditching the idea of SD Cards entirely. To be honest I’m not expecting real performance improvements as the bottleneck is probably in the SD card communication protocol itself, not the filesystem, but I’m mostly continuing my experiments with SD Cards for learning purposes.
It was in this process that I happened to stumble into an interesting thing…

The MMC Problem

I have 4 “SD” cards here, and -curiously enough- they are all different types! I’ve got a 512MB SD2, a 1GB SD1, an 8GB SDHC… and an ancient 16MB (yes, MB) MMC.

Arduino’s SD Library has no problem reading the SD1, SD2 and SDHC card… BUT it fails miserably with the old MMC Card.

As I’m trying to develop my own quick and dirty SD access library (for low level cached block reads) I started studying the Simplified SD Specs and Arduino’s implementation. I’ve also been checking other sources like this REALLY helpful website.

The Arduino implementation closely follows the specs but it does not implement an oddity of old cards that Elm-Chan’s website explains rather succintly:

Because ACMD41 instead of CMD1 is recommended for SDC, trying ACMD41 first and retry with CMD1 if rejected, is ideal to support both type of the cards.

This is what you’ll find in Sd2Card::init() in /libraries/SD/utility/SD2Card.cpp:

  while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
    // check for timeout
    if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
      error(SD_CARD_ERROR_ACMD41);
      goto fail;
    }
  }

As you can see, it  only tries the ACMD41 command until it gets a response or times out, and it’s at this point where my MMC card fails to “comply” with Arduino’s SD initialization! Old MMC cards respond to CMD1, not ACMD41! Implementing the website’s suggestion and trying CMD1 if ACMD41 gets rejected makes this code work for every card I own!

  status_ = cardAcmd(ACMD41, arg);
  while (status_ != R1_READY_STATE) {
    // check for timeout
    if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
      error(SD_CARD_ERROR_ACMD41);
      goto fail;
    }
    // Switch to CMD1 if the card fails to recognize ACMD41
    if (status_ & R1_ILLEGAL_COMMAND) useCmd1 = true;
    status_ = (!useCmd1 ? cardAcmd(ACMD41, arg) : cardCommand(CMD1, 0));
  }

(you’ll obviously need to define CMD1 too. A good place to add this is SdInfo.h, with the other CMDx constants)

uint8_t const CMD1 = 0x01;

And also add useCmd1 to the method’s data declarations (inside init() somewhere in between the other variables used by the code),

bool useCmd1 = false;

With this, Arduino will be able to cope even with extremely old cards like my 16MB MMC.

Now, just as Elm-Chan’s website said, the SD Cards specs specifically advice against CMD1 initialization:

2.1mm SD Memory Card can be initialized using CMD1 and Thin (1.4mm) SD Memory Card can be initialized using CMD1 only after firstly initialized by using CMD0 and ACMD41. In any of the cases CMD1 is not recommended because it may be difficult for the host to distinguish between MultiMediaCard and SD Memory Card.

Their concern is having MMC cards recognized as SD Cards if the host were using CMD1 for initialization instead of ACMD41.

I don’t think we will run into problems with the quick hack I just outlined though, basically because in any case we are always trying ACMD41 first, and then, only if the command is not recognized by the card, we start trying with CMD1. In fact, this actually makes possible to distinguish between both types of cards, as only old MMC cards will respond to ACMD41 with “illegal command”.

I hope this serves as a good starting point for those who want to customize the included SD Lib for specific purposes or quick-patching it to support old MMC cards.

I’ll probably be using this in the custom code I’m writing for my project.

EDIT: I updated the last few paragraphs to better explain why the specs don’t recommend CMD1 initialization and also modified the hack to make it more robust and easier to add to Arduino’s Sd library.

EDIT2: Clarified a bit the instructions regarding the extra required declarations.