/* * Copyright (c) 2006-2007 by Roland Riegel * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "fat16.h" #include "fat16_config.h" #include "partition.h" #include "sd_raw.h" #include "sd_raw_config.h" #include "uart.h" #define DEBUG 1 /** * \mainpage MMC/SD card example application * * This project is a small test application which implements read and write * support for MMC and SD cards. * * It includes * - low-level \link sd_raw MMC read/write routines \endlink * - \link partition partition table support \endlink * - a simple \link fat16 FAT16 read/write implementation \endlink * * \section circuit The circuit * The curcuit board is a self-made and self-soldered board consisting of a single * copper layer and standard DIL components, except of the MMC/SD card connector. * * The connector is soldered to the bottom side of the board. It has a simple * eject button which, when a card is inserted, needs some space beyond the connector * itself. As an additional feature the connector has two electrical switches * to detect wether a card is inserted and wether this card is write-protected. * * I used two microcontrollers during development, the Atmel ATmega8 with 8kBytes * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash. * The first one is the one I started with, but when I implemented FAT16 write * support, I ran out of flash space and switched to the ATmega168. * * \section pictures Pictures * \image html pic01.jpg "The circuit board used to implement and test this application." * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board." * * \section software The software * The software is written in pure standard ANSI-C. Sure, it might not be the * smallest or the fastest one, but I think it is quite flexible. * * I implemented a simple command prompt which is accessible via the UART at 9600 Baud. With * commands similiar to the Unix shell you can browse different directories, read and write * files, create new ones and delete them again. Not all commands are available in all * software configurations. * - cat \\n * Writes a hexdump of \ to the terminal. * - cd \\n * Changes current working directory to \. * - disk\n * Shows card manufacturer, status, filesystem capacity and free storage space. * - ls\n * Shows the content of the current directory. * - mkdir \\n * Creates a directory called \. * - rm \\n * Deletes \. * - sync\n * Ensures all buffered data is written to the card. * - touch \\n * Creates \. * - write \ \\n * Writes text to \, starting from \. The text is read * from the UART, line by line. Finish with an empty line. * * \htmlonly *

* The following table shows some typical code sizes in bytes, using the 20061101 release with malloc()/free(): *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
layercode sizestatic RAM usage
MMC/SD (read-only)15760
MMC/SD (read-write)2202517
Partition4180
FAT16 (read-only)38340
FAT16 (read-write)79320
* *

* The static RAM in the read-write case is used for buffering memory card * access. Without this buffer, implementation would have been much more complicated. *

* *

* Please note that the numbers above do not include the C library functions * used, e.g. malloc()/free() and some string functions. These will raise the * numbers somewhat if they are not already used in other program parts. *

* *

* When opening a partition, filesystem, file or directory, a little amount * of dynamic RAM is used, as listed in the following table. Alternatively, * the same amount of static RAM can be used. *

* * * * * * * * * * * * * * * * * * * * * * *
descriptordynamic/static RAM
partition17
filesystem26
file51
directory47
* * \endhtmlonly * * \section adaptation Adapting the software to your needs * The only hardware dependent part is the communication * layer talking to the memory card. The other parts like partition table and FAT16 * support are completely independent, you could use them even for managing * Compact Flash cards or standard ATAPI hard disks. * * By changing the MCU* variables in the Makefile, you can use other Atmel * microcontrollers or different clock speeds. You might also want to change * the configuration defines in the files fat16_config.h, partition_config.h, * sd_raw_config.h and sd-reader_config.h. For example, you could disable * write support completely if you only need read support. * * \section bugs Bugs or comments? * If you have comments or found a bug in the software - there might be some * of them - you may contact me per mail at feedback@roland-riegel.de. * * \section acknowledgements Acknowledgements * Thanks go to Ulrich Radig, who explained on his homepage how to interface * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/). * I adapted his work for my circuit. Although this is a very simple * solution, I had no problems using it. * * \section copyright Copyright 2006-2007 by Roland Riegel * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html). * At your option, you can alternatively redistribute and/or modify the following * files under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html): * - fat16.c * - fat16.h * - fat16_config.h * - partition.c * - partition.h * - partition_config.h * - sd_raw.c * - sd_raw.h * - sd_raw_config.h * - sd-reader_config.h */ static uint8_t print_disk_info(const struct fat16_fs_struct* fs); int main() { /* we will just use ordinary idle mode */ set_sleep_mode(SLEEP_MODE_IDLE); /* setup uart */ uart_init(); while(1) { /* setup sd card slot */ if(!sd_raw_init()) { #if DEBUG uart_puts_p(PSTR("MMC/SD initialization failed\n")); #endif continue; } /* open first partition */ struct partition_struct* partition = partition_open(sd_raw_read, sd_raw_read_interval, sd_raw_write, sd_raw_write_interval, 0 ); if(!partition) { /* If the partition did not open, assume the storage device * is a "superfloppy", i.e. has no MBR. */ partition = partition_open(sd_raw_read, sd_raw_read_interval, sd_raw_write, sd_raw_write_interval, -1 ); if(!partition) { #if DEBUG uart_puts_p(PSTR("opening partition failed\n")); #endif continue; } } /* open file system */ struct fat16_fs_struct* fs = fat16_open(partition); if(!fs) { #if DEBUG uart_puts_p(PSTR("opening filesystem failed\n")); #endif continue; } /* open root directory */ struct fat16_dir_entry_struct directory; fat16_get_dir_entry_of_path(fs, "/", &directory); struct fat16_dir_struct* dd = fat16_open_dir(fs, &directory); if(!dd) { #if DEBUG uart_puts_p(PSTR("opening root directory failed\n")); #endif continue; } /* print some card information as a boot message */ print_disk_info(fs); struct fat16_dir_entry_struct file_entry; uart_puts_p(PSTR("1: ")); if(!fat16_create_file(dd, "1", &file_entry)) uart_puts_p(PSTR("error1\n")); else uart_puts_p(PSTR("success1\n")); uart_puts_p(PSTR("2: ")); if(!fat16_create_file(dd, "2", &file_entry)) uart_puts_p(PSTR("error2\n")); else uart_puts_p(PSTR("success2\n")); uart_puts_p(PSTR("3: ")); if(!fat16_create_file(dd, "3", &file_entry)) uart_puts_p(PSTR("error3\n")); else uart_puts_p(PSTR("success3\n")); uart_puts_p(PSTR("4: ")); if(!fat16_create_file(dd, "4", &file_entry)) uart_puts_p(PSTR("error4\n")); else uart_puts_p(PSTR("success4\n")); uart_puts_p(PSTR("5: ")); if(!fat16_create_file(dd, "5", &file_entry)) uart_puts_p(PSTR("error5\n")); else uart_puts_p(PSTR("success5\n")); uart_puts_p(PSTR("6: ")); if(!fat16_create_file(dd, "6", &file_entry)) uart_puts_p(PSTR("error6\n")); else uart_puts_p(PSTR("success6\n")); uart_puts_p(PSTR("7: ")); if(!fat16_create_file(dd, "7", &file_entry)) uart_puts_p(PSTR("error7\n")); else uart_puts_p(PSTR("success7\n")); uart_puts_p(PSTR("8: ")); if(!fat16_create_file(dd, "8", &file_entry)) uart_puts_p(PSTR("error8\n")); else uart_puts_p(PSTR("success8\n")); uart_puts_p(PSTR("9: ")); if(!fat16_create_file(dd, "9", &file_entry)) uart_puts_p(PSTR("error9\n")); else uart_puts_p(PSTR("success9\n")); if(!sd_raw_sync()) uart_puts_p(PSTR("error syncing disk\n")); /* close file system */ fat16_close(fs); /* close partition */ partition_close(partition); while(1); } } uint8_t print_disk_info(const struct fat16_fs_struct* fs) { if(!fs) return 0; struct sd_raw_info disk_info; if(!sd_raw_get_info(&disk_info)) return 0; uart_puts_p(PSTR("manuf: 0x")); uart_putc_hex(disk_info.manufacturer); uart_putc('\n'); uart_puts_p(PSTR("oem: ")); uart_puts((char*) disk_info.oem); uart_putc('\n'); uart_puts_p(PSTR("prod: ")); uart_puts((char*) disk_info.product); uart_putc('\n'); uart_puts_p(PSTR("rev: ")); uart_putc_hex(disk_info.revision); uart_putc('\n'); uart_puts_p(PSTR("serial: 0x")); uart_putdw_hex(disk_info.serial); uart_putc('\n'); uart_puts_p(PSTR("date: ")); uart_putw_dec(disk_info.manufacturing_month); uart_putc('/'); uart_putw_dec(disk_info.manufacturing_year); uart_putc('\n'); uart_puts_p(PSTR("size: ")); uart_putdw_dec(disk_info.capacity); uart_putc('\n'); uart_puts_p(PSTR("copy: ")); uart_putw_dec(disk_info.flag_copy); uart_putc('\n'); uart_puts_p(PSTR("wr.pr.: ")); uart_putw_dec(disk_info.flag_write_protect_temp); uart_putc('/'); uart_putw_dec(disk_info.flag_write_protect); uart_putc('\n'); uart_puts_p(PSTR("format: ")); uart_putw_dec(disk_info.format); uart_putc('\n'); uart_puts_p(PSTR("free: ")); uart_putdw_dec(fat16_get_fs_free(fs)); uart_putc('/'); uart_putdw_dec(fat16_get_fs_size(fs)); uart_putc('\n'); return 1; } void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec) { *year = 2007; *month = 1; *day = 1; *hour = 0; *min = 0; *sec = 0; }