文件系統應用筆記之一:FatFS在STM32F4上的移植


  在實現如U盤文件讀寫,SD卡的文件讀寫等工作時,我們往往需要一個文件系統來支持我們的工作。特別在一些MCU應用中,文件系統的加入能明顯改善系統交互的友好性。在這一篇中,我們就來討論FatFS文件系統在STM32F4上的移植和應用。

1、准備工作

  在開始FatFS的移植之前我們需要做一些必要的准備工作。首先需要准備相應的硬件平台,我們在這里使用的是STM32F407VET6的操作平台。USB硬件相關的庫的移植工作也已完成。

  其次我們還需要准備FatFS的相關源碼,在這里我們使用最新的R0.14b版本,該文件可在網站下載:

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

  下載的源碼解壓后有兩個文件夾:document和source,其中document文件夾中是相關的文檔資料,與網站上的內容一樣,在移植時可以查看這些文檔來工作。Source文件夾中則是源碼相關的文件,主要包括:

  在上圖所示的一系列文件中,00readme.txt文件有對各個文件的介紹,我們查看其內容如下:

00readme.txt readme文件
00history.txt 版本記錄文件
ff.c FatFs模塊
ffconf.h FatFs模塊的配置文件
ff.h FatFs應用模塊的頭文件
diskio.h FatFs和磁盤IO模塊的頭文件
diskio.c 一個將磁盤IO函數附加到FatFS的實例
ffunicode.c Unicode編碼功能函數
ffsystem.c 可選的操作系統相關文件實例

  在這些文件中,ff.c和ff.h是核心文件。ffunicode.c是字符編碼,會根據配置文件的配置選擇編碼。ffsystem.c文件根據自己的需要決定。所以與具體的應用平台相關的,並需要我們來實現的文件是配置文件ffconf.h和磁盤操作文件diskio.h與diskio.c,這幾個文件也是我們移植的重點。

2、實現移植

  我們已經完成了移植的准備工作,接下來就來實現面向大容量U盤的應用移植。前面我們已經說過,移植需要處理的文件是配置文件ffconf.h和磁盤操作文件diskio.h與diskio.c。

  關於配置文件ffconf.h其實它本身有一個實例,我們只需要根據需要修改配置就好。這里我們需要修改的配置參數包括:

  所支持的編碼方式配置參數FF_CODE_PAGE,這個關系到文件編碼的問題,我們將其配置為簡體中文支持。

  邏輯驅動器的數量配置參數FF_VOLUMES,FatFS可以同時應用於多個驅動器,所以我們需要根據實際情況配置驅動器的數量。

  時間戳配置參數FF_FS_NORTC,我們大多時候並不需要記錄時間戳,所以在這里我們將其關閉。

  余下就是實現磁盤IO操作的相關函數,在FatFS的幫助文檔中告訴了我們需要實現的函數有兩類:一類是磁盤設備控制相關的函數,主要是獲取設備狀態函數、初始化設備函數、讀取數據函數、寫入數據函數以及控制設備相關功能函數;二類是實時時鍾操作函數,主要是獲取當前時間函數。所以實現這6個函數就是移植的主要工作。

2.1、獲取設備狀態函數

  磁盤狀態檢測函數disk_status。用於檢測磁盤狀態,在ff.c文件中會被調用。其函數原型如下:

  DSTATUS disk_status(BYTE drV);

  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤狀態獲取函數如下:

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

2.2、初始化設備函數

  存儲媒介初始化函數disk_initialize。用於對磁盤設備進行初始化,在ff.c文件中會被調用。其函數原型如下:

  DSTATUS disk_initialize(BYTE drv);

  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤驅動器初始化函數,但這里我們其實不需要,因為在USB HOST庫中已經完成了初始化,所以直接返回正確就可以了。

/*用於USBH的初始化函數*/
static DSTATUS USBH_initialize(BYTE lun)
{
  //USB HOST庫中已經完成了初始化
 return RES_OK;
}

2.3、讀取數據

  讀扇區函數disk_read。用於實現對磁盤數據的讀取,根據具體的磁盤IO編寫,在ff.c文件中會被調用。其函數原型如下:

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

  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤數據讀取函數如下:

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

2.4、寫入數據

  寫扇區函數disk_write。用於實現對磁盤數據的寫入,根據具體的磁盤IO編寫,在ff.c文件中會被調用。其函數原型如下:

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

  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤數據寫入函數如下:

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

