Go Down

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


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!


Feb 25, 2009, 08:50 pm Last Edit: Feb 25, 2009, 08:52 pm by sirmorris Reason: 1

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 :D

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.



Feb 25, 2009, 09:40 pm Last Edit: Feb 26, 2009, 04:04 am by bpeoples Reason: 1
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...


Mar 01, 2009, 04:07 am Last Edit: Mar 01, 2009, 04:46 am by koetting Reason: 1
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);

void error(const char* s)
 pprint(PSTR("Error: "));
 pprint(PSTR("<press reset>"));
 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;


 // 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);

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

void setup(void)

 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"));

 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!"));
         pprint(PSTR("Failed to write updated data."));
       pprint(PSTR("Failed to read data.bin."));
     error(PSTR("Found data.bin, but it's too small."));
   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.



Mar 03, 2009, 08:55 pm Last Edit: Mar 03, 2009, 08:56 pm by sirmorris Reason: 1

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.


@New Colin:

I have a sketch at http://docs.google.com/Doc?id=dqhc6fg_0gmk96kdd which uses the DS1307 with sirmorris's SD card software successfully. There's a LOT of code in there which will be totally irrelevant to you but I'm sure you can isolate the bits you need. If not, PM me & I'll cut the code down for you.



Mar 06, 2009, 03:00 am Last Edit: Mar 06, 2009, 03:04 am by johanzebin Reason: 1
Hi all,
and thank you for this really good documentation! Because of this, I'm convinced to try arduino for a study project I'm working on!

However, one question remains... and I hope it's not too stupid  ;) .. I tried to figure this out for hours now, but seemingly cannot decide it on my own.

The issue is this: we also depend on having fast PWM signalling - at a time-resolution of at least 64 micro-seconds / i.e. a minimum frequency of 15.625 kHz .

From the forum I have learned so far that PWM is possible on pins 5,6 (controlled by timer0) and 9,10 (timer 1), and 3,11 (timer2).
Since timer0 is also used for other stuff than pwm, I don't want to change it to have higher frequency... pins 9,10,11 are used by SPI anyway, which leaves us only with pin 3.

My question: Does somebody know whether setting timer2 to such a high frequency has any side-effects with the SPI-protocol? Maybe somebody might even try this shortly with a running setup? This would be amazing and probably save me days of studying Atmel's datasheet  :'(

Thank you already in advance!

PS: Here's a (hopefully working) post indicating how to change the PWM frequency of timer2: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1234764073#12 or http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/12#4


That is an excellent summary. If you put that onto a single page somewhere please
post the link.

(* jcl *)


@johanzebin: I don't know about this. The information would be great to add to the article though, so please let us kn ow how you get on. I think your best approach is to stick a frequency meter or oscilloscope on the PWM pin and look at what happens when you run the SPI code. You could write a little loop which writes or reads from the card and display the timings with and without tweaking the PWM rate. Experiment, have fun :)

@jluciani: thanks! I looked into putting this up in the playground, but  I don't have the time to learn wikispeak right now :( Any volunteers?? ;)


It seems to me that one ought to be able to write a MUCH smaller implementation for FAT16 on SD/MMC if all you want is read-only, AND you're willing to take into account the fact that accessing the flash card is relatively fast (compared to a floppy, say) so you won't need to cache quite as much info.

Does anyone know of such an implementation?


and in very simple mode, here....
I would like to read text [no writing] from a txt file on an SDcard, and send it to an LCD using serial.print... I have looked high and low through all the examples, but being um noob, I am having a heck of time figuring out how to do that. Could someone hold my hand, e.g. a code example? I use a 4x20 display, and of course there are control characters. I imagine I have to format the text for half the screen then the other half, each new section on a new line in the text file, padding each line to proper length [since I can't figure out how to parse/split text]... I am also having some um conceptual problems on how to go from bytes -> ascii, but I guess that will just be obvious in the code example, for which I certainly and hopefully do thank you in advance! I hope.



I haven't got the time to play ATM, but you need to do the inverse of devicePrint.

Code: [Select]
 bytesInBuffer = 0;

char getCharFromBuffer()
 if (bytesInBuffer == 0)
    readSectors(sector, buffer, 1);
    bytesInBuffer = 512;

 return buffer[bytesInBuffer];

A byte is an 8-bit wide data type. So is a char. The distinction is _usually_ whether the value is treated as signed or not. The ansi character set only uses 127 values because logically it's unsigned.

Do you really need SD for characters for an LCD display? Just how much text are you wanting to display??? Wouldn't it be better to store the strings in PROGMEM?



Am I getting this right, if I set up an arduino board, using the SPI pins connected to an SD card socket, with proper resistances;etc, I can log(save) data on an SD card?  Then after saving data on the card, I can remove the card from the card socket, bring the card to a PC, and enter the data directly to the PC via one of the computer's ports, such as one of the USB ports? Can I do this by using the uFAT library?

Or do I have the wrong idea?

I've already downloaded uFAT2, uFAT EXAMPLE, DEVICE PRINT, DEVICE PRINT LIBRARY, DEVICE PRINT DEMO.  Are the uFAT2 & DEVICE PRINT libraries for different purposes?

I've uploaded uFAT example and DevicePrint demo to my arduino board and, as soon as I connect the card socket to the SPI pins I'm ready to give it a try.  Is there anything else I should do? Is there anything that should be done with the SD card first?  Does it need to be pre-programmed for FAT operation?

Thanks in advance

Go Up