Application du système de fichiers FatFS sur MCU

Le système de fichiers FatFS est un système de fichiers bien connu dans le domaine des microcontrôleurs. En raison de sa légèreté et de sa compatibilité, il est très apprécié des développeurs de microcontrôleurs. Lors de la mise en œuvre de tâches telles que la lecture et l'écriture de fichiers sur des disques U et des cartes SD, nous avons souvent besoin d'un système de fichiers pour faciliter notre travail. Dans certaines applications de microcontrôleurs en particulier, l'ajout d'un système de fichiers peut améliorer considérablement la convivialité de l'interaction avec le système. Dans cet article, nous présentons la transplantation et l'application du système de fichiers FatFS sur STM32F4.

Caractéristique de FatFS

  1. Système de fichiers FAT/exFAT compatible DOS/Windows.
  2. Indépendant de la plateforme et facile à transplanter.
  3. Le code du programme et l'espace de travail occupent très peu d'espace.
  4. Prend en charge les différentes options de configuration suivantes :
    • Noms de fichiers longs en ANSI/OEM ou Unicode.
    • Système de fichiers exFAT, LBA 64 bits et GPT pour stocker de grandes quantités de données.
    • Sécurité des threads du RTOS.
    • Volumes multiples (lecteurs physiques et partitions).
    • Taille de secteur variable.
    • Plusieurs pages de codes, y compris DBCS.
    • Lecture seule, API optionnelle, tampons d'E/S, etc.

Transplantation FatFS sur MCU

Préparation

Nous devons effectuer quelques préparatifs nécessaires avant de commencer la transplantation de FatFS. Tout d'abord, vous devez préparer la plate-forme matérielle correspondante. Nous utilisons ici la plate-forme d'exploitation STM32F407VET6. Le travail de portage des bibliothèques liées au matériel USB est également terminé.

Ensuite, nous devons également préparer le code source correspondant de FatFS. Nous utilisons ici la dernière version R0.14b, qui peut être téléchargée sur le site web :

http://elm-chan.org/fsw/ff/00index_e.html

Une fois le code source téléchargé décompressé, vous trouverez deux dossiers : document et source. Le dossier document contient la documentation pertinente, qui est identique au contenu du site Web. Vous pouvez consulter ces documents pour travailler pendant la transplantation. Le dossier Source contient les fichiers liés au code source, notamment :

FatFS Source Code
FatFS Source Code

Parmi la série de fichiers illustrés dans l'image ci-dessus, le fichier 00readme.txt contient une introduction à chaque fichier. Nous affichons son contenu comme suit :

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

Parmi ces fichiers, ff.c et ff.h sont des fichiers centraux. ffunicode.c correspond au codage des caractères, et le codage sera sélectionné en fonction de la configuration du fichier de configuration. Le fichier ffsystem.c est déterminé en fonction de vos propres besoins. Par conséquent, les fichiers liés à la plate-forme d'application spécifique et qui doivent être implémentés par nos soins sont le fichier de configuration ffconf.h et les fichiers d'opération disque diskio.h et diskio.c. Ces fichiers sont également au centre de notre transplantation.

Mettre en œuvre la transplantation

Nous avons terminé les préparatifs pour la transplantation et allons maintenant mettre en œuvre la transplantation de l'application pour les disques USB de grande capacité. Comme nous l'avons déjà mentionné, les fichiers qui doivent être traités pour la transplantation sont le fichier de configuration ffconf.h et les fichiers d'opération disque diskio.h et diskio.c.

En ce qui concerne le fichier de configuration ffconf.h, il existe en fait une instance de lui-même. Nous devons uniquement modifier la configuration selon les besoins. Les paramètres de configuration que nous devons modifier ici sont les suivants :

Le paramètre de configuration de la méthode d'encodage prise en charge FF_CODE_PAGE est lié à la question de l'encodage des fichiers. Nous le configurons pour prendre en charge le chinois simplifié.

Le nombre de lecteurs logiques est configuré avec le paramètre FF_VOLUMES. FatFS peut être appliqué à plusieurs lecteurs en même temps, nous devons donc configurer le nombre de lecteurs en fonction de la situation réelle.

Le paramètre de configuration de l'horodatage FF_FS_NORTC. La plupart du temps, nous n'avons pas besoin d'enregistrer les horodatages, nous le désactivons donc ici.

Les autres sont des fonctions liées à la mise en œuvre des opérations d'E/S disque. Le document d'aide FatFS nous indique qu'il existe deux types de fonctions à mettre en œuvre : les premières sont les fonctions liées au contrôle des périphériques disque, principalement les fonctions permettant d'obtenir l'état du périphérique, d'initialiser les fonctions du périphérique et de lire. Fonctions de données, fonctions d'écriture de données et fonctions liées au contrôle des périphériques ; la deuxième catégorie est la fonction d'horloge en temps réel, qui est principalement utilisée pour obtenir la fonction d'heure actuelle. Par conséquent, la mise en œuvre de ces 6 fonctions est la tâche principale de la transplantation.

Obtenir la fonction d'état de l'appareil

Fonction de détection de l'état du disque disk_status. Utilisée pour détecter l'état du disque, elle sera appelée dans le fichier ff.c. Son prototype de fonction est le suivant :

				
					ˆ DSTATUS disk_status(BYTE drV);
				
			

