Файловая система FatFS является хорошо известной файловой системой в области микроконтроллеров. Благодаря своей легковесности и совместимости она пользуется популярностью у разработчиков микроконтроллеров. При выполнении таких задач, как чтение и запись файлов на U-дисках и SD-картах, нам часто требуется файловая система для поддержки нашей работы. Особенно в некоторых приложениях микроконтроллеров добавление файловой системы может значительно улучшить удобство взаимодействия с системой. В этой статье мы расскажем о переносе и применении файловой системы FatFS на STM32F4.
Особенность FatFS
- Файловая система FAT/exFAT, совместимая с DOS/Windows.
- Независимый от платформы и легко переносимый.
- Программный код и рабочая область занимают очень мало места.
- Поддерживает следующие различные параметры конфигурации:
- Длинные имена файлов в ANSI/OEM или Unicode.
- Файловая система exFAT, 64-разрядная LBA и GPT для хранения больших объемов данных.
- Потоковая безопасность RTOS.
- Несколько томов (физические диски и разделы).
- Переменный размер сектора.
- Несколько кодовых страниц, включая DBCS.
- Только для чтения, опциональный API, буферы ввода-вывода и т. д.
Перенос FatFS на MCU
Подготовка
Перед началом переноса FatFS необходимо выполнить некоторые необходимые подготовительные работы. Во-первых, нужно подготовить соответствующую аппаратную платформу. Здесь мы используем операционную платформу STM32F407VET6. Также завершена работа по переносу библиотек, связанных с аппаратным обеспечением USB.
Во-вторых, нам также необходимо подготовить соответствующий исходный код FatFS. Здесь мы используем последнюю версию R0.14b, которую можно скачать с веб-сайта:
http://elm-chan.org/fsw/ff/00index_e.html
После распаковки загруженного исходного кода появляются две папки: document и source. Папка document содержит соответствующую документацию, которая совпадает с содержанием веб-сайта. Вы можете просматривать эти документы во время переноса. Папка Source содержит файлы, связанные с исходным кодом, в основном включая:

