在實現如U盤文件讀寫,SD卡的文件讀寫等工作時,我們往往需要一個文件系統(tǒng)來支持我們的工作。特別在一些MCU應用中,文件系統(tǒng)的加入能明顯改善系統(tǒng)交互的友好性。在這一篇中,我們就來討論FatFS文件系統(tǒng)在STM32F4上的移植和應用。
1、準備工作
??在開始FatFS的移植之前我們需要做一些必要的準備工作。首先需要準備相應的硬件平臺,我們在這里使用的是STM32F407VET6的操作平臺。USB硬件相關的庫的移植工作也已完成。
??其次我們還需要準備FatFS的相關源碼,在這里我們使用最新的R0.14b版本,該文件可在網站下載。
??下載的源碼解壓后有兩個文件夾: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 | 可選的操作系統(tǒng)相關文件實例 |
??在這些文件中,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的幫助文檔中告訴了我們需要實現的函數有兩類:一類是磁盤設備控制相關的函數,主要是獲取設備狀態(tài)函數、初始化設備函數、讀取數據函數、寫入數據函數以及控制設備相關功能函數;二類是實時時鐘操作函數,主要是獲取當前時間函數。所以實現這6個函數就是移植的主要工作。
2.1、獲取設備狀態(tài)函數
??磁盤狀態(tài)檢測函數disk_status。用于檢測磁盤狀態(tài),在ff.c文件中會被調用。其函數原型如下:
??DSTATUS disk_status(BYTE drV);
??根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤狀態(tài)獲取函數如下:
/*用于USBH的狀態(tài)獲取函數*/
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、讀取數據
??讀扇區(qū)函數disk_read。用于實現對磁盤數據的讀取,根據具體的磁盤IO編寫,在ff.c文件中會被調用。其函數原型如下:
??DRESULT disk_read(BYTE drv,BYTE*buff,DWORD sector,BYTE.count);
??根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤數據讀取函數如下:
/*用于USBH的讀扇區(qū)函數*/
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、寫入數據
??寫扇區(qū)函數disk_write。用于實現對磁盤數據的寫入,根據具體的磁盤IO編寫,在ff.c文件中會被調用。其函數原型如下:
??DRESULT disk_write(BYTE drv,const BYTE*buff,DWORD sector,BYTE count);
??根據其原型定義以及我們USB大容量存儲設備的要求,我們可以實現磁盤數據寫入函數如下:
/*用于USBH的寫扇區(qū)函數*/
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。可以在此函數里編寫自己需要的功能代碼,比如獲得存儲媒介的大小、檢測存儲媒介的上電與否存儲媒介的扇區(qū)數等。如果是簡單的應用,也可以不用編寫。其函數原型如下:
??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大容量存儲設備的要求,我們可以實現磁盤狀態(tài)獲取函數如下:
/*讀取時鐘函數*/
DWORD get_fattime(void)
{
return 0;
}
??完成上述6個程序的編寫,移植工作也就基本完成了。大家可能會發(fā)現,我們實現的函數名似乎與原型函數不一樣,主要是考慮方便在多個存儲設備同時存在時進行操作,我們在目標函數中調用我們實現的函數就可以了。
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]; /* 讀文件緩沖器 */
/* 注冊文件系統(tǒng)對象到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盤上創(chuàng)建的文件,名為“STM32.TXT”,在上述源碼中,我們創(chuàng)建完文件后將其修改為打開與存在文件。創(chuàng)建的文件如下圖所示:
??向創(chuàng)建的STM32.TXT文件中寫入“This is STM32 working with FatFs!”,我們查看文件內容,結果如下:
??接著我們嘗試向已經存在的文件中追加內容。依然是STM32.TXT文件,我們操作完畢,查看其內容圖下:
??至此,我們完成了FatFS文件系統(tǒng)的移植與測試,從測試結果看,移植是正確的,至少在簡單應用下沒有發(fā)現問題。
4、移植小結
??在這篇中,我們移植了FatFS文件系統(tǒng),并進行了簡單的讀寫測試。從測試的結果來看,FatFS的一直是沒有問題的,至少驗證了在一般的讀寫操作方面是沒有問題的。
??在我們移植時,我們考慮到在同時有多種驅動器的情況下能夠方便的操作。我們定義的磁盤IO操作函數是需要根據實際硬件實現的,然后在系統(tǒng)指定的回調函數中調用我們編寫的磁盤IO函數。這樣就可以實現多個驅動器的操作,事實上FatFS給出的磁盤IO示例中也是這樣建議的。
-
移植
+關注
關注
1文章
379瀏覽量
28124 -
文件系統(tǒng)
+關注
關注
0文章
284瀏覽量
19904 -
STM32F4
+關注
關注
3文章
194瀏覽量
28046 -
FATFS
+關注
關注
0文章
44瀏覽量
18298
發(fā)布評論請先 登錄
相關推薦
評論