Anwendung des FatFS-Dateisystems auf MCU

Das FatFS-Dateisystem ist ein bekanntes Dateisystem im Bereich der Mikrocontroller. Aufgrund seiner Leichtigkeit und Kompatibilität wird es von MCU-Entwicklern bevorzugt. Bei der Implementierung von Aufgaben wie dem Lesen und Schreiben von Dateien auf U-Disketten und SD-Karten benötigen wir oft ein Dateisystem, das unsere Arbeit unterstützt. Insbesondere in einigen MCU-Anwendungen kann die Hinzufügung eines Dateisystems die Benutzerfreundlichkeit der Systeminteraktion erheblich verbessern. In diesem Artikel stellen wir die Übertragung und Anwendung des FatFS-Dateisystems auf STM32F4 vor.

Merkmal von FatFS

  1. DOS/Windows-kompatibles FAT/exFAT-Dateisystem.
  2. Plattformunabhängig und leicht zu übertragen.
  3. Der Programmcode und der Arbeitsbereich benötigen nur sehr wenig Speicherplatz.
  4. Unterstützt die folgenden verschiedenen Konfigurationsoptionen:
    • Lange Dateinamen in ANSI/OEM oder Unicode.
    • exFAT-Dateisystem, 64-Bit-LBA und GPT zum Speichern großer Datenmengen.
    • Thread-Sicherheit von RTOS.
    • Mehrere Volumes (physische Laufwerke und Partitionen).
    • Variable Sektorgröße.
    • Mehrere Codepages, einschließlich DBCS.
    • Schreibgeschützt, optionale API, E/A-Puffer usw.

FatFS-Transplantation auf MCU

Vorbereitung

Bevor wir mit der FatFS-Transplantation beginnen, müssen wir einige notwendige Vorbereitungen treffen. Zunächst müssen Sie die entsprechende Hardwareplattform vorbereiten. Wir verwenden hier die Betriebsplattform STM32F407VET6. Die Portierungsarbeiten für die USB-Hardware-bezogenen Bibliotheken sind ebenfalls abgeschlossen.

Zweitens müssen wir auch den entsprechenden Quellcode von FatFS vorbereiten. Hier verwenden wir die neueste Version R0.14b, die von der Website heruntergeladen werden kann:

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

Nach dem Entpacken des heruntergeladenen Quellcodes finden Sie zwei Ordner: „document“ und „source“. Der Ordner „document“ enthält relevante Dokumentationen, die mit den Inhalten auf der Website übereinstimmen. Sie können diese Dokumente während der Portierung einsehen. Der Ordner „source“ enthält Dateien mit dem Quellcode, darunter vor allem:

FatFS Source Code
FatFS Source Code

Unter den in der Abbildung oben gezeigten Dateien enthält die Datei „00readme.txt“ eine Einführung zu jeder Datei. Wir sehen ihren Inhalt wie folgt:

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

Unter diesen Dateien sind ff.c und ff.h Kerndateien. ffunicode.c ist die Zeichenkodierung, und die Kodierung wird entsprechend der Konfiguration der Konfigurationsdatei ausgewählt. Die Datei ffsystem.c wird entsprechend Ihren eigenen Anforderungen festgelegt. Daher sind die Dateien, die sich auf die spezifische Anwendungsplattform beziehen und von uns implementiert werden müssen, die Konfigurationsdatei ffconf.h und die Festplattenbetriebsdateien diskio.h und diskio.c. Diese Dateien stehen auch im Mittelpunkt unserer Übertragung.

Transplantation durchführen

Wir haben die Vorbereitungsarbeiten für die Übertragung abgeschlossen und werden nun die Anwendungsübertragung für USB-Speichermedien mit großer Kapazität durchführen. Wie bereits erwähnt, sind die Dateien, die für die Übertragung verarbeitet werden müssen, die Konfigurationsdatei ffconf.h und die Festplattenbetriebsdateien diskio.h und diskio.c.

Die Konfigurationsdatei ffconf.h enthält tatsächlich eine Instanz ihrer selbst. Wir müssen lediglich die Konfiguration nach Bedarf anpassen. Zu den Konfigurationsparametern, die wir hier ändern müssen, gehören:

Der Konfigurationsparameter FF_CODE_PAGE für die unterstützte Kodierungsmethode bezieht sich auf die Dateikodierung. Wir konfigurieren ihn so, dass er vereinfachtes Chinesisch unterstützt.

Die Anzahl der logischen Laufwerke wird mit dem Parameter FF_VOLUMES konfiguriert. FatFS kann gleichzeitig auf mehrere Laufwerke angewendet werden, daher müssen wir die Anzahl der Laufwerke entsprechend der tatsächlichen Situation konfigurieren.

Der Zeitstempel-Konfigurationsparameter FF_FS_NORTC: In den meisten Fällen müssen wir keine Zeitstempel aufzeichnen, daher deaktivieren wir ihn hier.

Die übrigen Funktionen beziehen sich auf die Implementierung von Festplatten-E/A-Operationen. Laut FatFS-Hilfedokument müssen zwei Arten von Funktionen implementiert werden: zum einen Funktionen zur Steuerung von Festplattengeräten, hauptsächlich Funktionen zum Abrufen des Gerätestatus, zum Initialisieren von Gerätefunktionen und zum Lesen. Datenfunktionen, Schreibdatenfunktionen und Funktionen zur Steuerung von Geräten; die zweite Kategorie sind Echtzeit-Uhrfunktionen, die hauptsächlich zum Abrufen der aktuellen Uhrzeit verwendet werden. Daher ist die Implementierung dieser 6 Funktionen die Hauptaufgabe der Transplantation.

