You've probably seen or done lots of cool microcontroller projects already, but did you ever consider adding permanent data storage to your projects? I did, and there are numerous advantages: saving data from sensors (data logger), saving configuration files, reading external content like images or songs, and so on!
One thing is to connect an SD Card to an AVR microcontroller via SPI (relatively easy), a completely different thing is to be able to manipulate files that are later compatible with your PC (if you take the card from your microcontroller project and connect it to your PC).
This is because PC data storage uses a file system, which needs to be implemented in order to achieve compatibility. There are several file systems currently in use, keeping up with constantly evolving hardware: FAT, FAT32, NTFS, EXT, etc. A file system is a way of organizing the data in the entities we are all used with: files, folders, but there is much more than that. One of the simplest file systems, yet compatible with most Operating systems available today is the FAT (older and with reduced capabilities) or the FAT32 (acceptable for the needs of most microcontroller projects). Even so, implementing the FAT32 logic is a considerable effort, taking a lot of time to do from scratch. Here is where FatFS comes to help!
ATmega128_SDCARD_FAT32

About FatFS

FatFs is a generic FAT file system. Intended as a module for small embedded systems, the FatFs is written in compliance with ANSI C and separated from the disk I/O layer completely. This makes it independent of the hardware architecture. It can be incorporated into low cost microcontrollers, such as AVR, 8051, PIC, ARM, Z80, 68k and etc..., without any change. Just perfect for this purpose!

Basic circuit diagram

I opted using SDCard modules available on Ebay. They come with a nice quality SDCard holder, a few resistors and capacitors, a LM1117 3.3V regulator, exposing the relevant data pins for easy connection via SPI to the microcontroller: CS, MOSI, MISO, SCK, VCC and GND.
sdcard_module_1 sdcard_module_2
sdcard_diagram_avr
Connecting it to the AVR is simple. Just connect the header pins to the corresponding SPI pins on the microcontroller. You have: MISO, MOSI, SCK. For CS Choose any available IO pin. VCC must be connected to the 3.3V. Let me know in case you need any help doing this. You can also connect an SDCard (or mini/micro sd) to the AVR directly, just match the pins correctly, according to the table in the picture above, and make sure you are using a 3V supply for the Card. The AVR can be connected to 5V (I am using it this way in this example).

The software

As I already told you, I'll be using the FatFS code here. FatFS also has a minimization level function, see _FS_MINIMIZE in ffconf.h :

  1.  
  2.  
  3. #define _FS_MINIMIZE 2 /* 0 to 3 */
  4. /* The _FS_MINIMIZE option defines minimization level to remove some functions.
  5. /
  6. / 0: Full function.
  7. / 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
  8. / are removed.
  9. / 2: f_opendir and f_readdir are removed in addition to 1.
  10. / 3: f_lseek is removed in addition to 2. */
  11.  

There are also other macros for disabling the code that you need and save on the output hex size (see _USE_LABEL, etc).
My sample mounts the SDCard, and creates a file. Then it appends some additional content. Finally it shows file size, the volume string and the serial number, and tries to read the data from the file and displays it on the 5110 LCD.

  1.  
  2. // init sdcard
  3. UINT bw;
  4. f_mount(0, &FatFs); // Give a work area to the FatFs module
  5. // open file
  6. fp = (FIL *)malloc(sizeof (FIL));
  7. if (f_open(fp, "file.txt", FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) { // Create a file
  8. char *text = "Hello World! SDCard support up and running!\r\n";
  9. f_write(fp, text, strlen(text), &bw); // Write data to the file
  10. f_close(fp);// Close the file
  11. if (bw == strlen(text)) { //we wrote the entire string
  12. led1.Set(1); // Lights LED if data written well (D4 led on atmega128 board)
  13. lcd.goto_xy(0,0); lcd.send_format_string("Created:%dB\n", f_size(fp));
  14. }
  15. //else led2.Set(1);
  16. }
  17. // test append
  18. if (f_open(fp, "file.txt", FA_WRITE | FA_OPEN_ALWAYS) == FR_OK) { // Open existing or create new file
  19. if (f_lseek(fp, f_size(fp)) == FR_OK)
  20. {
  21. char *text2 = "This is a new line, appended to existing file!\r\n";
  22. f_write(fp, text2, strlen(text2), &bw); // Write data to the file
  23. if (bw == strlen(text2)) { //we wrote the entire string
  24. lcd.send_format_string("Appended:%dB\n", f_size(fp));
  25. }
  26.  
  27. }
  28. f_close(fp);// Close the file
  29. }
  30. char str[12];
  31.  
  32.  
  33. // get card volume
  34. char szCardLabel[12] = {0};
  35. DWORD sn = 0;
  36. if (f_getlabel("", szCardLabel, &sn) == FR_OK) {
  37. lcd.send_format_string("%s SN:%X\n", szCardLabel, sn);
  38. }
  39.  
  40. // read from file
  41. if (f_open(fp, "file.txt", FA_READ ) == FR_OK) { // Create a file
  42. char text[255];
  43. UINT br;
  44. f_read(fp, text, 255, &br);
  45. f_close(fp);// Close the file
  46. // cut text the easy way
  47. text[10] = 0;
  48. lcd.send_format_string("Read:%s", text);
  49. }
  50.  

The result:
atmega128_sdcard_fat32_fatfs sdcard_pc_view

The sample code, compilable with AVR Studio is available here:
test_sdcard_avr128

Related Post