3 * Copyright (c) 2006-2012 by Roland Riegel <feedback@roland-riegel.de>
5 * This file is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
11 #include <avr/pgmspace.h>
12 #include <avr/sleep.h>
14 #include "fat_config.h"
15 #include "partition.h"
17 #include "sd_raw_config.h"
21 #include <aversive/error.h>
28 * \mainpage MMC/SD/SDHC card library
30 * This project provides a general purpose library which implements read and write
31 * support for MMC, SD and SDHC memory cards.
34 * - low-level \link sd_raw MMC, SD and SDHC read/write routines \endlink
35 * - \link partition partition table support \endlink
36 * - a simple \link fat FAT16/FAT32 read/write implementation \endlink
38 * \section circuit The circuit
39 * The circuit which was mainly used during development consists of an Atmel AVR
40 * microcontroller with some passive components. It is quite simple and provides
41 * an easy test environment. The circuit which can be downloaded on the
42 * <a href="http://www.roland-riegel.de/sd-reader/">project homepage</a> has been
43 * improved with regard to operation stability.
45 * I used different microcontrollers during development, the ATmega8 with 8kBytes
46 * of flash, and its pin-compatible alternative, the ATmega168 with 16kBytes flash.
47 * The first one is the one I started with, but when I implemented FAT16 write
48 * support, I ran out of flash space and switched to the ATmega168. For FAT32, an
49 * ATmega328 is required.
51 * The circuit board is a self-made and self-soldered board consisting of a single
52 * copper layer and standard DIL components, except of the MMC/SD card connector.
54 * The connector is soldered to the bottom side of the board. It has a simple
55 * eject button which, when a card is inserted, needs some space beyond the connector
56 * itself. As an additional feature the connector has two electrical switches
57 * to detect wether a card is inserted and wether this card is write-protected.
59 * \section pictures Pictures
60 * \image html pic01.jpg "The circuit board used to implement and test this application."
61 * \image html pic02.jpg "The MMC/SD card connector on the soldering side of the circuit board."
63 * \section software The software
64 * The software is written in C (ISO C99). It might not be the smallest or
65 * the fastest one, but I think it is quite flexible. See the project's
66 * <a href="http://www.roland-riegel.de/sd-reader/benchmarks/">benchmark page</a> to get an
67 * idea of the possible data rates.
69 * I implemented an example application providing a simple command prompt which is accessible
70 * via the UART at 9600 Baud. With commands similiar to the Unix shell you can browse different
71 * directories, read and write files, create new ones and delete them again. Not all commands are
72 * available in all software configurations.
73 * - <tt>cat \<file\></tt>\n
74 * Writes a hexdump of \<file\> to the terminal.
75 * - <tt>cd \<directory\></tt>\n
76 * Changes current working directory to \<directory\>.
78 * Shows card manufacturer, status, filesystem capacity and free storage space.
80 * Reinitializes and reopens the memory card.
82 * Shows the content of the current directory.
83 * - <tt>mkdir \<directory\></tt>\n
84 * Creates a directory called \<directory\>.
85 * - <tt>mv \<file\> \<file_new\></tt>\n
86 * Renames \<file\> to \<file_new\>.
87 * - <tt>rm \<file\></tt>\n
90 * Ensures all buffered data is written to the card.
91 * - <tt>touch \<file\></tt>\n
93 * - <tt>write \<file\> \<offset\></tt>\n
94 * Writes text to \<file\>, starting from \<offset\>. The text is read
95 * from the UART, line by line. Finish with an empty line.
99 * The following table shows some typical code sizes in bytes, using the 20090330 release with a
100 * buffered read-write MMC/SD configuration, FAT16 and static memory allocation:
103 * <table border="1" cellpadding="2">
107 * <th>static RAM usage</th>
111 * <td align="right">2410</td>
112 * <td align="right">518</td>
116 * <td align="right">456</td>
117 * <td align="right">17</td>
121 * <td align="right">7928</td>
122 * <td align="right">188</td>
127 * The static RAM is mostly used for buffering memory card access, which
128 * improves performance and reduces implementation complexity.
132 * Please note that the numbers above do not include the C library functions
133 * used, e.g. some string functions. These will raise the numbers somewhat
134 * if they are not already used in other program parts.
138 * When opening a partition, filesystem, file or directory, a little amount
139 * of RAM is used, as listed in the following table. Depending on the library
140 * configuration, the memory is either allocated statically or dynamically.
143 * <table border="1" cellpadding="2">
145 * <th>descriptor</th>
146 * <th>dynamic/static RAM</th>
150 * <td align="right">17</td>
153 * <td>filesystem</td>
154 * <td align="right">26</td>
158 * <td align="right">53</td>
162 * <td align="right">49</td>
168 * \section adaptation Adapting the software to your needs
169 * The only hardware dependent part is the communication layer talking to the
170 * memory card. The other parts like partition table and FAT support are
171 * completely independent, you could use them even for managing Compact Flash
172 * cards or standard ATAPI hard disks.
174 * By changing the MCU* variables in the Makefile, you can use other Atmel
175 * microcontrollers or different clock speeds. You might also want to change
176 * the configuration defines in the files fat_config.h, partition_config.h,
177 * sd_raw_config.h and sd-reader_config.h. For example, you could disable
178 * write support completely if you only need read support.
180 * For further information, visit the project's
181 * <a href="http://www.roland-riegel.de/sd-reader/faq/">FAQ page</a>.
183 * \section bugs Bugs or comments?
184 * If you have comments or found a bug in the software - there might be some
185 * of them - you may contact me per mail at feedback@roland-riegel.de.
187 * \section acknowledgements Acknowledgements
188 * Thanks go to Ulrich Radig, who explained on his homepage how to interface
189 * MMC cards to the Atmel microcontroller (http://www.ulrichradig.de/).
190 * I adapted his work for my circuit.
192 * \section copyright Copyright 2006-2012 by Roland Riegel
193 * This program is free software; you can redistribute it and/or modify it under
194 * the terms of the GNU General Public License version 2 as published by
195 * the Free Software Foundation (http://www.gnu.org/copyleft/gpl.html).
196 * At your option, you can alternatively redistribute and/or modify the following
197 * files under the terms of the GNU Lesser General Public License version 2.1
198 * as published by the Free Software Foundation (http://www.gnu.org/copyleft/lgpl.html):
206 * - partition_config.h
210 * - sd-reader_config.h
213 static uint8_t read_line(char* buffer, uint8_t buffer_length);
214 static uint32_t strtolong(const char* str);
215 static uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry);
216 static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name);
217 static uint8_t print_disk_info(const struct fat_fs_struct* fs);
221 /* we will just use ordinary idle mode */
222 set_sleep_mode(SLEEP_MODE_IDLE);
226 printf_P(PSTR("init\n"));
227 /* setup sd card slot */
231 printf_P(PSTR("MMC/SD initialization failed\n"));
236 /* open first partition */
237 struct partition_struct* partition = partition_open(sd_raw_read,
238 sd_raw_read_interval,
239 #if SD_RAW_WRITE_SUPPORT
241 sd_raw_write_interval,
251 /* If the partition did not open, assume the storage device
252 * is a "superfloppy", i.e. has no MBR.
254 partition = partition_open(sd_raw_read,
255 sd_raw_read_interval,
256 #if SD_RAW_WRITE_SUPPORT
258 sd_raw_write_interval,
268 printf_P(PSTR("opening partition failed\n"));
274 /* open file system */
275 struct fat_fs_struct* fs = fat_open(partition);
279 printf_P(PSTR("opening filesystem failed\n"));
284 /* open root directory */
285 struct fat_dir_entry_struct directory;
286 fat_get_dir_entry_of_path(fs, "/", &directory);
288 struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
292 printf_P(PSTR("opening root directory failed\n"));
297 /* print some card information as a boot message */
300 /* provide a simple shell */
305 printf_P(PSTR("> "));
308 char* command = buffer;
309 if(read_line(command, sizeof(buffer)) < 1)
312 /* execute command */
313 if(strcmp_P(command, PSTR("init")) == 0)
317 else if(strncmp_P(command, PSTR("cd "), 3) == 0)
320 if(command[0] == '\0')
323 /* change directory */
324 struct fat_dir_entry_struct subdir_entry;
325 if(find_file_in_dir(fs, dd, command, &subdir_entry))
327 struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry);
336 printf_P(PSTR("directory not found: %s\n"), command);
338 else if(strcmp_P(command, PSTR("ls")) == 0)
340 /* print directory listing */
341 struct fat_dir_entry_struct dir_entry;
342 while(fat_read_dir(dd, &dir_entry))
344 uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4;
346 printf_P(PSTR("%s%c"), dir_entry.long_name,
347 dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
349 uart_send(CMDLINE_UART, ' ');
350 printf_P(PSTR("%d\n"), dir_entry.file_size);
353 else if(strncmp_P(command, PSTR("cat "), 4) == 0)
356 if(command[0] == '\0')
359 /* search file in current directory and open it */
360 struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
363 printf_P(PSTR("error opening: %s\n"), command);
367 /* print file contents */
371 while((count = fat_read_file(fd, buffer, sizeof(buffer))) > 0)
373 printf_P(PSTR("%"PRIu32":"), offset);
374 for(intptr_t i = 0; i < count; ++i)
376 printf_P(PSTR(" %x"), buffer[i]);
378 printf_P(PSTR("\n"));
384 else if(strcmp_P(command, PSTR("disk")) == 0)
386 if(!print_disk_info(fs))
387 printf_P(PSTR("error reading disk info\n"));
389 #if FAT_WRITE_SUPPORT
390 else if(strncmp_P(command, PSTR("rm "), 3) == 0)
393 if(command[0] == '\0')
396 struct fat_dir_entry_struct file_entry;
397 if(find_file_in_dir(fs, dd, command, &file_entry))
399 if(fat_delete_file(fs, &file_entry))
403 printf_P(PSTR("error deleting file: %s\n"), command);
405 else if(strncmp_P(command, PSTR("touch "), 6) == 0)
408 if(command[0] == '\0')
411 struct fat_dir_entry_struct file_entry;
412 if(!fat_create_file(dd, command, &file_entry))
414 printf_P(PSTR("error creating file: %s\n"), command);
417 else if(strncmp_P(command, PSTR("mv "), 3) == 0)
420 if(command[0] == '\0')
423 char* target = command;
424 while(*target != ' ' && *target != '\0')
432 struct fat_dir_entry_struct file_entry;
433 if(find_file_in_dir(fs, dd, command, &file_entry))
435 if(fat_move_file(fs, &file_entry, dd, target))
439 printf_P(PSTR("error moving file: %s\n"), command);
441 else if(strncmp_P(command, PSTR("write "), 6) == 0)
444 if(command[0] == '\0')
447 char* offset_value = command;
448 while(*offset_value != ' ' && *offset_value != '\0')
451 if(*offset_value == ' ')
452 *offset_value++ = '\0';
456 /* search file in current directory and open it */
457 struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
460 printf_P(PSTR("error opening %s\n"), command);
464 int32_t offset = strtolong(offset_value);
465 if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
467 printf_P(PSTR("error seeking on %s\n"), command);
473 /* read text from the shell and write it to the file */
477 /* give a different prompt */
478 printf_P(PSTR("< "));
480 /* read one line of text */
481 data_len = read_line(buffer, sizeof(buffer));
485 /* write text to file */
486 if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
488 printf_P(PSTR("error writing to file\n"));
495 else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
498 if(command[0] == '\0')
501 struct fat_dir_entry_struct dir_entry;
502 if(!fat_create_dir(dd, command, &dir_entry))
504 printf_P(PSTR("error creating directory: %s\n"), command);
508 #if SD_RAW_WRITE_BUFFERING
509 else if(strcmp_P(command, PSTR("sync")) == 0)
512 printf_P(PSTR("error syncing disk\n"));
517 printf_P(PSTR("unknown command: %s\n"), command);
521 /* close directory */
524 /* close file system */
527 /* close partition */
528 partition_close(partition);
534 uint8_t uart_getc(void)
536 uint8_t b = uart_recv(CMDLINE_UART);
543 uint8_t read_line(char* buffer, uint8_t buffer_length)
545 memset(buffer, 0, buffer_length);
547 uint8_t read_length = 0;
548 while(read_length < buffer_length - 1)
550 uint8_t c = uart_getc();
552 if(c == 0x08 || c == 0x7f)
558 buffer[read_length] = '\0';
560 uart_send(CMDLINE_UART, 0x08);
561 uart_send(CMDLINE_UART, ' ');
562 uart_send(CMDLINE_UART, 0x08);
567 uart_send(CMDLINE_UART, c);
571 buffer[read_length] = '\0';
576 buffer[read_length] = c;
584 uint32_t strtolong(const char* str)
587 while(*str >= '0' && *str <= '9')
588 l = l * 10 + (*str++ - '0');
593 uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
597 while(fat_read_dir(dd, dir_entry))
599 if(strcmp(dir_entry->long_name, name) == 0)
609 struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name)
611 struct fat_dir_entry_struct file_entry;
612 if(!find_file_in_dir(fs, dd, name, &file_entry))
615 return fat_open_file(fs, &file_entry);
618 uint8_t print_disk_info(const struct fat_fs_struct* fs)
623 struct sd_raw_info disk_info;
624 if(!sd_raw_get_info(&disk_info))
627 printf_P(PSTR("manuf: 0x%x\n"), disk_info.manufacturer);
628 printf_P(PSTR("oem: %s\n"), disk_info.oem);
629 printf_P(PSTR("prod: %s\n"), disk_info.product);
630 printf_P(PSTR("rev: 0x%x\n"), disk_info.revision);
631 printf_P(PSTR("serial: 0x%x\n"), disk_info.serial);
632 printf_P(PSTR("date: 0x%d 0x%d\n"), disk_info.manufacturing_month,
633 disk_info.manufacturing_year);
634 printf_P(PSTR("size: %"PRIu32"\n"), disk_info.capacity / 1024 / 1024);
635 printf_P(PSTR("copy: 0x%x\n"), disk_info.flag_copy);
636 printf_P(PSTR("wr.pr.: 0x%x 0x%x\n"), disk_info.flag_write_protect_temp,
637 disk_info.flag_write_protect);
638 printf_P(PSTR("format: %d\n"), disk_info.format);
639 printf_P(PSTR("free: %d %d\n"), fat_get_fs_free(fs), fat_get_fs_size(fs));
644 #if FAT_DATETIME_SUPPORT
645 void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)