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

Файловая система 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 NameDescription
00readme.txtReadme file
00history.txtVersion history file
ff.cFatFs module
ffconf.hConfiguration file for the FatFs module
ff.hHeader file for the FatFs application module
diskio.hHeader file for the FatFs and disk I/O module
diskio.cAn instance that attaches disk I/O functions to FatFS
ffunicode.cUnicode encoding utility functions
ffsystem.cOptional 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 нет никаких проблем.

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

Поделиться:

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

Instant Quote