Применение файловой системы FatFS на микроконтроллере

Содержание

Application of FatFS file system on MCU

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

Особенность FatFS

  1. Файловая система FAT/exFAT, совместимая с DOS/Windows.
  2. Независимый от платформы и легко переносимый.
  3. Программный код и рабочая область занимают очень мало места.
  4. Поддерживает следующие различные параметры конфигурации:
    • Длинные имена файлов в 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 содержит файлы, связанные с исходным кодом, в основном включая:

FatFS Source Code
FatFS Source Code

Среди серии файлов, показанных на картинке выше, файл 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, как показано на рисунке ниже:

write file buffer
write file buffer

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

FatFS porting verification writing data to file
FatFS porting verification - writing data to file

На этом мы завершили перенос и тестирование файловой системы FatFS. Судя по результатам тестирования, перенос выполнен правильно, по крайней мере, в простых приложениях проблем обнаружено не было.

Заключение

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

При переносе мы учитывали удобство работы при одновременном наличии нескольких дисков. Определяемая нами функция дискового ввода-вывода должна быть реализована на основе фактического аппаратного обеспечения, а затем написанная нами функция дискового ввода-вывода вызывается в функции обратного вызова, указанной системой.

Об авторе

Picture of Aidan Taylor
Aidan Taylor

I am Aidan Taylor and I have over 10 years of experience in the field of PCB Reverse Engineering, PCB design and IC Unlock.

Поделиться

Рекомендуемый пост

Tags

Нужна помощь?

Прокрутить вверх

Мгновенный расчет

Instant Quote