Журнал РАДИОЛОЦМАН, декабрь 2013
Михаил Русских
Рассматривается библиотека FatFs, позволяющая работать с файловой системой FAT на SD-карте
Для того чтобы записанные на SD-карту данные можно было прочесть на компьютере, они должны быть упорядочены в соответствии со структурой файловой системы FAT (File Allocation Table или таблица размещения файлов). Для облегчения труда разработчиков малых встраиваемых систем при реализации таблицы FAT программистом под псевдонимом ChaN была написана библиотека FatFs [1], которая быстро приобрела популярность благодаря тому, что ее функциональность позволяет выполнять действия, характерные для серьезных операционных систем при работе с файлами и папками. На данный момент доступна версия R0.10, которая вышла в середине октября 2013 года.
Эта библиотека написана на ANSI C и состоит из нескольких файлов. Она представляет собой связующее программное обеспечение (middleware) между приложением и аппаратным интерфейсным модулем (Рисунок 3). Это значит, что библиотека не зависит от аппаратной части и может быть использована с любым микроконтроллером. Портирование, то есть написание кода, связывающего аппаратную часть с FatFs, должен выполнить сам разработчик встраиваемой системы.
Рисунок 3. | Место библиотеки FatFs в программной структуре малой встраиваемой системы. |
В библиотеку входят следующие файлы:
- ff.c и ff.h – являются основой FatFs, поскольку определяют функции, необходимые для работы с файловой системой.
- diskio.c и diskio.h – отвечают за низкоуровневый интерфейс, эти файлы должны быть изменены разработчиком в соответствии с используемым способом связи с носителем.
- integer.h – в этом файле можно задать используемые в библиотеке ключевые слова, определяющие тип данных, например, typedef int INT. Следует учитывать возможности этого файла при переходе с 8 разрядной системы на 32 разрядную и наоборот.
- ffconf.h – здесь определяются настройки библиотеки, например, #define _FS_READONLY 0 разрешает чтение и запись на носитель, при выборе 1 запись была бы запрещена.
На Рисунке 4 показана взаимосвязь файлов библиотеки FatFs с приложением и отвечающими за работу модуля SDIO файлами, описанными в предыдущей части.
Рисунок 4. | Взаимосвязь файлов. |
Для того чтобы модуль SDIO понимал команды FatFs, нужно определить содержимое функций файла diskio.c. Первым делом организуется функция disk_initialize(drv). Она инициализирует физический диск и подготавливает его к чтению и записи данных. Аргумент drv определяет номер диска. В этой функции обязательно должна вызываться функция SD_Init(), которая непосредственно принимает на себя обязанности по инициализации SD-карты. Также следует учитывать, что функция disk_initialize не должна вызываться из приложения, поскольку она является служебной функцией в составе файла ff.c. Разработчик FatFs предупреждает, что в противном случае файловая структура носителя может быть испорчена. Вместо этого в приложении для монтирования диска нужно вызывать функцию f_mount, содержащую в себе disk_initialize. Функция f_mount будет рассмотрена ниже.
Следующей необходимой для правильной работы библиотеки функцией является disk_status(drv). Она возвращает текущее состояние диска. В ней обязателен вызов функции SD_GetCardInfo, передающей информацию о SD-карте. При получении от SD_GetCardInfo флага SD_OK функция disk_status должна возвращать 0, в противном случае она должна вернуть флаг STA_NOINIT, это будет являться свидетельством того, что диск не был проинициализирован.
Для чтения секторов имеется функция disk_read, которая в качестве аргументов принимает четыре параметра: drv (номер диска), *buff (указатель на буфер чтения данных), sector (номер начального сектора), count (количество читаемых секторов). В этой функции нужно организовать цикл, в котором функция SD_ReadBlock будет считывать блоки данных в указанном диапазоне (от sector до count). Чтобы прочитать данные, нужно в функции disk_write, принимающей в качестве аргументов те же четыре параметра, что и disk_read, организовать подобный цикл, но уже с функцией SD_WriteBlock, позволяющей считывать информацию с SD-карты. Обе функции (disk_read и disk_write) обязательно должны предусматривать возвращение флага RES_OK в случае успешной операции, либо флага RES_ERROR при возникновении аппаратной ошибки чтения/записи. Также библиотека FatFs предусматривает возврат от этих функций флага RES_PARERR в случае приема неверного параметра и флага RES_NOTRDY, если диск не был инициализирован.
Также для организации дополнительной функциональности, не включающей в себя операции чтения и записи, существует функция disk_ioctl. В качестве аргументов она принимает drv (номер привода), ctrl (команду управления) и *buff (указатель на буфер данных). Функция возвращает те же флаги, что и disk_read или disk_write. Аргумент ctrl может принимать следующие значения: CTRL_SYNC (позволяет завершить операции, ожидающие окончания процесса записи), GET_SECTOR_SIZE (возвращает размер сектора привода в переменную, на которую указывает buff), GET_SECTOR_COUNT (возвращает количество доступных секторов в переменную, на которую указывает buff), GET_BLOCK_SIZE (возвращает размер блока для очистки в переменную, на которую указывает buff), CTRL_ERASE_SECTOR (очищает область памяти, определяемую массивом, первым элементом которого является начальный адрес этой области памяти, а последним элементом – конечный; на сам массив должен указывать buff).
Наконец, в файле diskio.c нужно организовать работу еще двух зависящих от времени функций – disk_timerproc и get_fattime. Первая обеспечивает таймауты для надежного функционирования библиотеки. Ее нужно вызывать каждые 10 мс. Вторая возвращает текущее время выполнения определенной операции. Для ее правильной работы нужно включить и настроить часы реального времени. Если это не нужно, то можно организовать возврат какого-либо определенного значения или нуля. Подробный пример правильной инициализации всех вышеприведенных функций можно найти в [2]. Данная программа предназначена для микроконтроллеров STM32F407xxx/42xxxx/43xxxx и основана на работе модуля SDIO и драйвера stm324xg_eval_sdio_sd.
Теперь перейдем непосредственно к рассмотрению работы библиотеки FatFs в рамках приложения. Как уже было сказано выше, для монтирования и регистрации диска используется функция f_mount с тремя аргументами fatfs, path и opt. Аргумент fatfs является указателем на объект файловой системы, который должен быть зарегистрирован, path указывает на строку, которая в случае единственного привода должна быть пустой, opt определяет опцию инициализации и может принимать два значения: 0 (не монтировать привод, он будет смонтирован позже) и 1 (смонтировать привод сейчас, чтобы проверить его доступность). Функции библиотеки FatFs могут возвращать флаги, перечисленные в Таблице 2. f_mount возвращает FR_OK, FR_INVALID_DRIVE, FR_DISK_ERR, FR_NOT_READY и FR_NO_FILESYSTEM.
Таблица 2. | Флаги, возвращаемые функциями библиотеки FatFs | ||||||||||||||||||||||||||||||||||||||||
|
Для того чтобы создавать файлы и работать с ними используется функция f_open. Она создает файловый объект, необходимый для доступа к конкретному файлу. Функция принимает три аргумента: *fp (указатель на структуру данных объекта файла), *path (указатель на строку с именем файла) и mode (определяет тип доступа и метод открытия файла). Аргумент mode может быть представлен одним или несколькими флагами: FA_READ (позволяет считывать из файла), FA_WRITE (позволяет записывать в файл), FA_OPEN_EXISTING (открывает уже имеющийся на диске файл), FA_OPEN_ALWAYS (либо открывает существующий файл, либо создает новый), FA_CREATE_NEW (создает новый файл) и FA_CREATE_ALWAYS (создает новый файл, если файл с таким именем существует, то он будет перезаписан). Функция f_open может возвращать все перечисленные в Таблице 2 флаги, кроме FR_INVALID_OBJECT, FR_MKFS_ABORTED и FR_INVALID_PARAMETER. После работы с файлом требуется его закрыть, для этого существует функция f_close. Она принимает только один аргумент *fp и может вернуть FR_OK, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_OBJECT и FR_TIMEOUT.
Для чтения и записи данных служат функции f_read и f_write, соответственно. Они обе принимают четыре аргумента: *fp, *buff (указатель на буфер, куда будут записаны прочитанные данные, или где содержатся записываемые данные), btr или btw (количество байт для чтения или для записи, соответственно), *br или *bw (указатели на переменную для возврата количества прочитанных или записанных байтов). Эти функции могут возвращать FR_OK, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_OBJECT и FR_TIMEOUT. Следует отметить, что функция f_write недоступна при включенном режиме «только чтение», то есть при _FS_READONLY = 1.
Для полноценной работы с файловой системой нужно уметь выполнять операции не только над файлами, но и над каталогами. Создать новый каталог можно с помощью функции f_mkdir, которая принимает строку (аргумент *path), указывающую имя каталога и полный путь. Если задать f_mkdir("sub1"), то каталог sub1 будет создан в корневом каталоге, если записать f_mkdir("sub1/sub2"), то подкаталог sub2 будет создан в sub1. Здесь также следует учитывать, что строка не должна заканчиваться символом «/». Для того, чтобы получить доступ к каталогу, то есть открыть его, используют функцию f_opendir. Ее аргументами являются указатель на незаполненную структуру объекта директории *dp и указатель *path на определяющую путь строку. Чтобы поменять текущую директорию диска применяют f_chdir с единственным аргументом *path, указывающим на директорию, которая станет текущей.
Для удаления файла или каталога используется функция f_unlink. Она принимает только *path – указатель на строку, которая задает имя удаляемого объекта. При этом удаляемый объект не должен иметь атрибута «Только чтение», удаляемый файл не должен быть открыт, удаляемая директория не должна быть текущей и обязательно должна быть пустой.
При работе с файлами и папками иногда возникает необходимость в их переименовании. Для этого имеется функция f_rename. Ее аргументами являются указатели *old_name и *new_name. Первый указывает на строку, которая содержит текущее имя объекта, а второй на строку с новым именем объекта. Помимо обычного переименования функция позволяет переносить объект в другую директорию. Например, запись f_rename("oldname.txt", "dir1/newname.txt") переименует файл oldname.txt в newname.txt и переместит его в каталог dir1.
Библиотека FatFs позволяет не только работать с файлами и каталогами, но и выполнять действия на более фундаментальном уровне, то есть делить физический диск на разделы и создавать на диске файловую систему FAT. Первое можно осуществить с помощью функции f_fdisk, принимающей аргументы pdrv (определяет номер физического диска, который будет разбит на разделы), part[] (таблица карты разделов, имеющая 4 элемента) и work (определяет размер рабочей области для функции; минимальный размер указывается в переменной _MAX_SS). Разбивка на разделы выполняется в соответствии с форматом FDISK, допускающим создание максимум 4 разделов. Соотношение объемов разделов определяется вторым аргументом функции, который можно задать, например, как DWORD plist[] = {50, 50, 0, 0}, что будет означать разбиение диска на два логических раздела с одинаковым объемом. Для дальнейшего форматирования диска в соответствии с FAT используют функцию f_mkfs, принимающую аргументы *path (указатель на логический номер диска, который должен быть отформатирован), sfd (правило разбиения на разделы) и au (задает размер единицы данных в байтах). Аргумент sfd может принимать два значения: 0 или 1. В первом случае предусматривается правило разбиения FDISK, во втором – SFD. При работе с твердотельными накопителями, в том числе и с SD-картами, следует выбирать по умолчанию 0. Правило SFD подходит для работы с гибкими дисками, поскольку не предусматривает разделы диска, при этом FAT начинается с первого сектора физического диска. С помощью аргумента au можно принудительно задать размер кластера, это число должно быть 2n. Именно оно определяет тип используемой файловой системы: FAT12, FAT16 или FAT32.
Библиотека FatFs имеет еще ряд дополнительных функций, облегчающих работу с файловой системой FAT, но их рассмотрение в рамках одной статьи не представляется возможным. Тем не менее, описанные выше функции предоставляют необходимый функционал, позволяющий форматировать диски, создавать, перемещать и удалять файлы и каталоги, а также записывать и считывать информацию, то есть делать то, что может обычный менеджер файлов.
Список источников