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.

 

34 thoughts on “Arduino SD Library support for MMC Cards

  1. hie Mr E
    A very interesting article.
    l copied the above code according to the instructions you gave us and the code complied well ..bt my proteus 8 still have the intialization error on MMC.
    l am not sure where l am going wrong
    .Can you help me.
    thank you Mr E

    • Hi LeeChando, first time I hear of an MMC card not working with that code. I guess it’s still a possibility since there are several revisions to both MMC and SD standards, and the MMC specs is actually still evolving. There are modern MMC cards with some crazy improvements, for which this code may not work. Having said that, if you can inject several Serial.print() debugging statements between each cardAcmd() and cardCommand() call during the initialization you’d be able to determine which step the process fails.

      If you can try that maybe I’d be able to help you with this. I’d be really interested in knowing where it’s failing.

    • I’m glad this helped you with your Proteus project.
      And it’s quite flattering to see my blog featured in a video! Thanks!

  2. Hi friend, sorry to bother you.
    I don’t know, but maybe you can help me.
    I’m trying to modify SdFat by adding your hints.
    Do you know SdFat library?
    It’s pretty similar to the standard Arduino’s SD library but much better, even if actually now SD library is better of SdFat library cause it can also handle MMC thanks your work.
    By adding to it your scripts I got these errors:
    C:\Arduino\libraries\SdFat\Sd2Card.cpp: In member function ‘bool Sd2Card::begin(uint8_t, uint8_t)’:
    C:\Arduino\libraries\SdFat\Sd2Card.cpp:317: error: ‘status_’ was not declared in this scope
    I suspect the culprit can be due the fact SD library and SdFat library are similar but not the same.
    For instance in SdFat there is ‘bool Sd2Card::begin(uint8_t, uint8_t)’:, no ‘bool Sd2Card::init(uint8_t, uint8_t)’:
    Any clue, if you can?
    Thank you!
    Arduino jr

    • Hi!
      As far as I understand the SD Card Library in Arduino is a sort of wrapper for the SDFat library, but they *may* be using an older version of SDFat, though.
      I checked the last version of SDFat and the variable “status_” is now called “m_status”, so replacing every instance of “status_” in my code with “m_status” should do the trick (that seems to be the only change).
      Hope that helps!

    • Thank you friend.
      Indeed that does the trick, it works!
      I wonder where have you been the whole time until now cause you are saving a lot of people with your knowledge.
      My congratulations and respect.
      Arduino jr

  3. Hi Elias.
    Very interesting article!
    Me too I deal with MMC and SD very often.
    I think your idea is the Columbus’ egg, I don’t understand some things though.
    I have to premise I’m not so skilfull like Arduino’s programmer, anyway I have tried to change the standard SD2Card.cpp file adding your suggestions but it doesn’t work.
    For instance I get many errors attempting to compile the CardInfo sketch.
    More generally using my own modified SD2Card.cpp always I get errors with any related sketch.
    I guess it’s because I’m not able to put “uint8_t const CMD1 = 0x01;” and “bool useCmd1 = false;” in the right place.
    Could you explain more?
    Thanks in advance.
    OrSoOn

    • Hi,
      Thanks for your kind words!
      Now, about your problem: You’ll find a file called SdInfo.h in the same folder where the Sd2Card.cpp file is. This file contains most of the definitions used by Sd2Card.cpp. There you’ll see a lot of CMDxx constants defined so it’s the perfect place to add our CMD1.

      bool useCmd1 = false; should be added to the variable declaration block inside Sd2Card::init(). You can add it after the “uint32_t arg;” line, or before “errorCode_ = inBlock …” or somewhere in between. The important thing is that it should be inserted before function calls and other statements inside init().

      Hope that helps!

    • Hey man you are the number one!
      Elias you are right I did do so and it works like a charms.
      Terrific!
      You really deserve the Oscar!
      OrSoOn

Leave a Reply

Your email address will not be published. Required fields are marked *