2.5、控制設備相關功能

  存儲媒介控制函數disk_ioctl。可以在此函數里編寫自己需要的功能代碼,比如獲得存儲媒介的大小、檢測存儲媒介的上電與否存儲媒介的扇區數等。如果是簡單的應用,也可以不用編寫。其函數原型如下:

  DRESULT disk_ioctl(BYTE drv,BYTE ctrl,VoiI*buff);
  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤設備控制相關功能函數如下:

/*USBH IO控制函數 */
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;
}

2.6、獲取當前時間

  實時時鍾函數get_fattime。用於獲取當前時間,返回一個32位無符號整數,時鍾信息包含在這32位中。如果不使用時間戳,可以直接返回一個數,如0。其函數原型如下:

  DWORD get_fattime(Void);

  根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤狀態獲取函數如下:

/*讀取時鍾函數*/
DWORD get_fattime(void)
{
 
 return 0;
 
}

  完成上述6個程序的編寫,移植工作也就基本完成了。大家可能會發現,我們實現的函數名似乎與原型函數不一樣,主要是考慮方便在多個存儲設備同時存在時進行操作,我們在目標函數中調用我們實現的函數就可以了。

3、應用測試

  我們完成了FatFS的移植,現在來驗證移植的是否正確。為此,我們來編寫一個應用,向U盤中寫入數據到文件以及讀取文件的數據等。

/* USB HOST MSC操作函數,這部分功能根據需求設定 */
static void MSC_Application(void)
{
  FRESULT res;                      /* FatFs函數返回值 */
  uint32_t byteswritten, bytesread;           /* 文件讀寫的數量 */
  uint8_t wtext[] = "This is STM32 working with FatFs!"; /* 寫文件緩沖器 */
  uint8_t wtext2[] = "這是一個FatFs讀寫的例子!"; /* 寫文件緩沖器 */
  uint8_t wtext3[] = "這是一個向文件追加數據的測試!"; /* 寫文件緩沖器 */
  uint8_t rtext[100];                  /* 讀文件緩沖器 */
  
  /* 注冊文件系統對象到FatFs模塊 */
  if(f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0) != FR_OK)
  {
    /* 錯誤處理 */
    Error_Handler();
  }
  else
  {
    /* 打開一個文件 */
    if(f_open(&USBHFile, "STM32.TXT", FA_OPEN_EXISTING | FA_WRITE) != FR_OK) 
    {
      /* 錯誤處理 */
      Error_Handler();
    }
    else
    {
      res=f_lseek(&USBHFile,f_size(&USBHFile));  //將指針指向文件末
      //res=f_lseek(&USBHFile,100);  //將指針指向文件末
      /* 寫數據到文件 */
      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_Handler();
      }
      else
      {
        /* 關閉文件 */
        f_close(&USBHFile);
        
        /* 打開文件讀 */
        if(f_open(&USBHFile, "STM32.TXT", FA_READ) != FR_OK)
        {
          /* 錯誤處理 */
          Error_Handler();
        }
        else
        {
          /* 從文件讀數據 */
          res = f_read(&USBHFile, rtext, sizeof(rtext), (void *)&bytesread);
          
          if((bytesread == 0) || (res != FR_OK))
          {
            /* 錯誤處理 */
            Error_Handler();
          }
          else
          {
            /* 關閉文件 */
            f_close(&USBHFile);
            
            /* 比較讀和寫的數據 */
            if((bytesread != byteswritten))
            {         
              /* 錯誤處理*/
              Error_Handler();
            }
            else
            {
              /* 無錯誤 */
              
            }
          }
        }
      }
    }
  }
  
  FATFS_UnLinkDriver(USBHPath);
}

  我們先在U盤上創建的文件,名為“STM32.TXT”,在上述源碼中,我們創建完文件后將其修改為打開與存在文件。創建的文件如下圖所示:

  向創建的STM32.TXT文件中寫入“This is STM32 working with FatFs!”,我們查看文件內容,結果如下:

  接着我們嘗試向已經存在的文件中追加內容。依然是STM32.TXT文件,我們操作完畢,查看其內容圖下:

  至此,我們完成了FatFS文件系統的移植與測試,從測試結果看,移植是正確的,至少在簡單應用下沒有發現問題。

4、移植小結

  在這篇中,我們移植了FatFS文件系統,並進行了簡單的讀寫測試。從測試的結果來看,FatFS的一直是沒有問題的,至少驗證了在一般的讀寫操作方面是沒有問題的。

  在我們移植時,我們考慮到在同時有多種驅動器的情況下能夠方便的操作。我們定義的磁盤IO操作函數是需要根據實際硬件實現的,然后在系統指定的回調函數中調用我們編寫的磁盤IO函數。這樣就可以實現多個驅動器的操作,事實上FatFS給出的磁盤IO示例中也是這樣建議的。

歡迎關注:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM