在實(shí)現(xiàn)如U盤文件讀寫(xiě),SD卡的文件讀寫(xiě)等工作時(shí),我們往往需要一個(gè)文件系統(tǒng)來(lái)支持我們的工作。特別在一些MCU應(yīng)用中,文件系統(tǒng)的加入能明顯改善系統(tǒng)交互的友好性。在這一篇中,我們就來(lái)討論FatFS文件系統(tǒng)在STM32F4上的移植和應(yīng)用。
1、準(zhǔn)備工作
??在開(kāi)始FatFS的移植之前我們需要做一些必要的準(zhǔn)備工作。首先需要準(zhǔn)備相應(yīng)的硬件平臺(tái),我們?cè)谶@里使用的是STM32F407VET6的操作平臺(tái)。USB硬件相關(guān)的庫(kù)的移植工作也已完成。
??其次我們還需要準(zhǔn)備FatFS的相關(guān)源碼,在這里我們使用最新的R0.14b版本,該文件可在網(wǎng)站下載。
??下載的源碼解壓后有兩個(gè)文件夾:document和source,其中document文件夾中是相關(guān)的文檔資料,與網(wǎng)站上的內(nèi)容一樣,在移植時(shí)可以查看這些文檔來(lái)工作。Source文件夾中則是源碼相關(guān)的文件,主要包括:
??在上圖所示的一系列文件中,00readme.txt文件有對(duì)各個(gè)文件的介紹,我們查看其內(nèi)容如下:
00readme.txt | readme文件 |
---|---|
00history.txt | 版本記錄文件 |
ff.c | FatFs模塊 |
ffconf.h | FatFs模塊的配置文件 |
ff.h | FatFs應(yīng)用模塊的頭文件 |
diskio.h | FatFs和磁盤IO模塊的頭文件 |
diskio.c | 一個(gè)將磁盤IO函數(shù)附加到FatFS的實(shí)例 |
ffunicode.c | Unicode編碼功能函數(shù) |
ffsystem.c | 可選的操作系統(tǒng)相關(guān)文件實(shí)例 |
??在這些文件中,ff.c和ff.h是核心文件。ffunicode.c是字符編碼,會(huì)根據(jù)配置文件的配置選擇編碼。ffsystem.c文件根據(jù)自己的需要決定。所以與具體的應(yīng)用平臺(tái)相關(guān)的,并需要我們來(lái)實(shí)現(xiàn)的文件是配置文件ffconf.h和磁盤操作文件diskio.h與diskio.c,這幾個(gè)文件也是我們移植的重點(diǎn)。
2、實(shí)現(xiàn)移植
??我們已經(jīng)完成了移植的準(zhǔn)備工作,接下來(lái)就來(lái)實(shí)現(xiàn)面向大容量U盤的應(yīng)用移植。前面我們已經(jīng)說(shuō)過(guò),移植需要處理的文件是配置文件ffconf.h和磁盤操作文件diskio.h與diskio.c。
??關(guān)于配置文件ffconf.h其實(shí)它本身有一個(gè)實(shí)例,我們只需要根據(jù)需要修改配置就好。這里我們需要修改的配置參數(shù)包括:
??所支持的編碼方式配置參數(shù)FF_CODE_PAGE,這個(gè)關(guān)系到文件編碼的問(wèn)題,我們將其配置為簡(jiǎn)體中文支持。
??邏輯驅(qū)動(dòng)器的數(shù)量配置參數(shù)FF_VOLUMES,F(xiàn)atFS可以同時(shí)應(yīng)用于多個(gè)驅(qū)動(dòng)器,所以我們需要根據(jù)實(shí)際情況配置驅(qū)動(dòng)器的數(shù)量。
??時(shí)間戳配置參數(shù)FF_FS_NORTC,我們大多時(shí)候并不需要記錄時(shí)間戳,所以在這里我們將其關(guān)閉。
??余下就是實(shí)現(xiàn)磁盤IO操作的相關(guān)函數(shù),在FatFS的幫助文檔中告訴了我們需要實(shí)現(xiàn)的函數(shù)有兩類:一類是磁盤設(shè)備控制相關(guān)的函數(shù),主要是獲取設(shè)備狀態(tài)函數(shù)、初始化設(shè)備函數(shù)、讀取數(shù)據(jù)函數(shù)、寫(xiě)入數(shù)據(jù)函數(shù)以及控制設(shè)備相關(guān)功能函數(shù);二類是實(shí)時(shí)時(shí)鐘操作函數(shù),主要是獲取當(dāng)前時(shí)間函數(shù)。所以實(shí)現(xiàn)這6個(gè)函數(shù)就是移植的主要工作。
2.1、獲取設(shè)備狀態(tài)函數(shù)
??磁盤狀態(tài)檢測(cè)函數(shù)disk_status。用于檢測(cè)磁盤狀態(tài),在ff.c文件中會(huì)被調(diào)用。其函數(shù)原型如下:
??DSTATUS disk_status(BYTE drV);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤狀態(tài)獲取函數(shù)如下:
/*用于USBH的狀態(tài)獲取函數(shù)*/
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、初始化設(shè)備函數(shù)
??存儲(chǔ)媒介初始化函數(shù)disk_initialize。用于對(duì)磁盤設(shè)備進(jìn)行初始化,在ff.c文件中會(huì)被調(diào)用。其函數(shù)原型如下:
??DSTATUS disk_initialize(BYTE drv);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤驅(qū)動(dòng)器初始化函數(shù),但這里我們其實(shí)不需要,因?yàn)樵赨SB HOST庫(kù)中已經(jīng)完成了初始化,所以直接返回正確就可以了。
/*用于USBH的初始化函數(shù)*/
static DSTATUS USBH_initialize(BYTE lun)
{
//USB HOST庫(kù)中已經(jīng)完成了初始化
return RES_OK;
}
2.3、讀取數(shù)據(jù)
??讀扇區(qū)函數(shù)disk_read。用于實(shí)現(xiàn)對(duì)磁盤數(shù)據(jù)的讀取,根據(jù)具體的磁盤IO編寫(xiě),在ff.c文件中會(huì)被調(diào)用。其函數(shù)原型如下:
??DRESULT disk_read(BYTE drv,BYTE*buff,DWORD sector,BYTE.count);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤數(shù)據(jù)讀取函數(shù)如下:
/*用于USBH的讀扇區(qū)函數(shù)*/
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、寫(xiě)入數(shù)據(jù)
??寫(xiě)扇區(qū)函數(shù)disk_write。用于實(shí)現(xiàn)對(duì)磁盤數(shù)據(jù)的寫(xiě)入,根據(jù)具體的磁盤IO編寫(xiě),在ff.c文件中會(huì)被調(diào)用。其函數(shù)原型如下:
??DRESULT disk_write(BYTE drv,const BYTE*buff,DWORD sector,BYTE count);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤數(shù)據(jù)寫(xiě)入函數(shù)如下:
/*用于USBH的寫(xiě)扇區(qū)函數(shù)*/
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、控制設(shè)備相關(guān)功能
??存儲(chǔ)媒介控制函數(shù)disk_ioctl。可以在此函數(shù)里編寫(xiě)自己需要的功能代碼,比如獲得存儲(chǔ)媒介的大小、檢測(cè)存儲(chǔ)媒介的上電與否存儲(chǔ)媒介的扇區(qū)數(shù)等。如果是簡(jiǎn)單的應(yīng)用,也可以不用編寫(xiě)。其函數(shù)原型如下:
??DRESULT disk_ioctl(BYTE drv,BYTE ctrl,VoiI*buff);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤設(shè)備控制相關(guān)功能函數(shù)如下:
/*USBH IO控制函數(shù) */
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、獲取當(dāng)前時(shí)間
??實(shí)時(shí)時(shí)鐘函數(shù)get_fattime。用于獲取當(dāng)前時(shí)間,返回一個(gè)32位無(wú)符號(hào)整數(shù),時(shí)鐘信息包含在這32位中。如果不使用時(shí)間戳,可以直接返回一個(gè)數(shù),如0。其函數(shù)原型如下:
??DWORD get_fattime(Void);
??根據(jù)其原型定義以及我們USB大容量存儲(chǔ)設(shè)備的要求,我們可以實(shí)現(xiàn)磁盤狀態(tài)獲取函數(shù)如下:
/*讀取時(shí)鐘函數(shù)*/
DWORD get_fattime(void)
{
return 0;
}
??完成上述6個(gè)程序的編寫(xiě),移植工作也就基本完成了。大家可能會(huì)發(fā)現(xiàn),我們實(shí)現(xiàn)的函數(shù)名似乎與原型函數(shù)不一樣,主要是考慮方便在多個(gè)存儲(chǔ)設(shè)備同時(shí)存在時(shí)進(jìn)行操作,我們?cè)谀繕?biāo)函數(shù)中調(diào)用我們實(shí)現(xiàn)的函數(shù)就可以了。
3、應(yīng)用測(cè)試
??我們完成了FatFS的移植,現(xiàn)在來(lái)驗(yàn)證移植的是否正確。為此,我們來(lái)編寫(xiě)一個(gè)應(yīng)用,向U盤中寫(xiě)入數(shù)據(jù)到文件以及讀取文件的數(shù)據(jù)等。
/* USB HOST MSC操作函數(shù),這部分功能根據(jù)需求設(shè)定 */
static void MSC_Application(void)
{
FRESULT res; /* FatFs函數(shù)返回值 */
uint32_t byteswritten, bytesread; /* 文件讀寫(xiě)的數(shù)量 */
uint8_t wtext[] = "This is STM32 working with FatFs!"; /* 寫(xiě)文件緩沖器 */
uint8_t wtext2[] = "這是一個(gè)FatFs讀寫(xiě)的例子!"; /* 寫(xiě)文件緩沖器 */
uint8_t wtext3[] = "這是一個(gè)向文件追加數(shù)據(jù)的測(cè)試!"; /* 寫(xiě)文件緩沖器 */
uint8_t rtext[100]; /* 讀文件緩沖器 */
/* 注冊(cè)文件系統(tǒng)對(duì)象到FatFs模塊 */
if(f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0) != FR_OK)
{
/* 錯(cuò)誤處理 */
Error_Handler();
}
else
{
/* 打開(kāi)一個(gè)文件 */
if(f_open(&USBHFile, "STM32.TXT", FA_OPEN_EXISTING | FA_WRITE) != FR_OK)
{
/* 錯(cuò)誤處理 */
Error_Handler();
}
else
{
res=f_lseek(&USBHFile,f_size(&USBHFile)); //將指針指向文件末
//res=f_lseek(&USBHFile,100); //將指針指向文件末
/* 寫(xiě)數(shù)據(jù)到文件 */
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))
{
/* 錯(cuò)誤處理 */
Error_Handler();
}
else
{
/* 關(guān)閉文件 */
f_close(&USBHFile);
/* 打開(kāi)文件讀 */
if(f_open(&USBHFile, "STM32.TXT", FA_READ) != FR_OK)
{
/* 錯(cuò)誤處理 */
Error_Handler();
}
else
{
/* 從文件讀數(shù)據(jù) */
res = f_read(&USBHFile, rtext, sizeof(rtext), (void *)&bytesread);
if((bytesread == 0) || (res != FR_OK))
{
/* 錯(cuò)誤處理 */
Error_Handler();
}
else
{
/* 關(guān)閉文件 */
f_close(&USBHFile);
/* 比較讀和寫(xiě)的數(shù)據(jù) */
if((bytesread != byteswritten))
{
/* 錯(cuò)誤處理*/
Error_Handler();
}
else
{
/* 無(wú)錯(cuò)誤 */
}
}
}
}
}
}
FATFS_UnLinkDriver(USBHPath);
}
??我們先在U盤上創(chuàng)建的文件,名為“STM32.TXT”,在上述源碼中,我們創(chuàng)建完文件后將其修改為打開(kāi)與存在文件。創(chuàng)建的文件如下圖所示:
??向創(chuàng)建的STM32.TXT文件中寫(xiě)入“This is STM32 working with FatFs!”,我們查看文件內(nèi)容,結(jié)果如下:
??接著我們嘗試向已經(jīng)存在的文件中追加內(nèi)容。依然是STM32.TXT文件,我們操作完畢,查看其內(nèi)容圖下:
??至此,我們完成了FatFS文件系統(tǒng)的移植與測(cè)試,從測(cè)試結(jié)果看,移植是正確的,至少在簡(jiǎn)單應(yīng)用下沒(méi)有發(fā)現(xiàn)問(wèn)題。
4、移植小結(jié)
??在這篇中,我們移植了FatFS文件系統(tǒng),并進(jìn)行了簡(jiǎn)單的讀寫(xiě)測(cè)試。從測(cè)試的結(jié)果來(lái)看,F(xiàn)atFS的一直是沒(méi)有問(wèn)題的,至少驗(yàn)證了在一般的讀寫(xiě)操作方面是沒(méi)有問(wèn)題的。
??在我們移植時(shí),我們考慮到在同時(shí)有多種驅(qū)動(dòng)器的情況下能夠方便的操作。我們定義的磁盤IO操作函數(shù)是需要根據(jù)實(shí)際硬件實(shí)現(xiàn)的,然后在系統(tǒng)指定的回調(diào)函數(shù)中調(diào)用我們編寫(xiě)的磁盤IO函數(shù)。這樣就可以實(shí)現(xiàn)多個(gè)驅(qū)動(dòng)器的操作,事實(shí)上FatFS給出的磁盤IO示例中也是這樣建議的。
-
移植
+關(guān)注
關(guān)注
1文章
376瀏覽量
28096 -
文件系統(tǒng)
+關(guān)注
關(guān)注
0文章
284瀏覽量
19871 -
STM32F4
+關(guān)注
關(guān)注
3文章
194瀏覽量
27974 -
FATFS
+關(guān)注
關(guān)注
0文章
44瀏覽量
18253
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論