Среди серии файлов, показанных на картинке выше, файл 00readme.txt содержит введение к каждому файлу. Мы просматриваем его содержимое следующим образом:
| File Name | Description |
|---|---|
| 00readme.txt | Readme file |
| 00history.txt | Version history file |
| ff.c | FatFs module |
| ffconf.h | Configuration file for the FatFs module |
| ff.h | Header file for the FatFs application module |
| diskio.h | Header file for the FatFs and disk I/O module |
| diskio.c | An instance that attaches disk I/O functions to FatFS |
| ffunicode.c | Unicode encoding utility functions |
| ffsystem.c | Optional operating system-related file instance |
Среди этих файлов ff.c и ff.h являются основными файлами. ffunicode.c — это кодировка символов, которая будет выбрана в соответствии с конфигурацией файла настроек. Файл ffsystem.c определяется в соответствии с вашими собственными потребностями. Таким образом, файлы, которые связаны с конкретной платформой приложения и должны быть реализованы нами, — это конфигурационный файл ffconf.h и файлы дисковых операций diskio.h и diskio.c. Эти файлы также являются основным объектом нашей переноса.
Реализация трансплантации
Мы завершили подготовительную работу для переноса и теперь приступим к переносу приложения на USB-диски большой емкости. Как мы уже говорили, файлы, которые необходимо обработать для переноса, — это файл конфигурации ffconf.h и файлы дисковых операций diskio.h и diskio.c.
Что касается конфигурационного файла ffconf.h, то он фактически имеет свой собственный экземпляр. Нам нужно только изменить конфигурацию по мере необходимости. Параметры конфигурации, которые нам нужно изменить, включают:
Параметр конфигурации поддерживаемого метода кодирования FF_CODE_PAGE связан с вопросом кодирования файлов. Мы настраиваем его для поддержки упрощенного китайского языка.
Количество логических дисков настраивается с помощью параметра FF_VOLUMES. FatFS может применяться к нескольким дискам одновременно, поэтому нам нужно настроить количество дисков в соответствии с реальной ситуацией.
Параметр конфигурации временных меток FF_FS_NORTC. В большинстве случаев нам не нужно записывать временные метки, поэтому мы отключаем его здесь.
Остальные функции связаны с реализацией операций ввода-вывода диска. В справочном документе FatFS указано, что необходимо реализовать два типа функций: первый — функции, связанные с управлением дисковым устройством, в основном функции для получения статуса устройства, инициализации функций устройства и чтения. Функции данных, функции записи данных и функции, связанные с управлением устройством; вторая категория — функция работы часов реального времени, которая в основном используется для получения функции текущего времени. Таким образом, реализация этих 6 функций является основной задачей переноса.
Функция получения статуса устройства
Функция определения состояния диска disk_status. Используется для определения состояния диска и будет вызываться в файле ff.c. Прототип функции выглядит следующим образом:
ˆ DSTATUS disk_status(BYTE drV);
В соответствии с определением прототипа и требованиями нашего USB-устройства хранения данных, мы можем реализовать функцию получения информации о состоянии диска следующим образом:
/*Status acquisition function for USBH*/
static DSTATUS USBH_status(BYTE lun)
{
DRESULT res = RES_ERROR;
if(USBH_MSC_UnitIsReady(&hUsbHostFS, lun))
{
res = RES_OK;
}
else
{
res = RES_ERROR;
}
return res;
}
Инициализировать функцию устройства
Функция инициализации носителя информации disk_initialize. Она используется для инициализации дискового устройства и будет вызвана в файле ff.c. Прототип ее функции выглядит следующим образом:
ˆ DSTATUS disk_initialize(BYTE drv);
Согласно определению прототипа и требованиям нашего USB-устройства хранения данных, мы можем реализовать функцию инициализации дискового накопителя, но на самом деле она нам здесь не нужна, поскольку инициализация уже выполнена в библиотеке USB HOST, поэтому просто верните правильную функцию напрямую.
/*Initialization function for USBH*/
static DSTATUS USBH_initialize(BYTE lun)
{
//Initialization has been completed in the USB HOST library
return RES_OK;
}
Читать данные
Функция чтения сектора disk_read. Используется для чтения данных с диска. Написана в соответствии с конкретным дисковым вводом-выводом и будет вызвана в файле ff.c. Прототип функции выглядит следующим образом:
- DRESULT disk_read(BYTE drv, BYTE*buff, DWORD sector, BYTE.count);
В соответствии с определением прототипа и требованиями нашего USB-устройства хранения данных, мы можем реализовать функцию чтения данных с диска следующим образом:
/*Read sector function for USBH*/
static DRESULT USBH_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
if(USBH_MSC_Read(&hUsbHostFS, lun, sector, buff, count) == USBH_OK)
{
res = RES_OK;
}
else
{
USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info);
switch(info.sense.asc)
{
case SCSI_ASC_LOGICAL_UNIT_NOT_READY:
case SCSI_ASC_MEDIUM_NOT_PRESENT:
case SCSI_ASC_NOT_READY_TO_READY_CHANGE:
USBH_ErrLog ("USB Disk is not ready!");
res = RES_NOTRDY;
break;
default:
res = RES_ERROR;
break;
}
}
return res;
}
Запись данных
Напишите функцию sector disk_write. Она используется для записи данных на диск. Она написана в соответствии с конкретным дисковым вводом-выводом и будет вызвана в файле ff.c. Прототип ее функции выглядит следующим образом:
- DRESULT disk_write(BYTE drv, const BYTE*buff, DWORD sector, BYTE count);
В соответствии с определением прототипа и требованиями нашего USB-устройства хранения данных, мы можем реализовать функцию записи данных на диск следующим образом:
/*Write sector function for USBH*/
static DRESULT USBH_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
if(USBH_MSC_Write(&hUsbHostFS, lun, sector, (BYTE *)buff, count) == USBH_OK)
{
res = RES_OK;
}
else
{
USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info);
switch(info.sense.asc)
{
case SCSI_ASC_WRITE_PROTECTED:
USBH_ErrLog("USB Disk is Write protected!");
res = RES_WRPRT;
break;
case SCSI_ASC_LOGICAL_UNIT_NOT_READY:
case SCSI_ASC_MEDIUM_NOT_PRESENT:
case SCSI_ASC_NOT_READY_TO_READY_CHANGE:
USBH_ErrLog("USB Disk is not ready!");
res = RES_NOTRDY;
break;
default:
res = RES_ERROR;
break;
}
}
return res;
}
Функции, связанные с устройством управления
Функция управления носителями информации disk_ioctl. В этой функции можно написать необходимый функциональный код, например, для получения размера носителя информации, определения, включен ли носитель информации, количества секторов носителя информации и т. д. Если это простое приложение, то писать его не нужно. Прототип функции выглядит следующим образом:
- DRESULT disk_ioctl(BYTE drv, BYTE ctrl, VoiI*buff);
В соответствии с определением прототипа и требованиями нашего устройства массовой памяти USB, мы можем реализовать функции управления дисковым устройством следующим образом:
/*USBH IO control function */
static DRESULT USBH_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
switch(cmd)
{
/* Make sure that no pending write process */
case CTRL_SYNC:
res = RES_OK;
break;
/* Get number of sectors on the disk (DWORD) */
case GET_SECTOR_COUNT :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_nbr;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
/* Get R/W sector size (WORD) */
case GET_SECTOR_SIZE :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_size;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
/* Get erase block size in unit of sector (DWORD) */
case GET_BLOCK_SIZE :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_size / USB_DEFAULT_BLOCK_SIZE;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
default:
res = RES_PARERR;
}
return res;
}
Получить текущее время
Функция реального времени get_fattime. Используется для получения текущего времени и возврата 32-разрядного целого числа без знака. Информация о часах содержится в этих 32 битах. Если вы не используете временную метку, вы можете напрямую вернуть число, например 0. Прототип функции выглядит следующим образом:
ˆ DWORD get_fattime(Void);
В соответствии с определением прототипа и требованиями нашего USB-устройства хранения данных, мы можем реализовать функцию получения информации о состоянии диска следующим образом:
/*Read clock function*/
DWORD get_fattime(void)
{
return 0;
}
Завершите написание вышеуказанных 6 программ, и работа по переносу будет в основном завершена. Вы можете заметить, что название функции, которую мы реализовали, отличается от прототипа функции. Это сделано в основном для облегчения работы, когда одновременно существует несколько устройств хранения. Мы можем просто вызвать функцию, которую мы реализовали, в целевой функции.
Тестирование приложений
Мы завершили перенос FatFS, теперь давайте проверим, правильно ли выполнен перенос. Для этого давайте напишем приложение для записи данных в файл на USB-флеш-накопителе и чтения данных из файла.
/* USB HOST MSC operation function, this part of the function is set according to needs */
static void MSC_Application(void)
{
FRESULT res; /* FatFs function return value */
uint32_t byteswritten, bytesread; /* Number of files read and written */
uint8_t wtext[] = "This is STM32 working with FatFs!"; /* Write file buffer */
uint8_t wtext2[] = "This is an example of FatFs reading and writing!"; /* Write file buffer */
uint8_t wtext3[] = "This is a test of appending data to a file!"; /* Write file buffer */
uint8_t rtext[100]; /* Read file buffer */
/* Register the file system object to the FatFs module */
if(f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
/* Open a file */
if(f_open(&USBHFile, "STM32.TXT", FA_OPEN_EXISTING | FA_WRITE) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
res=f_lseek(&USBHFile,f_size(&USBHFile)); //Point the pointer to the end of the file
//res=f_lseek(&USBHFile,100); //Point the pointer to the end of the file
/* Write data to file */
res = f_write(&USBHFile, wtext, sizeof(wtext), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
res = f_write(&USBHFile, wtext2, sizeof(wtext2), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
res = f_write(&USBHFile, wtext3, sizeof(wtext3), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
if((byteswritten == 0) || (res != FR_OK))
{
/* Error handling */
Error_Handler();
}
else
{
/* Close file */
f_close(&USBHFile);
/* Open file for reading */
if(f_open(&USBHFile, "STM32.TXT", FA_READ) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
/* Read data from file */
res = f_read(&USBHFile, rtext, sizeof(rtext), (void *)&bytesread);
if((bytesread == 0) || (res != FR_OK))
{
/* Error handling */
Error_Handler();
}
else
{
/* Close file */
f_close(&USBHFile);
/* Compare read and written data */
if((bytesread != byteswritten))
{
/* Error handling */
Error_Handler();
}
else
{
/* No errors */
}
}
}
}
}
}
FATFS_UnLinkDriver(USBHPath);
}
Файл, который мы сначала создали на USB-диске, называется «STM32.TXT». В приведенном выше исходном коде после создания файла мы изменяем его, чтобы открыть и сохранить. Напишите «This is STM32 working with FatFs!» в созданный файл STM32.TXT, как показано на рисунке ниже:

Далее мы попробуем добавить содержимое к уже существующему файлу. Это по-прежнему файл STM32.TXT, как показано ниже:

На этом мы завершили перенос и тестирование файловой системы FatFS. Судя по результатам тестирования, перенос выполнен правильно, по крайней мере, в простых приложениях проблем обнаружено не было.
Заключение
В этой статье мы перенесли файловую систему FatFS на STM32F4 и провели простой тест чтения и записи. Судя по результатам теста, в процессе переноса FatFS нет никаких проблем.
При переносе мы учитывали удобство работы при одновременном наличии нескольких дисков. Определяемая нами функция дискового ввода-вывода должна быть реализована на основе фактического аппаратного обеспечения, а затем написанная нами функция дискового ввода-вывода вызывается в функции обратного вызова, указанной системой.