Selon la définition du prototype et les exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter la fonction d'acquisition de l'état du disque comme suit :

				
					/*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;
}
				
			

Initialiser la fonction du périphérique

Fonction d'initialisation du support de stockage disk_initialize. Elle sert à initialiser le périphérique disque et sera appelée dans le fichier ff.c. Son prototype de fonction est le suivant :

				
					ˆ DSTATUS disk_initialize(BYTE drv);
				
			

Selon sa définition prototype et les exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter la fonction d'initialisation du lecteur de disque, mais nous n'en avons pas réellement besoin ici, car l'initialisation a été effectuée dans la bibliothèque USB HOST. Il suffit donc de renvoyer directement la fonction correcte.

				
					/*Initialization function for USBH*/
static DSTATUS USBH_initialize(BYTE lun)
{
  //Initialization has been completed in the USB HOST library
return RES_OK;
}
				
			

Lire les données

Fonction de lecture de secteur disk_read. Elle sert à lire les données du disque. Elle est écrite en fonction de l'E/S spécifique du disque et sera appelée dans le fichier ff.c. Son prototype de fonction est le suivant :

				
					- DRESULT disk_read(BYTE drv, BYTE*buff, DWORD sector, BYTE.count);
				
			

Selon la définition de son prototype et les exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter la fonction de lecture des données du disque comme suit :

				
					/*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;
}
				
			

Écrire des données

Écrivez la fonction sectorielle disk_write. Elle sert à écrire des données sur le disque. Elle est écrite en fonction de l'E/S spécifique du disque et sera appelée dans le fichier ff.c. Son prototype de fonction est le suivant :

				
					- DRESULT disk_write(BYTE drv, const BYTE*buff, DWORD sector, BYTE count);
				
			

Selon la définition du prototype et les exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter la fonction d'écriture des données sur le disque comme suit :

				
					/*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;
}
				
			

Fonctions liées au dispositif de commande

Fonction de contrôle du support de stockage disk_ioctl. Vous pouvez écrire le code fonctionnel dont vous avez besoin dans cette fonction, tel que l'obtention de la taille du support de stockage, la détection de l'alimentation du support de stockage, le nombre de secteurs du support de stockage, etc. S'il s'agit d'une application simple, il n'est pas nécessaire de l'écrire. Son prototype de fonction est le suivant :

				
					- DRESULT disk_ioctl(BYTE drv, BYTE ctrl, VoiI*buff);
				
			

Conformément à sa définition prototype et aux exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter les fonctions liées au contrôle du périphérique disque comme suit :

				
					/*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;
}
				
			

Obtenir l'heure actuelle

Fonction d'horloge en temps réel get_fattime. Utilisée pour obtenir l'heure actuelle et renvoyer un entier non signé de 32 bits. Les informations d'horloge sont contenues dans ces 32 bits. Si vous n'utilisez pas d'horodatage, vous pouvez renvoyer directement un nombre, tel que 0. Son prototype de fonction est le suivant :

				
					ˆ DWORD get_fattime(Void);
				
			

Selon la définition du prototype et les exigences de notre périphérique de stockage de masse USB, nous pouvons implémenter la fonction d'acquisition de l'état du disque comme suit :

				
					/*Read clock function*/
DWORD get_fattime(void)
{

return 0;

}
				
			

Une fois les 6 programmes ci-dessus écrits, le travail de transplantation est pratiquement terminé. Vous remarquerez peut-être que le nom de la fonction que nous avons implémentée semble différent de celui de la fonction prototype. Cela vise principalement à faciliter les opérations lorsque plusieurs périphériques de stockage coexistent. Nous pouvons simplement appeler la fonction que nous avons implémentée dans la fonction cible.

Test des applications

Nous avons terminé la transplantation de FatFS, vérifions maintenant si elle est correcte. Pour cela, écrivons une application permettant d'écrire des données dans un fichier sur une clé USB et de lire les données du fichier.

				
					/* 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);
}
				
			

Le fichier que nous avons créé initialement sur la clé USB s'appelle « STM32.TXT ». Dans le code source ci-dessus, après avoir créé le fichier, nous le modifions pour l'ouvrir et le fermer. Écrivez « This is STM32 working with FatFs! » (STM32 fonctionne avec FatFs !) dans le fichier STM32.TXT créé, comme indiqué dans la figure ci-dessous :

write file buffer
write file buffer

Ensuite, nous essayons d'ajouter du contenu à un fichier déjà existant. Il s'agit toujours du fichier STM32.TXT, comme indiqué ci-dessous :

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

À ce stade, nous avons terminé la transplantation et les tests du système de fichiers FatFS. D'après les résultats des tests, la transplantation est correcte, du moins aucun problème n'a été détecté dans les applications simples.

Conclusion

Dans cet article, nous avons transposé le système de fichiers FatFS vers STM32F4 et avons effectué un test simple de lecture et d'écriture. À en juger par les résultats du test, le processus de transposition de FatFS ne pose aucun problème.

Lors du portage, nous avons pris en compte la facilité d'utilisation lorsque plusieurs lecteurs sont disponibles en même temps. La fonction d'opération d'E/S disque que nous définissons doit être implémentée en fonction du matériel réel, puis la fonction d'E/S disque que nous avons écrite est appelée dans la fonction de rappel spécifiée par le système.

Part à:

Retour en haut

Instant Quote