SD/MMC карта памяти и микроконтроллер AVR (часть 2) Система FAT, Petit FatFs.
В этой заметке рассмотрим работу совместно с файловой системой FAT. Итак, мы имеем микроконтроллер Atmega32 и SD/MMC карту памяти, отформатированную в системе FAT. На карте памяти, в корне есть файлы read.txt и write.txt.
Из read.txt нужно прочитать содержимое и вывести на программу Terminal 1.9 по UART. А в файл write.txt требуется записать некоторые тестовые данные, предположим «write ok».
Про подключение и по блоковую работу карты памяти SD|MMC с микроконтроллером писалось в предидущей статье цикла. Теперь попробуем пописать/почитать карту памяти на уровне файлов. С этим нам поможет библиотека Petit FatFs от мистера ChaN (elm-chan.org). Petit FatFs умеет работать с FAT16, FAT32, и предоставляет высокоуровневые функции работы с файлами. Эта библиотека представляет собой «драйвер» FAT, к которому можно подключить практически любой носитель с файловой системой FAT. Но для этого нужно написать 3 функции взаимодействия с носителем:
1) DSTATUS disk_initialize (void)
Инициализирует носитель, в случае успешной инициализации возвращает 0. В случае, если носитель не инициализировался, возвращает STA_NOINIT, а если носитель отсутствует - STA_NODISK.
typedef BYTE DSTATUS; //Статус носителя #define STA_NOINIT 0x01 //Носитель не инициализирован #define STA_NODISK 0x02 //Носитель отсутствует
2) DRESULT disk_readp (BYTE* Buffer, DWORD SectorNumber, WORD Offset, WORD Count)
Считывает часть сектора в область, куда указывает указатель Buffer. Считывает Count байт, начиная Offset-го байта сектора под номером SectorNumber (номер сектора в LBA адресации). Возвращает следующий результат:
typedef enum { //Статус дисковой операции RES_OK = 0, //Операция успешно завершена RES_ERROR, //Ошибка носителя RES_NOTRDY, //Носитель не готов RES_PARERR //Ошибочный аргумент } DRESULT;
3) DRESULT disk_writep (BYTE* Buffer, DWORD SectorBytes)
Пишет данные, на которые указывает указатель Buffer. Если Buffer=NULL, то это инициализирует/завершает операцию записи в сектор. SectorBytes выполняет двоякую роль, при инициализации указывают номер сектора, а при записи – количество байт, которые нужно записать. Пример работы с функцией disk_writep:
disk_writep(0, sector_number); //Инициализирует операцию записи в сектор sector_number disk_writep(data, byte_to_write); //Пишем данные в сектор, до 512 байт disk_writep(data, byte_to_write); disk_writep(data, byte_to_write); disk_writep(0, 0); // Завершаем операцию записи сектора, // если сектор не был дописан до конца, оставшиеся байты забиваются нулями.
Результат возвращает тот же, что и disk_readp.
На сайте мистера ChaN был приведен пример использования библиотеки Petit FatFs совместно с SD|MMC картой. Этот пример я взял за базу, и немного переделав его, получил удобный инструмент работы с системой FAT. Далее была написана программа-пример, которая демонстрирует чтение из файла read.txt и запись в файл write.txt которые расположены в корне SD|MMC карты памяти. Также было показано сканирования папки на предмет наличия в ней файлов и остальных папок. Рассмотрим поподробнее этот пример.
Скачав исходный код, и открыв его в AVR Studio 4 можно увидеть следующее:
Сам драйвер Petit FatFs находится в файлах pff.c, pff.h. Как видно из кода выше, стандартные типы char, int, long int в Petit FatFs были переопределены. Эти переопределения можно посмотреть в файле integer.h. В файле diskio.h находятся заголовки функций disk_initialize, disk_readp, disk_writep. В mmc.c – все для работы c SD|MMC картой, и сами функции disk_initialize, disk_readp, disk_writep. В оставшемся файле Petit_FAT_FS_sample.c производиться собственно использование Petit FatFS для чтения/записи файлов на карте.
Для подключения карты памяти к микроконтроллеру используется так же макетка и схема, что и в статье «SD/MMC карта памяти и микроконтроллер AVR (часть 1) Базовые операции.» Если же карта подключена к PortB по другому, то стоит подредактировать секцию в mmc.c:
#define SD_DI 0 #define SD_DO 1 #define SD_CLK 2 #define SD_CS 3 #define SD_INS 4 #define SD_WP 5
Теперь поподробнее рассмотрим инструментарий:
1) FRESULT pf_mount (FATFS* FileSystemObject)
Монтирует файловую систему FAT, где указатель FileSystemObject указывает на объект файловой системы. Возвращает FRESULT:
typedef enum { FR_OK = 0, //Все хорошо FR_DISK_ERR, //Ошибка диска FR_NOT_READY, //Диск не готов FR_NO_FILE, //Нет файла FR_NO_PATH, //Неправильный путь FR_NOT_OPENED, //Файл не открыт FR_NOT_ENABLED, //Раздел не смонтирован FR_NO_FILESYSTEM //На диске не имеется файловой системы FAT } FRESULT;
А точнее, возвращает только FR_OK, FR_NOT_READY, FR_DISK_ERR, FR_NO_FILESYSTEM
2) FRESULT pf_open (const char* FileName)
Открывает файл для подальших операций чтения/записи. Указатель FileName указывает на строку – путь к файлу. Возвращает FR_OK, FR_NO_FILE, FR_NO_PATH, FR_DISK_ERR, FR_NOT_ENABLED.
3) FRESULT pf_read (void* Buffer, WORD ByteToRead, WORD* BytesRead)
Считывает ByteToRead байт в область, на которую указывает Buffer из открытого файла. BytesRead – количество считанных байт, если BytesRead< ByteToRead, то был достигнут конец файла. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED, FR_NOT_ENABLED.
4) FRESULT pf_write (void* Buffer, WORD ByteToWrite, WORD* BytesWritten)
Пишет ByteToWrite байтов из буфера, на который указывает Buffer в открытый файл. BytesWritten – указатель на число записанных байт. Записанные в файл данные следует финализровать. Вот так:
pf_open (path); //Открываем файл pf_write(buff, btw, &bw); //Пишем pf_write(buff, btw, &bw); //Еще пишеи pf_write(0, 0, &bw); //Финализируем
Эта функция имеет существенные ограничения. Нельзя с ее помощью создать файл, нельзя увеличить размер файла (в секторах), может стартовать только с начала сектора, не учитывает файловый атрибует read-only. Если *BytesWritten < ByteToWrite, то это означает что был достигнут конец файла и дальше писать данные невозможно. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED, FR_NOT_ENABLED.
5) FRESULT pf_lseek (DWORD Offset)
Устанавливает курсор для операции чтения внутри открытого файла в позицию Offset. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED
6) FRESULT pf_opendir (DIR* DirObject, const char* DirName)
Открывает директорию c путем на который указывает DirName в объект, на который указывает DirObject. Структура DirObject может быть в любое время уничтожена. Возвращает FR_OK, FR_NO_PATH, FR_NOT_READY, FR_DISK_ERR, FR_NOT_ENABLED.
7) FRESULT pf_readdir (DIR* DirObject, FILINFO* FileInfo)
Считывает содержимое директории по указателю DirObject в структуру по указателю FileInfo. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED.
Имея описанный выше инструментарий, мы можем почитать/пописать данные на карту SD|MMC на уровне файлов:
#include <avr/io.h> #include <avr/interrupt.h> #include <string.h> #include "pff.h" #include "diskio.h" #include "integer.h" //------------------------------------------------------------------ char read_buf[128]={}; char write_buf[128]={'w','r','i','t','e',' ','o','k','\r','\n',0x00}; //------------------------------------------------------------------ void uart_init(void) { UBRRH = 0x00; //Установим скорость UART 256000 bit/sec UBRRL = 0x01; UCSRA = 0x00; UCSRB = (1<<RXEN)|(1<<TXEN); //Разрешаем передачу и прием данных UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); } void uart_transmit(char data ) //Передаем байт по UART { while ( !( UCSRA & (1<<UDRE)) ) ; UDR = data; } void uart_transmit_message(char* msg) //Передаем сообщение по UART { unsigned char i; i=0; while ((i<256)&(msg[i]!=0x00) ) { uart_transmit(msg[i]); i++; } } //------------------------------------------------------------------ FRESULT scan_files (char* path) //Фунция сканирования директории { FRESULT res; FILINFO fno; DIR dir; res = pf_opendir(&dir, path); //Открываем директорию if (res == FR_OK) { for (;;) { res = pf_readdir(&dir, &fno); //Считываем директорию if (res != FR_OK || fno.fname[0] == 0) break; if (fno.fattrib & AM_DIR) { //Если объект - директория uart_transmit_message("DIR: "); uart_transmit_message(fno.fname); uart_transmit_message("\r\n"); } else { //Если объект - файл uart_transmit_message("FILE: "); uart_transmit_message(fno.fname); uart_transmit_message("\r\n"); } } } return res; } //------------------------------------------------------------------ int main () { FATFS fs; //FatFs объект FRESULT res; //Результат выполнения WORD s1; uart_init(); //Инициализация UART res=pf_mount(&fs); //Монтируем FAT uart_transmit_message("mounting FAT "); if (res==0x00) { //Если FAT успешно смонтирован uart_transmit_message("OK\r\n"); res=pf_open("/read.txt"); //Откроем read txt uart_transmit_message("open read.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); res=pf_lseek (0); //Установим курсор чтения на 0 в read.txt uart_transmit_message("set 0 to pointer into read.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); res = pf_read(read_buf, 128, &s1);//Считаем первые 128 байт из read.txt uart_transmit_message("read first 128 bytes from read.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); uart_transmit_message("data in read.txt:\r\n"); uart_transmit_message(read_buf);//Передадим первые 128 байт с read.txt на компьютер res=pf_open("/write.txt"); uart_transmit_message("open write.txt ");//Откроем write.txt if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); res=pf_write(write_buf,strlen(write_buf),&s1);//Запишем в write.txt uart_transmit_message("writing @write ok@ in write.txt "); if ((res==0x00)&&(s1==strlen(write_buf))) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); res=pf_write(0,0,&s1); //Финализируем write.txt uart_transmit_message("finalizing write.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); res=pf_lseek (0); //Установим курсор чтения на начало write.txt uart_transmit_message("set 0 to pointer into write.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); read_buf[0]=0; //Чистим буфер res = pf_read(read_buf, 128, &s1); //Считываем первые 128 байт из write.txt uart_transmit_message("read first 128 bytes from write.txt "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); uart_transmit_message("data in write.txt:\r\n"); uart_transmit_message(read_buf); //Передаем считанные данные на компьютер uart_transmit_message("scaning test_dir:\r\n"); scan_files ("test_dir"); //Сканируем test_dir uart_transmit_message("scaning ended\r\n"); res=pf_mount(0x00); //Демонтируемt FAT uart_transmit_message("unmounting FAT "); if (res==0x00) uart_transmit_message("OK\r\n"); else uart_transmit_message("FAIL\r\n"); } else uart_transmit_message("FAIL\r\n"); while (1); }
Код программы, монтируем FAT, считываем данные с read.txt, пишем данные в write.txt, считывает то, что записали. Далее сканируем и выводим содержимое папки test_dir, после чего размонтируем FAT. Вот что я увидел в окошке Terminal 1.9, настроив его на скорость 256000 бит/с и запустив макет с Petit FatFs:
Если у вас не будет производиться операции записи - проверьте защелку Write Protect на карточке. Ее положение учитывается. Для подтверждения сказанного откроем содержимое карты в проводнике:
Как видно из скриншота, все работает. Сделаем небольшие выводы по Petit FatFs. Главные преимущества – небольшой размер (можно урезать до размера менее 8кб флеша), простота использования, высокоуровневый доступ к файлам. Из минусов отмечу невозможность создавать и значительно расширять существующие файлы. Так что Petit FatFs хорошо сгодиться там, где нужно уметь читать данные (всякие плееры WAV файлов), хороший логгер с использованием Petit FatFs, сделать не получиться. Про создание, удаление и запись больших файлов поговорим в следующей статье цикла.
UDP: в Petit FatFs также есть константы, управляя которыми можно выбросить неиспользуемые функции и тем самым сократив требования по ОЗУ и флэш-памяти для микроконтроллера. Эти константы можно задать в файле pff.h. Рассмотрим их поподробнее:
Скачать исходный код в виде проекта под AVR Studio 4.
Следующая статья цикла: «SD/MMC карта памяти и микроконтроллер AVR (часть 3) Система FAT, FatFs.»
Предведущая статья цикла: «SD/MMC карта памяти и микроконтроллер AVR (часть 1) Базовые операции.»
Попробовал вашу реализацию
Попробовал вашу реализацию библиотеки от Чанга - работает. Но если файл для чтения больше чем одна строка, то читаются только первых 128 байт файла. Так понимаю, что перед:
нужно поставить условие "Если не достигнут конец файла", но как это написать незнаю. Прошу помоши.
---
Вроде вычислил, вот код:
Не проходит инициализация
Доброго времени суток, не получается осуществить инициализацию карточки. Посылаю CMD0, в конце send_cmd ожидается ответ 0x01, на самом деле получаю только 0x00 (а поскольку в INIT_SPI() происходит инвертизация SD_DO, то в реальности на МК 0xff).
Подскажите пожалуйста, скачал
Подскажите пожалуйста, скачал проект и не запускается. Не проходит инициализация карты, если каждую команду прокручивать по 2...3 раза то срабатывает(for)и запись в файл ни в какую не производится. Симулирую в протеусе, можно ли узнать причины этого?
В протеусе код не ганял. Все
В протеусе код не ганял. Все сразу в железе собирал, с карточкой Kingston в 2 Гб все работало.
А что случилось с 3-й частью?
А что случилось с 3-й частью?
Пока не работает. Причины
Пока не работает.
Причины устанавливаем.
Починил
Починил
Почемуто не открывается 3
Почемуто не открывается 3 часть статьи по работе с SD картами
Жду с нетерпением следующей
Жду с нетерпением следующей части, где описано, как создать большой файл для логгера!
Будет, все будет. После того
Будет, все будет. После того как только автор сдаст диплом :)
Добрый вечер у меня такая
Добрый вечер у меня такая проблема собрал даное устройство в железе все работает только в терминале не хочет писать в файл write.txt write ok зашелку проверял все нормально карты памяти пробовал другую такая же проблема Испульзую вместо Атмеги32 Атмегу16 Помогите в чем проблема?
Посмотри в эту сторону
у меня была таже проблема.
Возможно проблема в следующем:
В данной реализации урезаной библиотеки она не может создавать новые кластера, а только перезаписывать уже существующие. Если создан файл, но в нем ничего не записано, то записать в него ничего не получиться. Попробуй в файл записать большое количество пробелов.
ок спасибо попробую
ок спасибо попробую