Gerätestatus abrufen

Funktion zur Erkennung des Festplattenstatus disk_status. Dient zur Erkennung des Festplattenstatus und wird in der Datei ff.c aufgerufen. Der Funktionsprototyp lautet wie folgt:

				
					ˆ DSTATUS disk_status(BYTE drV);
				
			

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Funktion zur Erfassung des Festplattenstatus wie folgt implementieren:

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

Gerätefunktion initialisieren

Funktion zur Initialisierung von Speichermedien disk_initialize. Sie dient zur Initialisierung des Festplattengeräts und wird in der Datei ff.c aufgerufen. Ihr Funktionsprototyp lautet wie folgt:

				
					ˆ DSTATUS disk_initialize(BYTE drv);
				
			

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Festplatteninitialisierungsfunktion implementieren, benötigen sie hier jedoch nicht, da die Initialisierung in der USB-HOST-Bibliothek bereits abgeschlossen wurde. Geben Sie daher einfach direkt die richtige Funktion zurück.

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

Daten lesen

Lesesektorfunktion disk_read. Sie wird zum Lesen von Festplattendaten verwendet. Sie wird entsprechend der spezifischen Festplatten-E/A geschrieben und in der Datei ff.c aufgerufen. Ihr Funktionsprototyp lautet wie folgt:

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

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Funktion zum Lesen der Festplattendaten wie folgt implementieren:

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

Daten schreiben

Schreibsektorfunktion disk_write. Sie wird zum Schreiben von Festplattendaten verwendet. Sie wird entsprechend der spezifischen Festplatten-E/A geschrieben und in der Datei ff.c aufgerufen. Ihr Funktionsprototyp lautet wie folgt:

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

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Funktion zum Schreiben von Daten auf die Festplatte wie folgt implementieren:

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

Funktionen im Zusammenhang mit Steuergeräten

Speichermedium-Steuerungsfunktion disk_ioctl. In dieser Funktion können Sie den benötigten Funktionscode schreiben, z. B. zum Abrufen der Größe des Speichermediums, zum Erkennen, ob das Speichermedium eingeschaltet ist oder nicht, und zum Abrufen der Anzahl der Sektoren des Speichermediums usw. Bei einer einfachen Anwendung ist es nicht erforderlich, diesen Code zu schreiben. Der Funktionsprototyp lautet wie folgt:

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

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Funktionen zur Steuerung des Festplattengeräts wie folgt implementieren:

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

Die aktuelle Uhrzeit abrufen

Echtzeituhrfunktion get_fattime. Wird verwendet, um die aktuelle Zeit abzurufen und eine 32-Bit-Ganzzahl ohne Vorzeichen zurückzugeben. Die Uhrzeitinformationen sind in diesen 32 Bit enthalten. Wenn Sie keinen Zeitstempel verwenden, können Sie direkt eine Zahl zurückgeben, z. B. 0. Der Funktionsprototyp lautet wie folgt:

				
					ˆ DWORD get_fattime(Void);
				
			

Gemäß der Prototypdefinition und den Anforderungen unseres USB-Massenspeichergeräts können wir die Funktion zur Erfassung des Festplattenstatus wie folgt implementieren:

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

return 0;

}
				
			

Schließen Sie das Schreiben der oben genannten 6 Programme ab, dann ist die Übertragung im Wesentlichen abgeschlossen. Möglicherweise stellen Sie fest, dass sich der Name der von uns implementierten Funktion von der Prototyp-Funktion zu unterscheiden scheint. Dies dient in erster Linie dazu, den Betrieb zu erleichtern, wenn mehrere Speichergeräte gleichzeitig vorhanden sind. Wir können einfach die von uns implementierte Funktion in der Zielfunktion aufrufen.

Anwendungstests

Wir haben die Übertragung von FatFS abgeschlossen. Nun wollen wir überprüfen, ob die Übertragung korrekt ist. Zu diesem Zweck schreiben wir eine Anwendung, um Daten in eine Datei auf einem USB-Stick zu schreiben und Dateidaten zu lesen.

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

Die Datei, die wir zuerst auf dem USB-Stick erstellt haben, heißt „STM32.TXT”. Im obigen Quellcode ändern wir die Datei nach ihrer Erstellung so, dass sie geöffnet und geschlossen wird. Schreiben Sie „This is STM32 working with FatFs!” in die erstellte Datei STM32.TXT, wie in der Abbildung unten gezeigt:

write file buffer
write file buffer

Als Nächstes versuchen wir, Inhalte an eine bereits vorhandene Datei anzuhängen. Es handelt sich weiterhin um die Datei STM32.TXT, wie unten gezeigt:

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

An dieser Stelle haben wir die Übertragung und das Testen des FatFS-Dateisystems abgeschlossen. Aus den Testergebnissen geht hervor, dass die Übertragung korrekt ist, zumindest wurden in einfachen Anwendungen keine Probleme festgestellt.

Fazit

In diesem Artikel haben wir das FatFS-Dateisystem auf STM32F4 portiert und einen einfachen Lese- und Schreibtest durchgeführt. Den Testergebnissen nach zu urteilen, gibt es keine Probleme beim Portierungsprozess von FatFS.

Bei der Portierung haben wir die Benutzerfreundlichkeit berücksichtigt, wenn mehrere Laufwerke gleichzeitig verfügbar sind. Die von uns definierte Festplatten-E/A-Funktion muss auf der Grundlage der tatsächlichen Hardware implementiert werden, und dann wird die von uns geschriebene Festplatten-E/A-Funktion in der vom System festgelegten Callback-Funktion aufgerufen.

Teilen auf:

Nach oben scrollen

Instant Quote