SD/MMC From the ground up

Links:

Original thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1206874649

MMC Product Manual: www.sandisk.com/Assets/File/OEM/Manuals/ProdManRS-MMCv1.3.pdf

MMC/uFat2 and Deviceprint: Arduino Nut: Libraries

The Roland: www.roland-riegel.de - sd-reader: MMC/SD/SDHC card library

AdaFruit: Audio Shield for Arduino

SD2IEC: http://sd2iec.de/

SPI introduction: Serial Peripheral Interface - Wikipedia

MMC card information at Elm: How to Use MMC/SDC

Fat module at Elm: FatFs - Generic FAT Filesystem Module

MMC card information at retroleum: http://www.retroleum.co.uk/mmc_cards.html

Voltage divider theory: Voltage divider - Wikipedia

Floppy-connector socketry: http://uanr.com/sdfloppy/

Thanks for reading! :sunglasses:

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

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...

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?

Thanks

Gordon

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!

Thanks.

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?

You're right, my bad! I'll amend the code asap.

You should be able to drive the card as fast as Arduino can go without any problems.

This is a great library-- and it worked on my micro-SD card (Transcend 1G) right out of the chute.

One little problem I ran into is that if I have the SD card and an SCP1000 pressure sensor on the same SPI bus, I can't get them both to work.

I can get them to work individually in the same hardware configuration, and I can get the pressure sensor working OR the SD card working. (just by commenting out parts of the code) But if I initalize the SD card, the pressure sensor just returns 0x00's.

I know there's some funky SPI code to interface to a SD card-- is the card perhaps responding even if the CS line is high?

Also, on the library, mmc.h should have the #defines for the pins, not mmc.c -- since CS doesn't have to be Arduino 10 if you have multiple devices =)

Thanks again!

Excellent!

As for the multiple SPI slave situation - indeed, this is something that I've thought about but, like most things, I don't do anything about until it bites me :smiley:

So every device in the SPI chain needs its own /CS line. The only time you need to do anything funky with the one allocated to the MMC card is during initialisation. In order to 'wake it up' and put it in SPI mode you need to send it at least 72 clocks with CS high. I don't think that would cause too much of a problem in a multiple device chain as long as your other devices also have their select lines de-asserted.

If however the MMC will respond even with /CS high.. Oh oh. I've never investigated this. If this is the case then it's time for SoftSPI! This is something I've dabbled with when I was debugging something or other. It's a software implementation of SPI mode 0. You can allocate whichewver pins you like so there's no need to share any lines. I hope it doesn't come to this though... It's comparatively slow.

In practice it doesn't matter too much where the CS pin is defined if you use the MMC code as a library. It would have to be recompiled for every sketch that used it. A way around this would be to allow the CS pin be specified in a variable. It would need this facility in the libraries for each device though... Or not use a true library, instead copying the code to each sketch folder that required it.

C

Very good point! I can just re#define in my program and go to town.

I'd be interested to know if the card will respond with CS high-- the 72 clocks shouldn't do anything to the SCP1000, since it's CS is also high at the time. shrug might have something to do with my particular implementation.

Update: interestingly, it's the code and not the SD card-- I removed the card, but still had the software running. I had to completely remove power and replace power to get the SCP1000 to work with the new code. Bizzare...

Update: I downloaded the latest files and it works now ...

When I try to compile I get an error ... The last time I programmed in C was about 10 years ago, so forgive me if I should know this. I did try and figure it out myself, just not smart enough.

o: In function main': undefined reference to microfat2::walkDirectory(bool ()(directory_entry_t, unsigned int, void*), void*)


#include <WProgram.h>
#include <avr/pgmspace.h>
#include <microfat2.h>
#include <mmc.h>

byte sector_buffer[512];

char sprint_buffer[40];

// BEWARE - don't print strings longer than 39 characters!
// If you can't help it, adjust buffer size above.
//
void pprint(const char* s)
{
strcpy_P(sprint_buffer, (PGM_P)s);
Serial.print(sprint_buffer);
}

void error(const char* s)
{
pprint(PSTR("Error: "));
pprint(s);
pprint(PSTR(""));
for( /* ever */ ; ; )
{
digitalWrite(13, (millis() / 250) & 1);
}
}

bool showDirectory_walkerfn(directory_entry_t* directory_entry_data, unsigned index, void* user_data)
{
int* count = (int*)user_data;

Serial.print(index, DEC);
Serial.print(' ');

// Terminate the filename string.
// This is deliberately corrupting the buffer data, but that's ok.
directory_entry_data->filespec[11] = 0;

Serial.println(directory_entry_data->filespec);

// Increase 'seen file' count
*count = (*count)+1;

// don't stop
return false;
}

void showDirectory(void)
{
int count = 0;

pprint(PSTR("Directory of files on card:\n\n"));

microfat2::walkDirectory(showDirectory_walkerfn, &count);

pprint(PSTR("\n"));
Serial.print(count, DEC);
pprint(PSTR(" files found.\n\n"));
}

void setup(void)
{
Serial.begin(115200);

pprint(PSTR("uFat2 demonstration\n"));

if (mmc::initialize() != RES_OK)
{
error(PSTR("mmc init failed.\n"));
}

// Pass in the sector-sized buffer we'll be using ourselves later.
// uFat doesn't own it, it just needs to use it temporarily.
// We also pass in the address of a function that is used to read
// sectors from our device.
//
if (!microfat2::initialize(sector_buffer, &mmc::readSectors))
{
error(PSTR("uFat init failed.\n"));
}

showDirectory();

unsigned long sector;
unsigned long byteSize;

if (microfat2::locateFileStart(PSTR("DATA BIN"), sector, byteSize))
{
if (byteSize >= 512)
{
if (RES_OK == mmc::readSectors(sector_buffer, sector, 1))
{
for (int i = 0; i < 512; ++i)
{
sector_buffer = sector_buffer + 1;
* }*
* if (RES_OK == mmc::writeSectors(sector_buffer, sector, 1))
_
{_
_
pprint(PSTR("Written to data.bin OK!"));_
_
}_
_
else*_
* {*
* pprint(PSTR("Failed to write updated data."));*
* }*
* }*
* else*
* {*
* pprint(PSTR("Failed to read data.bin."));*
* }*
* }*
* else*
* {*
* error(PSTR("Found data.bin, but it's too small."));*
* }*
* }*
* else*
* {*
* pprint(PSTR("data.bin not present on card."));*
* }*
}
void loop(void)
{
* digitalWrite(13, (millis() / 1000) & 1);*
}

Thank you SirMorris. Your code worked first time. However I ran into trouble when I tried to also run it at the same time as having a DS1307 I2c real time clock header file included. This prevented even the PrintWelcome function executing properly. Do you have any suggestions where to start debugging?

I'd be happy to make any headers/source code available once I work out how to.

Thanks,
Col.

@koetting:

Have you installed the mmc & microfat2 libraries properly? It sounds like the compiler isn't finding the code. The folders need to be in arduino\hardware\libraries.

@New Colin:

Problems like this are usually related to memory depletion. Have a look and see if any memory allocations or large static buffers are being declared.