AVR114: использование файловой системы Atmel |
![]() |
Добавил(а) microsin |
Здесь приведен перевод апноута AVR114: Using the ATMEL File System management for AT32UC3x, AT90USBx and ATmega32U4 [1], посвященного файловой системе Atmel для микроконтроллеров.
Прим. переводчика: готовый проект с исходным кодом для микроконтроллера ATmega32U4 можно скачать по ссылкам в статье [6], это архив avr115.zip. Также см. библиотеку LUFA, где есть проект TempDataLogger с похожим функционалом (логгер температуры в файл). [2 Особенности и ограничения ATMEL File System] 2.1 Основные возможности • Позволяет смонтировать (Mount) диски, форматированные FAT12, FAT16, FAT32. 2.2 Возможности, расширяемые плагинами • Чтение текстовых файлов (кодировки ASCII, UTF16, UTF16BE, UTF8). 2.3 Ограничения в использовании, которые накладываются стандартом FAT12/FAT16/FAT32 • Поддерживаемый размер диска до 2 терабайт. *Примечание: один файл может использовать больше одной записи файла, в зависимости от длины имени этого файла. 2.4 Ограничения в использовании, которые накладываются реализацией ATMEL File System • При старте не проверяется целостность системы FAT (отсутствует FAT integrity check). Примечание: по стандарту Microsoft имеется 2 копии таблицы FAT, для надежности. Сама Microsoft очень рекомендует использовать вторую копию таблицы FAT. [3 Обзор ATMEL File System] 3.1 Общие замечания Atmel FileSystem использует в качестве основы понятие Navigator handle. Navigator handle позволяет просмотреть одну директорию (каталог) или открыть один файл. Система поддерживает несколько handle, чтобы обеспечить одновременны просмотр нескольких папок или открытие нескольких файлов одновременно. Navigator handle очень мал по размеру и требует только 40 байт SRAM. Рис. 3-1. Организация модуля Atmel FileSystem. 3.2 Навигация (Navigation) Доступ к файлу по строковому пути возможен, но navigation handle позволяет получить доступ к списку файлов. Навигация показывает содержимое директории, которое называется списком файлов (file list), и имеет простые команды типа следующий/предыдущий (next/previous), которые используются для перемещения индекса по списку. Список файлов может включать в себя директории и файлы. Когда открывается выбранный по индексу файл, то навигация замораживается (locked). См. рис. 4.2. Navigation handle (или идентификатор навигации, navigation ID) содержит в себе следующую информацию: • Номер диска (disk number). Рис. 3-2. Организация навигации. Когда диск только что подмонтирован (disk mounted), то папкой по умолчанию (default directory) является корневой каталог (root directory), это папка самого верхнего уровня диска. Вы можете переместить значение индекса на подпапку и выбрать её (команда "cd folder") в качестве новой директории для просмотра. Также Вы можете выбрать поддиректорию (команда "cd .."), когда новая директория была просмотрена. [4 Архитектура ATMEL File System] 4.1 Ядро (Core) На рисунке 4-1 показана архитектура ядра ATMEL File System. Рис. 4-1. Архитектура ядра. 4.1.1 FAT Модуль FAT является модулем низкого уровня, и он декодирует структуру FAT. Все подпрограммы модуля являются приватными (static), и они не могут быть вызваны пользователем. 4.1.2 Навигация (Navigation) Этот модуль предоставляет подпрограммы: • для выбора navigator handle Все подпрограммы описаны в заголовочном файле navigation.h. 4.1.3 Доступ к файлу (File) Этот модуль предоставляет управление файловым вводом/выводом (file I/O control): • Передача одного байта, file_getc() - file_putc(), самая медленная работа с файлом. *Примечание: Direct transfer разработана для передачи данных файла напрямую между двумя областями памяти, без необходимости копирования данных в буфер RAM (например DMA между двумя областями памяти). Все подпрограммы описаны в заголовочном файле file.h. 4.1.4 Память, носители данных (Memory) Интерфейс с памятью следующий: • mem_test_unit_ready(), для проверки состояния памяти. Все подпрограммы описаны в заголовочном файле ctrl_access.h. Прим. переводчика: интерфейс с памятью отделяет слой носителя данных от файловой системы. Благодаря этому носитель данных может быть абсолютно любым. Это может быть как флешка SD, подключенная через SPI, параллельная статическая память, динамическая память DDR, floppy-диск, жесткий диск и т. д. Немного (не слишком сильно) утрируя, это может быть хоть радиомодем, перфолента или даже почтовые голуби. Единственное условие - должны быть реализованы эти 4 подпрограммы, которые дают доступ к линейному адресному пространству памяти носителя. 4.1.5 Обработка ошибок (Error control) Многие подпрограммы возвращают TRUE или FALSE. В случае возврата FALSE глобальная переменная fs_g_status содержит идентификатор ошибки, чтобы можно было получить больше информации об ошибке. Список ошибок доступен в файле fs_com.h. 4.2 Plug-In (подключаемый модуль расширения, плагин) Рис. 4-2. Архитектура плагинов. 4.2.1 Text reader (чтение текстовых файлов) Этот плагин позволяет открыть обычный текстовый файл в режиме только для чтения (read only). Поддерживаемые текстовые форматы ASCII, UTF16, UTF16BE и UTF8. Поддерживается множественное открытие текстового файла. 4.2.2 Play list (плейлист, список проигрывания) Этот плагин поддерживает файловый плейлист в режиме только для чтения (read only). Поддерживаемые расширения *.m3u, *.m3u8, *.pls и *.smp. Имеется ограничение на размер плейлиста - 65535 файлов. Множественное открытие не поддерживается. 4.2.3 Automatic Navigation Автоматическая навигация была разработана для модуля плеера/просмотрщика. Плагин строит список файлов "file list" со следующими параметрами, управляемыми пользователем: • Фильтр по расширению файла (Extension filter). 4.2.4 Режим фильтрации (Filtered mode) Этот плагин фильтрует список файлов "file list" по расширению файла от navigation module. Пример: // Архитектура диска folder1 | folder3 | | file4.mp3 | file5.txt folder2 | file6.txt file1.mp3 file2.txt file3.mp3 4.2.5 Flat mode (режим сквозного, "плоского" просмотра) FLAT mode игнорирует уровни папки и предоставляет список файлов со всеми файлами/папками, которые имеются в выбранной папке или подпапке. Пример: // Архитектура диска folder1 | folder3 | | file4 | file5 folder2 | file6 file1 file2 file3 4.2.6 Filtered & Flat mode Этот плагин включает в себя возможности фильтрации и плоского режима. 4.3 Интерфейс POSIX Аббревиатура POSIX расшифровывается как Portable Operating System Interface (портируемый интерфейс с операционной системой), этот интерфейс совместим с операционной системой Unix. Этот интерфейс доступен только для чипов семейств AVR32 и UC3. Рис. 4-3. Архитектура POSIX. [5 Конфигурирование ATMEL File System] 5.1 Ядро В зависимости от Вашего приложения, Вы можете оптимизировать размер ядра, если сконфигурируете следующие параметры: • Разрешение/запрет поддерживаемых разновидностей FAT (FAT12/FAT16/FAT32). *Примечание: WRITE_COMPLET включает возможность WRITE (записи) и некоторые дополнительные. Конфигурация задается в файле conf_explorer.h: // Этот подключенный заголовочный файл предоставляет подпрограммы для // работы с памятью (например memset(), memcpy_ram2ram(), ...). #define LIB_MEM // Этот подключенный заголовочный файл предоставляет подпрограммы для // доступа к диску (например ram_2_memory(), mem_wr_protect(), ...). #define LIB_CTRLACCESS "ctrl_access.h" 5.2 Конфигурация плагинов Конфигурации Plug-In задается в файле conf_explorer.h. 5.2.1 Play list (список проигрывания) // Навигатор, используемый для открытия файла плейлиста #define FS_NAV_ID_PLAYLIST 2 // Интерфейс плейлиста, чтобы выделить место для хранения текущего пути, // включаемого в плейлист. В этом примере используется библиотека alloc. #define PLAYLIST_BUF_ALLOC( size ) malloc( size ) #define PLAYLIST_BUF_FREE( buf ) free( buf ) // Пример без использования библиотеки alloc. #define PLAYLIST_BUF_ALLOC( size ) ((sizeof(g_buffer_512) > 512)? NULL : g_buffer_512) #define PLAYLIST_BUF_FREE( buf ) 5.2.2 Automatic Navigation (автонавигация). // Фича "учитывать все доступные файлы" может быть запрещена, чтобы // уменьшить время запуска. #define FS_NAV_AUTOMATIC_NBFILE ENABLE // Задает порядок следования файлов в списке, предоставленном плагинов // автонавигации (nav_automatic.c). #define NAV_AUTO_FILE_IN_FIRST // Раскомментируйте, чтобы запретить фичу // Размер для генерации случайного выбора файла (в единицах 8 файлов) #define NAVAUTO_MAX_RANGE_RAND 8 // 8*8 = 64 файла // Метод вычисления случайного числа (значение байта) #include "rand.h" #define NAVAUTO_GET_RAND( value ) (value=rand()) 5.2.3 Navigation Flat (плоская, сквозная навигация по файловой системе) // Разрешает модули режима FLAT (nav_flat.c & navfilterflat.c) #define NAVIGATION_MODE_FLAT // Раскомментируйте, чтобы запретить фичу [6 Примеры использования ATMEL File System] Примечание: все примеры подразумевают, что файловая система Atmel инициализирована так, как показано в примере "Последовательность включения/выключения (Power ON/OFF)". 6.1 Последовательность включения/выключения (Power ON/OFF) Инициализация модуля файловой системы: nav_reset(); Это нужно выполнить перед остановкой модуля файловой системы: // Если у Вас были открыты файлы, то закройте их. // Сброс данных (flush), которые могли остаться в кэше FAT, на диск. nav_exit(); Внимание: не подразумевается, что можно иметь две файловые системы одновременно на одном и том же диске. Если Ваша система использует диски, общие с файловой системой Atmel File System и другой файловой системой (например EFLS или PetitFS), то Вы должны остановить Atmel File System перед тем, как перейти к использованию другой файловой системой, и заново переинициализировать Atmel File System после того, как вышли из другой файловой системы. Таким образом, чтобы избежать ошибок, нельзя использовать две разные файловые системы одновременно. Пример: если микроконтроллер Atmel работает в режиме устройства хранения данных (Device MassStorage) USB, то Atmel File System должна быть выключена, чтобы позволить работать файловой системе хоста USB (например, работающего под управлением операционной системы Windows®) с устройством MassStorage. 6.2 Проверка состояния диска Для проверки диска нужны две подпрограммы: • nav_drive_set() позволяет детектировать присутствие драйвера памяти (обслуживающий носитель данных). Примечание: количество драйверов памяти может быть динамически меняющимся (например, U-Disk может иметь несколько носителей данных). Bool check_disk( U8 lun ) { nav_select(0); // Выбор navigator ID if( !nav_drive_set(lun) ) { //Носитель данных отсутствует printf("Driver memory no available\n"); return FALSE; } // В этом месте память для хранения выбрана if( !nav_partition_mount() ) { switch( fs_g_status ) { case FS_ERR_HW_NO_PRESENT: //Диск отсутствует printf("Disk not present\n"); break; case FS_ERR_HW: //Ошибка доступа к диску printf("Disk access error\n"); break; case FS_ERR_NO_FORMAT: //Диск не форматирован printf("Disk no formated\n"); break; case FS_ERR_NO_PART: //На диске нет созданных доступных разделов printf("No partition available on disk\n"); break; case FS_ERR_NO_SUPPORT_PART: //Этот тип раздела не поддерживается printf("Partition no supported\n"); break; default: //Другая ошибка системы printf("Other system error\n"); break; } return FALSE; } // В этом месте память раздела успешно смонтирована, // и навигатор находится в корневой директории (root dir). return TRUE; } 6.3 Изменение даты/времени файла В этом примере изменяется дата создания файла и дата последнего изменения. Bool changedate( void ) { // Пример модификации даты файла // Date = 12/25/2005 12h30mn10.5s const U8 _MEM_TYPE_SLOW_ date_create[]="2005122512301050"; // Date = 07/23/2006 19h51mn30s const U8 _MEM_TYPE_SLOW_ date_write[]="2006072319005130"; nav_select(0); // Для этой последовательности выбран navigator ID 0 if( !nav_drive_set(LUN_ID_NF_DISKMASS)) return FALSE; if( !nav_partition_mount() ) return FALSE; // Выбор первого файла или директории if( !nav_filelist_set(0, FS_FIND_NEXT) ) return FALSE; // Модификация даты создания if( !nav_file_dateset(date_create, FS_DATE_CREATION) ) return FALSE; // Модификация даты последнего доступа if( !nav_file_dateset(date_write, FS_DATE_LAST_WRITE) ) return FALSE; return TRUE; } 6.4 Использование строкового пути (path string) Вы можете использовать текстовый путь (в формате ASCII или UNICODE), чтобы получить доступ к нужному файлу или пути. Подпрограмма nav_setcwd() принимает путь в виде строки по следующим правилам (примеры): • "name" для доступа к файлу или каталогу в пределах текущего каталога Bool search_path( void ) { const _MEM_TYPE_SLOW_ U8 path[]="dir1/file.txt"; nav_select(0); // Выбор свободного навигатора // Выбор диска и монтирование его nav_drive_set(1); nav_partition_mount(); // В этом месте навигатор находится в корневом каталоге (root dir) #if( (FS_ASCII == ENABLED) && (FS_UNICODE == ENABLED) ) nav_string_ascii(); // Выбор формата имени ASCII #endif // поиск файла "dir1/file.txt" в текущей директории на disk 1 if( !nav_setcwd( (FS_STRING)path , TRUE, FALSE ) ) return FALSE; return TRUE; } 6.5 Ускорение множественной записи (multi-write) файлов 6.5.1 Обзор multi-write В случае множества вызовов file_write_buf() или file_putc() (например, при выводе в файл лога file), выполнение может замедлиться, потому что количество обращений к диску на запись очень важно для построения таблицы FAT. Если Вы записываете данные в файл несколько раз, и при этом вызовы записи разделены (как с примером записи в лог), то может быть интересным построение таблицы FAT файла более эффективной последовательностью кода. Каждый вызов подпрограмм file_write_buf() или file_putc() (например, для записи в лог) требует резервирования места в таблице FAT, см. Пример A. Чтобы увеличить эффективность, Atmel советует создать резервирование необходимого места FAT за одну предварительную последовательность, перед выполнением других команд, см. Пример B. Этот пример даст более эффективную подпрограмму записи. См. раздел 6.5.4. 6.5.2 Пример A Этот пример показывает обычную последовательность заполнения файла: Bool fill_file( void ) { const UNICODE _MEM_TYPE_SLOW_ name[50]={'l','o','g','.','b','i','n',0}; U16 u16_nb_write; memset(g_trans_buffer, 0x55, FILL_FILE_BUF_SIZE); if( !nav_drive_set(LUN_DISK) ) // Вход в диск return FALSE; if( !nav_partition_mount() ) // Монтирование раздела диска return FALSE; if( !nav_file_create((const FS_STRING)name) ) // Создание файла return FALSE; if( !file_open(FOPEN_MODE_W) ) // Открыть файл в режиме записи, и принудительно // задать его размер 0. return FALSE; for( u16_nb_write=0; u16_nb_write<FILL_FILE_NB_WRITE; u16_nb_write++ ) { // Здесь при каждой записи в файл происходит также выделение места // в FAT, и если у нас несколько буферов для записи, то выполнение // может замедлиться. if( !file_write_buf( g_trans_buffer , FILL_FILE_BUF_SIZE )) { file_close(); return FALSE; } } file_close(); return TRUE; } 6.5.3 Пример B Этот пример позволяет ускорить обращения на запись с помощью подпрограммы предварительного выделения. // Заполнение файла размером > 1MB #define FILL_FILE_NB_WRITE 855L #define FILL_FILE_BUF_SIZE 120L Bool fill_file_fast( void ) { const UNICODE _MEM_TYPE_SLOW_ name[50]={'l','o','g','_','f','a','s','t','.','b','i','n',0}; _MEM_TYPE_SLOW_ Fs_file_segment g_recorder_seg; U16 u16_nb_write; memset(g_trans_buffer, 0x55, FILL_FILE_BUF_SIZE); if( !nav_drive_set(LUN_DISK) ) // Вход в диск return FALSE; if( !nav_partition_mount() ) // Монтирование раздела диска return FALSE; if( !nav_file_create( (const FS_STRING) name )) // Создание файла return FALSE; if( !file_open(FOPEN_MODE_W) ) // Открыть файл в режиме записи, и принудительно // задать его размер 0. return FALSE; // Определение размера сегмента для prealloc (в единицах 512 байт) // Внимание: Вы можете выделить больше памяти в случае, когда не знаете // общий размер. g_recorder_seg.u16_size = (FILL_FILE_NB_WRITE*FILL_FILE_BUF_SIZE + 512L)/512L; // ****PREALLLOC****** сегмент для заполнения if( !file_write(&g_recorder_seg) ) { file_close(); return FALSE; } // Проверка размера выделенного сегмента if( g_recorder_seg.u16_size < ((FILL_FILE_NB_WRITE*FILL_FILE_BUF_SIZE + 512L)/512L) ) { file_close(); return FALSE; } // Закрыть/открыть файл, чтобы сбросить его размер file_close(); // Закрывает файл. Эта подпрограмма не удаляет предыдущее выделение. if( !file_open(FOPEN_MODE_W) ) // Открыть файл в режиме записи, // что сбросит его размер в 0 return FALSE; for( u16_nb_write=0; u16_nb_write<FILL_FILE_NB_WRITE; u16_nb_write++ ) { // В этом месте список кластеров файла уже выделен и подпрограмма записи // будет работать быстрее. if( !file_write_buf( g_trans_buffer, FILL_FILE_BUF_SIZE) ) { file_close(); return FALSE; } } file_close(); return TRUE; } 6.5.4 Статистика использования Вот результаты работы следующего примера (можно наглядно увидеть, насколько оптимальнее работает Пример B): Создание файла 100.2 килобайта (буфер 120 байт * количество записей 855) на диске 256 мегабайт (размер кластера FAT16 4 килобайта): • С примером A, количество обращений на запись в тот же самый сектор FAT 50 раз максимум и в среднем 25 раз. Создание файла 1.1 мегабайта (буфер 120 байт * количество записей 10000) на диске 256 мегабайт (размер кластера FAT16 4 килобайта): • С примером A, количество обращений на запись в тот же самый сектор FAT 255 раз максимум и в среднем 147 раз. Внимание: важно знать, что время создания FAT зависит от используемого типа FAT. Выделение пространства FAT работает быстрее в FAT12 и FAT16, чем в FAT32. Если у Вас размер диска < 2 гигабайт, то Вы можете принудительно использовать обычный FAT (FAT12 и FAT16), когда вызываете nav_drive_format(FS_FORMAT_FAT). Формат FAT32 требует для записи большее количество (в 4 раза) секторов в таблице FAT, чем FAT16, для того же самого размера файла и того же самого размера диска. 6.6 Копирование диска на другой диск Этот пример использует 3 навигатора (navigator handle): • для просмотра диска-источника (source disk) Bool copydisk( void ) { const UNICODE _MEM_TYPE_SLOW_ name[50]; U8 u8_folder_level = 0; //trace("Монтирование драйва\n"); //** Используется 3 навигатора: // 0 для просмотра диска-источника SD (стандартная карта SD) // 1 для просмотра диска-назначения NF (носитель NandFlash) // 2 используется подпрограммой копирования файла nav_select( 0 ); if( !nav_drive_set(LUN_ID_MMC_SD) ) return FALSE; if( !nav_partition_mount() ) return FALSE; nav_select( 1 ); if( !nav_drive_set( LUN_ID_NF_DISKMASS )) return FALSE; if( !nav_partition_mount() ) return FALSE; // Цикл для сканирования и создания ВСЕХ папок и файлов while(1) { // В текущем каталоге нет каталогов, так что переход в родительский // каталог дисков SD и NandFlash while(1) { //trace("Поиск файлов в каталоге\n"); // Выбор SD заново nav_select( 0 ); if( nav_filelist_set( 0 , FS_FIND_NEXT ) ) break; // найден следующий файл и каталог // В текущем каталоге нет других каталогов или файлов, так что // делается переход на родительский каталог дисков SD и NandFlash. if( 0 == u8_folder_level ) { // конец обновления каталога //trace("End of copy\n"); return TRUE; //********* КОПИРОВАНИЕ ОКОНЧЕНО ************** } //trace("Перейти в родительскую папку\n"); // Пометка: подпрограмма nav_dir_gotoparent() переходит в родительский каталог // и выбирает дочерний каталог в списке u8_folder_level--; if( !nav_dir_gotoparent() ) return FALSE; // Выбор навигатора NandFlash, и переход в тот же самый каталог, что и на SD nav_select( 1 ); if( !nav_dir_gotoparent() ) return FALSE; } // конец while (1) if( nav_file_isdir()) { //trace("Найден каталог - создание каталога & и переход в него (CD)\n"); //** В этом месте найден и выбран новый каталог // Получение имени текущего выбора (= имени каталога на SD) if( !nav_file_name( (FS_STRING)name, 50, FS_NAME_GET, FALSE) ) return FALSE; // Войти в каталог (на SD) if( !nav_dir_cd() ) return FALSE; u8_folder_level++; // Выбор диска NandFlash nav_select( 1 ); // Создание каталога на диске NandFlash if( !nav_dir_make((FS_STRING)name) ) { if( FS_ERR_FILE_EXIST != fs_g_status ) return FALSE; // в этом месте ошибка, такое имя уже есть } // В этом месте навигатор выбрал каталог на NandFlash if( !nav_dir_cd() ) { if( FS_ERR_NO_DIR == fs_g_status ) { // FYC -> копирование невозможно, потому что имя файла совпадает // с именем каталога } return FALSE; } // здесь каталог создан, и навигаторы вошли в этот каталог } else { //trace("Файл найден - копирование файла\n"); //** Здесь найден и выбран новый файл // Получение имени текущего выбора (= имени файла на SD) if( !nav_file_name((FS_STRING)name, 50, FS_NAME_GET, FALSE) ) return FALSE; if( !nav_file_copy() ) return FALSE; // Вставить (PASTE) файл в текущий каталог диска NandFlash nav_select( 1 ); while( !nav_file_paste_start((FS_STRING)name) ) { // Ошибка if( fs_g_status != FS_ERR_FILE_EXIST ) return FALSE; //trace("удаление файла\n"); // Файл существует, тогда он будет удален if( !nav_file_del( TRUE ) ) return FALSE; // здесь снова попробуем вставить (retry PASTE) } // Запуск копирования { U8 status; do{ status = nav_file_paste_state(FALSE); }while( COPY_BUSY == status ); if( COPY_FINISH != status ) return FALSE; } } // если каталог или файл } // конец внешнего while(1) } [Ссылки] 1. AVR114: Using the ATMEL File System management for AT32UC3x, AT90USBx and ATmega32U4 site:atmel.com. |