24.4
FatFs文件系統(tǒng)移植實驗
24.4.1
硬件設計及FSP
FatFs屬于軟件組件,不需要附帶其他硬件電路。我們使用串行Flash芯片作為物理存儲設備,其硬件電路在上一章已經(jīng)做了分析,這里就直接使用。
24.4.2
FatFs移植步驟概述
基本步驟:
實現(xiàn)底層驅動接口
修改配置文件
移植FatFs之前我們先通過FatFs的程序結構圖了解FatFs在程序中的關系網(wǎng)絡,見圖FatFs程序結構圖。

用戶應用程序需要由用戶編寫,想實現(xiàn)什么功能就編寫什么的程序,一般我們只用到f_mount()、f_open()、f_write()、f_read()就可以實現(xiàn)文件的讀寫操作。
FatFs組件是FatFs的主體,文件都在源碼src文件夾中,其中ff.c、ff.h、ffsystem.c以及ffunicode.c4個文件我們不需要改動,只需要修改ffconf.h和diskio.c/.h3個文件。
底層設備輸入輸出要求實現(xiàn)存儲設備的讀寫操作函數(shù)、存儲設備信息獲取函數(shù)等等。我們使用串行Flash芯片作為物理設備,在上一章節(jié)已經(jīng)編寫好了串行Flash芯片的驅動程序,這里我們就直接使用。
24.4.2.1
實現(xiàn)底層驅動接口
FatFs文件系統(tǒng)與底層介質(zhì)的驅動分離開來,對底層介質(zhì)的操作都要交給用戶去實現(xiàn),它僅僅是提供了一個函數(shù)接口而已。表FatFs移植需要用戶支持函數(shù)為FatFs移植時用戶必須支持的函數(shù)。
通過表FatFs移植需要用戶支持函數(shù)我們可以清晰知道很多函數(shù)是在一定條件下才需要添加的,只有前三個函數(shù)是必須添加的。我們完全可以根據(jù)實際需求選擇所需用到的函數(shù)。
前三個函數(shù)是實現(xiàn)讀文件最基本需求。接下來三個函數(shù)是實現(xiàn)創(chuàng)建文件、修改文件需要的。為實現(xiàn)格式化功能,需要在disk_ioctl添加兩個獲取物理設備信息選項。我們一般只要實現(xiàn)前面六個函數(shù)就可以了,已經(jīng)足夠滿足大部分功能。
為支持簡體中文長文件名稱需要添加ff_uni2oem、ff_oem2uni和ff_wtoupper 函數(shù),實際這三個已經(jīng)在ffunicode.c文件中實現(xiàn),我們只要直接把ffunicode.c文件添加到工程中就可以。

底層設備驅動函數(shù)是存放在diskio.c文件,我們的目的就是把diskio.c中的函數(shù)接口與串行Flash芯片驅動連接起來。總共有五個函數(shù),分別為設備狀態(tài)獲取(disk_status)、設備初始化(disk_initialize)、扇區(qū)讀取(disk_read)、扇區(qū)寫入(disk_write)、其他控制(disk_ioctl)。
接下來,我們對每個函數(shù)結合串行Flash芯片驅動做詳細講解。
24.4.2.2
修改配置文件
ffconf.h文件是FatFs的配置文件。
下面是ffconf.h文件中,需要修改的部分,只把需要修改的部分放出來:
列表1:
代碼清單24?1 FatFs的配置文件:ffconf.h
左右滑動查看完整內(nèi)容
/********************/ /* 下面是經(jīng)過修改部分 */ /********************/ #define FF_USE_MKFS 1 //此選項切換是否啟用 f_mkfs() 函數(shù),用于格式化 Flash、SD卡等 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ #define FF_CODE_PAGE 936 //此選項指定使用的OEM代碼頁 /* This option specifies the OEM code page to be used on the target system. / Incorrect code page setting can cause a file open failure. / / 437 - U.S. / 720 - Arabic / 737 - Greek / 771 - KBL / 775 - Baltic / 850 - Latin 1 / 852 - Latin 2 / 855 - Cyrillic / 857 - Turkish / 860 - Portuguese / 861 - Icelandic / 862 - Hebrew / 863 - Canadian French / 864 - Arabic / 865 - Nordic / 866 - Russian / 869 - Greek 2 / 932 - Japanese (DBCS) / 936 - Simplified Chinese (DBCS) / 949 - Korean (DBCS) / 950 - Traditional Chinese (DBCS) / 0 - Include all code pages above and configured by f_setcp() */ #define FF_USE_LFN 2 //此選項切換對長文件名的支持 #define FF_MAX_LFN 255 //設置長文件名的最長長度 /* The FF_USE_LFN switches the support for LFN (long file name). / / 0: Disable LFN. FF_MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. #define FF_LFN_UNICODE 2 //此選項設置是否啟用 Unicode 字符編碼 /* This option switches the character encoding on the API when LFN is enabled. / / 0: ANSI/OEM in current CP (TCHAR = char) / 1: Unicode in UTF-16 (TCHAR = WCHAR) / 2: Unicode in UTF-8 (TCHAR = char) / 3: Unicode in UTF-32 (TCHAR = DWORD) / / Also behavior of string I/O functions will be affected by this option. / When LFN is not enabled, this option has no effect. */ #define FF_VOLUMES 2 //要使用的卷(邏輯驅動器)的數(shù)量。范圍(1-10) /* Number of volumes (logical drives) to be used. (1-10) */ #define FF_MIN_SS 512 #define FF_MAX_SS 4096 //這組選項配置支持的扇區(qū)大小范圍 /* This set of options configures the range of sector size to be supported. (512, / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and / harddisk, but a larger value may be required for on-board flash memory and some / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ /* 若 FF_MIN_SS != FF_MAX_SS。則需要在 disk_ioctl() 中指定所需操作的設備的扇區(qū)大小 * 在 case GET_SECTOR_SIZE 項中指定 */ #define FF_FS_NORTC 1 //設置為1關閉時間戳 啟用時間戳功能需要RTC #define FF_NORTC_MON 1 #define FF_NORTC_MDAY 1 #define FF_NORTC_YEAR 2022 /* The option FF_FS_NORTC switches timestamp feature. If the system does not have / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the / timestamp feature. Every object modified by FatFs will have a fixed timestamp / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be / added to the project to read current time form real-time clock. FF_NORTC_MON, / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
24.4.3.路徑名格式說明
在 FatFs 的 API 函數(shù)中,經(jīng)常能看到 “const TCHAR* path” 這樣的函數(shù)參數(shù),比如:
FRESULT f_mount (
FATFS* fs, /* 指向要注冊的文件系統(tǒng)對象的指針(NULL:卸載) */
const TCHAR* path, /* 要裝載/卸載的邏輯驅動器號 */
BYTE opt /* 裝載選項:0=不裝載(延遲裝載),1=立即裝載 */
)
FRESULT f_open (
FIL* fp, /* 指向空白文件對象的指針 */
const TCHAR* path, /* 指向文件名的指針 */
BYTE mode /* 訪問模式和打開模式標志 */
)
這就要求我們要知道 path 這個參數(shù)的可輸入值是什么,也就是我們要了解FatFs的文件命名規(guī)范。
24.4.3.1.文件命名格式
FatFs模塊上的路徑名格式與DOS/Windows的文件名規(guī)范類似,如下所示:
[drive#:][/]"directory 目錄"/"file 文件"
FatFs 模塊支持長文件名(LFN) 和 8.3 格式的文件名 (SFN)。長文件名(LFN) 可在 FF_USE_LFN >= 1 時使用。 子目錄用 \ 或 / 分隔,方式與DOS/Windows API相同。 重復的分隔符和終止分隔符將被忽略,例如:“//animal///cat/”。 唯一不同的是,F(xiàn)atFs中指定邏輯驅動器(FAT卷)的標題驅動器前綴是數(shù)字(0-9)+冒號,而在DOS/Windows中是字母(A-Z)+冒號。 邏輯驅動器編號是指定要訪問的卷的標識符。 如果省略驅動器前綴,則假定邏輯驅動器編號為默認驅動器。
注解
LFN 和 SFN
8.3 命名規(guī)則,又稱短文件名(Short File Name,SFN)是一種限制文件名長度的方法,這在DOS和Windows 95及Windows NT 3.51以前的Microsoft Windows版本中,在FAT文件系統(tǒng)中的常用方法。
長文件名,(Long file name,LFN)也指長文件名支持。在舊版本的DOS操作系統(tǒng)下,因為文件名稱有8.3格式的限制,凡文件主檔名超過8字節(jié)或擴展名超過3字節(jié)的文件名,都被稱為“長文件名”,在Windows下正常的文件名置換于DOS(或“命令提示字符”)環(huán)境下則可能無法完整顯示,如“Program files”資料夾可能會顯示成其對應的8.3文件名“PROGRA~1”。
24.4.3.2.FF_FS_RPATH
在默認配置 (FF_FS_RPATH==0) 中, 是沒有當前目錄的概念的。 卷上的每個對象始終以從根目錄開始的完整路徑名指定。 不可以使用點目錄名稱(“.”,“..”)。標題分隔符被忽略, 它可以存在或省略。默認驅動器固定為驅動器0。
啟用相對路徑功能時(FF_FS_RPATH>=1),如果存在分隔符, 則從根目錄跟隨指定的路徑。如果沒有分隔符,則從默認驅動器的當前目錄開始。 路徑名也允許使用點目錄名。 當前目錄由f_chdir函數(shù)設置,默認驅動器為f_chdrive函數(shù)設置的當前驅動器。

此外,驅動器前綴可以采用預定義的任意字符串。 當選項FF_STR_VOLUME_ID == 1 時,也可以將任意字符串卷 ID 用作驅動器前綴。 例如 “flash:file1.txt”、“ram:temp.dat” 或 “sd:” 。 如果 srting 與任何卷 ID 都不匹配,則該函數(shù)將失敗并返回 FR_INVALID_DRIVE。
當FF_STR_VOLUME_ID == 2 時,可以使用 Unix 樣式的驅動器前綴。 例如 “/flash/file1.txt”、“/ram/temp.dat” 或 “/sd” 。
FF_STR_VOLUME_ID 和 FF_VOLUME_STRS 宏
#define FF_STR_VOLUME_ID 0 //FF_STR_VOLUME_ID開關支持任意字符串形式的卷ID。 #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each / logical drives. Number of items must not be less than FF_VOLUMES. Valid / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is / not defined, a user defined volume string table needs to be defined as: / / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... */
FF_STR_VOLUME_ID: 此選項切換對字符串卷 ID 的支持。
FF_STR_VOLUME_ID 為不同數(shù)值時,對應的可選項| 數(shù)值 | 描述 | 示例 |
|---|---|---|
| 0 | 只能使用數(shù)字 ID 中的 DOS/Windows 樣式驅動器前綴。 | 1:/filename |
| 1 | 還可以使用字符串 ID 中的 DOS/Windows 樣式驅動器前綴。 | flash:/filename |
| 2 | 也可以使用字符串ID中的Unix樣式驅動器前綴。 | /flash/filename |
FF_VOLUME_STRS: 此選項定義每個邏輯驅動器的卷 ID 字符串。項目數(shù)量不得少于FF_VOLUMES。 卷 ID 字符串的有效字符是 A-Z、a-z 和 0-9,不區(qū)分大小寫。 如果FF_STR_VOLUME_ID == 0,則此選項不起作用。 如果 FF_STR_VOLUME_ID >= 1 并且未定義此選項, 則需要定義用戶定義的卷字符串表,如下所示。不應動態(tài)修改該表。
用戶定義卷ID字符串
/* 0: ~ 3: 的用戶定義卷ID字符串: */
const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb"};
這里我們并沒有用到任意字符串形式的卷ID,就沒有開啟 FF_STR_VOLUME_ID 宏。
24.4.4.接口函數(shù)
主要的接口文件都在 diskio.c 文件中了,我們只需要根據(jù)函數(shù)所提供的輸入?yún)?shù)和返回參數(shù), 來實現(xiàn)函數(shù)的功能,提供給FatFs調(diào)用就好了。
FatFs文件系統(tǒng)與存儲設備的連接函數(shù)在diskio.c文件中,主要有5個函數(shù)需要我們編寫的。
宏定義和存儲設備狀態(tài)獲取函數(shù)
代碼清單: 24_2 disk_ioctl 函數(shù)
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case DEV_FLASH :
QSPI_Flash_WaitForWriteEnd(); //等待Flash芯片內(nèi)部操作完成
stat = RES_OK;
return stat;
}
return STA_NOINIT;
}
存儲設備初始化函數(shù)
代碼清單: 24_3 disk_ioctl 函數(shù)
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case DEV_FLASH :
QSPI_Flash_Init();
stat = RES_OK;
return stat;
}
return STA_NOINIT;
}
存儲設備數(shù)據(jù)讀取函數(shù)
代碼清單: 24_4 disk_ioctl 函數(shù)
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
DRESULT res;
switch (pdrv) {
case DEV_FLASH :
// translate the arguments here
QSPI_Flash_BufferRead(buff, sector<<12, count<<12); //1 sector == 4096 bytes
res = RES_OK;
return res;
}
return RES_PARERR;
}
存儲設備數(shù)據(jù)寫入函數(shù)
代碼清單: 24_5 disk_ioctl 函數(shù)
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
uint32_t write_addr;
switch (pdrv) {
case DEV_FLASH :
write_addr = sector << 12;
QSPI_Flash_SectorErase(write_addr);
QSPI_Flash_BufferWrite(buff, write_addr, count<<12);
res = RES_OK;
return res;
}
return RES_PARERR;
}
IO控制函數(shù)
代碼清單: 24_6 disk_ioctl 函數(shù)
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch (pdrv) {
case DEV_FLASH :
switch (cmd) {
case GET_SECTOR_COUNT: /* 扇區(qū)數(shù)量:1024*4096/1024/1024 = 4(MB) */
*(DWORD *)buff = 1024;
break;
case GET_SECTOR_SIZE: /* 扇區(qū)大小 */
*(WORD *)buff = 4096;
break;
case GET_BLOCK_SIZE: /* 同時擦除扇區(qū)個數(shù) */
*(DWORD *)buff = 1;
break;
}
res = RES_OK;
return res;
}
return RES_PARERR;
}
get_fattime函數(shù)
代碼清單: 24_7 disk_ioctl 函數(shù)
DWORD get_fattime(void) {
/* 返回當前時間戳 */
return (DWORD)(2022 - 80) << 25 | /* Year */
(DWORD)(1 + 1) << 21 | /* Month */
(DWORD)1 << 16 | /* Mday */
(DWORD)1 << 11 | /* Hour */
(DWORD)1 << 5 | /* Min */
(DWORD)1 >> 1; /* Sec */
}
由于之前在配置文件關閉了 RTC 和時間戳功能,因此其實不需要實現(xiàn)這個返回當前時間戳函數(shù)。
由 FF_FS_NORTC 決定是否啟用,F(xiàn)F_FS_NORTC設置為1以禁用時間戳功能。
24.4.5.FatFs 基本 API 函數(shù)說明
更多關于 FatFs 接口函數(shù)的說明請看官方的說明:http://elm-chan.org/fsw/ff/00index_e.html
24.4.5.1.f_mount
f_mount 函數(shù)原型如下:
FRESULT f_mount ( FATFS* fs, /* [IN] Filesystem object */ const TCHAR* path, /* [IN] Logical drive number */ BYTE opt /* [IN] Initialization option */ );
FatFs 需要每個邏輯驅動器(FAT 卷)的工作區(qū)域(文件系統(tǒng)對象)。 在執(zhí)行任何文件/目錄操作之前,需要使用邏輯驅動器的f_mount函數(shù)注冊文件系統(tǒng)對象。 完成此過程后,文件/目錄 API 函數(shù)已準備好工作。 某些卷管理功能(f_mkfs、f_fdisk和f_setcp)不需要文件系統(tǒng)對象。
f_mount函數(shù)將文件系統(tǒng)對象注冊/注銷到 FatFs 模塊,如下所示:
確定由 path 指定的邏輯驅動器。
清除并注銷卷的已注冊工作區(qū)(如果存在)。
如果 fs 不為 NULL,則清除新工作區(qū)并將其注冊到卷中。
如果指定了強制裝入,則對卷執(zhí)行卷裝入過程。
opt:安裝選項。0:現(xiàn)在不掛載(要在第一次訪問卷時掛載),1:強制掛載卷以檢查它是否準備好工作。
24.4.5.2.f_mkfs
f_mkfs函數(shù)原型如下:
FRESULT f_mkfs ( const TCHAR* path, /* [IN] Logical drive number */ const MKFS_PARM* opt,/* [IN] Format options */ void* work, /* [-] Working buffer */ UINT len /* [IN] Size of working buffer */ );
簇(cluster): 由于扇區(qū)的空間比較小且數(shù)目眾多,在尋址時比較困難, 所以操作系統(tǒng)就將多個的扇區(qū)組合在一起,形成一個更大的單位, 再對這個單位進行整體的操作。也可以理解為文件的磁盤空間分配單位。 當簇的大小為 32768 字節(jié)時,大小為 100 字節(jié)的文件將占用 32768 字節(jié)的磁盤空間。 隨著簇大小的增加,磁盤使用的空間效率會變得很低,但與此同時,讀/寫效率也會提高。 因此,簇的大小是空間效率和讀/寫效率之間的權衡。
opt:若為NULL,則啟用默認參數(shù)
f_mkfs opt 默認參數(shù)
static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
由work來指向用于格式化過程的工作緩沖區(qū)的指針。
由len來確定工作緩沖區(qū)的大小(以字節(jié)為單位)。至少需要FF_MAX_SS。
提示
當 FF_FS_READONLY == 0 且 FF_USE_MKFS == 1 時可用。
24.4.5.3.f_setlabel
f_setlabel 函數(shù)原型如下:
FRESULT f_setlabel ( const TCHAR* label /* [IN] Volume label to be set */ );
當字符串具有驅動器前綴時,卷標將設置為驅動器前綴指定的卷。 Unix 樣式的卷 ID 不能用于指定卷。 如果未指定驅動器號,則卷標將設置為默認驅動器。 如果給定卷標的長度為零,則將刪除卷上的卷標。卷標的格式如下所示:
在 FAT 卷上轉換 OEM 代碼頁時最多 11 個字節(jié)。 在 exFAT 卷中最多 11 個字符。 FAT 卷允許的字符數(shù)為:SFN 允許的字符不包括點。低寫字符向上轉換。 exFAT 卷允許的字符為:LFN 允許的字符包括點。將保留小寫字符。 空格可以嵌入卷標中的任何位置。尾隨空格在 FAT 音量處被截斷。
提示
當FF_FS_READONLY == 0 且FF_USE_LABEL == 1 時可用。
24.4.5.4.f_open
f_open 函數(shù)原型如下:
FRESULT f_open ( FIL* fp, /* [OUT] Pointer to the file object structure */ const TCHAR* path, /* [IN] File name */ BYTE mode /* [IN] Mode flags */ );
打開一個文件并創(chuàng)建一個文件對象。 完成對文件的訪問后應該使用f_close函數(shù)將其關閉。 如果在關機、移出介質(zhì)或重新裝入之前對文件進行了任何更改且未調(diào)用f_close函數(shù)將其關閉,則文件可能會 collapsed (損壞)。
可選mode選項:

提示
當FF_FS_READONLY == 1 時,只有FA_READ和FA_OPEN_EXISTING可用于模式標志。
24.4.5.5.f_close
f_close 函數(shù)原型如下:
FRESULT f_close ( FIL* fp /* [IN] Pointer to the file object */ );
關閉打開的文件對象。如果文件已更改,則文件的緩存信息將寫回卷。 函數(shù)成功后,文件對象不再有效,可以丟棄。
請注意,如果文件對象處于只讀模式且未啟用FF_FS_LOCK, 則也可以在不執(zhí)行f_close函數(shù)的情況下丟棄該文件對象。 但是,不建議這樣做。
24.4.5.6.f_write
f_write 函數(shù)原型如下:
FRESULT f_write ( FIL* fp, /* [IN] Pointer to the file object structure */ const void* buff, /* [IN] Pointer to the data to be written */ UINT btw, /* [IN] Number of bytes to write */ UINT* bw /* [OUT] Pointer to the variable to return number of bytes written */ );
該函數(shù)開始在讀/寫指針指向的文件偏移處將數(shù)據(jù)寫入文件。 讀/寫指針隨著寫入的字節(jié)數(shù)而前進。 功能成功后,應檢查 *bw 以檢測磁盤已滿。 如果 *bw < btw,則表示卷在寫入操作期間已滿。 該函數(shù)在卷已滿或接近滿時可能需要一段時間。
提示
FF_FS_READONLY == 0 時可用。
24.4.5.7.f_read
f_read 函數(shù)原型如下:
FRESULT f_read ( FIL* fp, /* [IN] File object */ void* buff, /* [OUT] Buffer to store read data */ UINT btr, /* [IN] Number of bytes to read */ UINT* br /* [OUT] Number of bytes read */ );
該函數(shù)開始在讀/寫指針指向的文件偏移處從文件中讀取數(shù)據(jù)。 讀/寫指針隨著讀取的字節(jié)數(shù)而前進。 函數(shù)成功后,應檢查 *br 以檢測文件的末尾。 如果 *br < btr,則表示在讀取操作期間讀/寫指針命中文件。
24.4.6.軟件設計
與FatFs文件系統(tǒng)使用相關的變量定義如下:
代碼清單: 24_8 hal_entry.c:全局變量
FATFS fs; /* FatFs文件系統(tǒng)對象 */
FIL fnew; /* 文件對象 */
UINT fnum; /* 文件成功讀寫數(shù)量 */
FRESULT res_flash; /* 文件操作結果 */
BYTE fileReadBuffer[1024]; /* 讀緩沖區(qū) */
BYTE fileWriteBuffer[] = /* 寫緩沖區(qū) */
"感謝您選用野火啟明瑞薩RA開發(fā)板 [FatFs讀寫測試文件.txt]";
BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */
FATFS是在ff.h文件定義的一個結構體類型,針對的對象是物理設備,包含了物理設備的物理編號、扇區(qū)大小等等信息, 一般我們都需要為每個物理設備定義一個FATFS變量。
FIL也是在ff.h文件定義的一個結構體類型,針對的對象是文件系統(tǒng)內(nèi)具體的文件,包含了文件很多基本屬性,比如文件大小、 路徑、當前讀寫地址等等。如果需要在同一時間打開多個文件進行讀寫,才需要定義多個FIL變量,不然一般定義一個FIL變量即可。
FRESULT是也在ff.h文件定義的一個枚舉類型,作為FatFs函數(shù)的返回值類型,主要管理FatFs運行中出現(xiàn)的錯誤。 總共有19種錯誤類型,包括物理設備讀寫錯誤、找不到文件、沒有掛載工作空間等等錯誤。這在實際編程中非常重要, 當有錯誤出現(xiàn)是我們要停止文件讀寫,通過返回值我們可以快速定位到錯誤發(fā)生的可能地點。如果運行沒有錯誤才返回FR_OK。
fnum是個32位無符號整形變量,用來記錄實際讀取或者寫入數(shù)據(jù)的數(shù)組。
fileReadBuffer和fileWriteBuffer分別對應讀取和寫入數(shù)據(jù)緩存區(qū),都是8位無符號整形數(shù)組。
hal_entry 入口函數(shù)
代碼清單: 24_9 hal_entry 入口函數(shù)
/* 用戶頭文件包含 */ #include "led/bsp_led.h" #include "debug_uart/bsp_debug_uart.h" //FatFs #include "FatFs/ff15/ff.h" FATFS fs; /* FatFs文件系統(tǒng)對象 */ FIL fnew; /* 文件對象 */ UINT fnum; /* 文件成功讀寫數(shù)量 */ FRESULT res_flash; /* 文件操作結果 */ BYTE fileReadBuffer[1024]; /* 讀緩沖區(qū) */ BYTE fileWriteBuffer[] = /* 寫緩沖區(qū) */ "感謝您選用野火啟明瑞薩RA開發(fā)板 [FatFs讀寫測試文件.txt]"; BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */ void hal_entry(void) { /* TODO: add your own code here */ LED_Init(); // LED 初始化 Debug_UART4_Init(); // SCI4 UART 調(diào)試串口初始化 printf("這是一個串行FLASH的FatFs使用演示例程\r\n"); printf("打開串口助手查看打印的信息\r\n\r\n"); /* 嘗試掛載外部FLASH FAT文件系統(tǒng) */ res_flash = f_mount(&fs, "0:", 1); if (res_flash == FR_NO_FILESYSTEM) { printf(">>> FLASH還沒有文件系統(tǒng),即將進行格式化...\r\n"); /* 格式化 */ res_flash = f_mkfs("0:", NULL, work, sizeof(work)); if (res_flash == FR_OK) { printf(">>> FLASH已成功格式化文件系統(tǒng)。\r\n"); /* 格式化后,先取消掛載 */ res_flash = f_mount(NULL,"0:",1); /* 重新掛載 */ res_flash = f_mount(&fs,"0:",1); } else { printf(">>> 格式化失敗!!\r\n"); while (1); } } else if (res_flash == FR_OK) { printf(">>> 文件系統(tǒng)掛載成功,可以進行讀寫測試。\r\n"); } else { printf("!!外部Flash掛載文件系統(tǒng)失敗。(%d)\r\n", res_flash); printf("!!可能原因:Flash初始化不成功。\r\n"); while (1); } /*----------------------- 文件系統(tǒng)測試:寫測試 -----------------------------*/ printf("\r\n****** 即將進行文件寫入測試 ******\r\n"); /* 打開文件,如果文件不存在則創(chuàng)建它 */ res_flash = f_open(&fnew, "0:FatFs讀寫測試文件.txt", FA_CREATE_ALWAYS | FA_WRITE); if ( res_flash == FR_OK ) { printf(">>> 打開/創(chuàng)建“FatFs讀寫測試文件.txt”文件成功,向文件寫入數(shù)據(jù)。\r\n"); /* 將指定存儲區(qū)內(nèi)容寫入到文件內(nèi) */ res_flash = f_write(&fnew, fileWriteBuffer, sizeof(fileWriteBuffer), &fnum); if (res_flash==FR_OK) { printf(">>> 文件寫入成功,寫入字節(jié)數(shù)據(jù):%d\r\n", fnum); printf(">>> 向文件寫入的數(shù)據(jù)為:%s\r\n", fileWriteBuffer); } else { printf("!!文件寫入失敗:(%d)\n",res_flash); } /* 不再讀寫,關閉文件 */ f_close(&fnew); } else { printf("!!打開/創(chuàng)建文件失敗。\r\n"); } /*----------------------- 文件系統(tǒng)測試:讀測試 -----------------------------*/ printf("****** 即將進行文件讀取測試 ******\r\n"); /* 打開文件,該文件前面已創(chuàng)建 */ res_flash = f_open(&fnew, "0:FatFs讀寫測試文件.txt", FA_OPEN_EXISTING | FA_READ); if (res_flash == FR_OK) { printf(">>> 打開文件成功。\r\n"); res_flash = f_read(&fnew, fileReadBuffer, sizeof(fileReadBuffer), &fnum); if (res_flash==FR_OK) { printf(">>> 文件讀取成功,讀到字節(jié)數(shù)據(jù):%d\r\n", fnum); printf(">>> 讀取得的文件數(shù)據(jù)為:%s\r\n", fileReadBuffer); } else { printf("!!文件讀取失敗:(%d)\n",res_flash); } /* 不再讀寫,關閉文件 */ f_close(&fnew); } else { printf("!!打開文件失敗。\r\n"); } /* 不再使用文件系統(tǒng),取消掛載文件系統(tǒng) */ f_mount(NULL,"0:",1); printf("****** 測試結束 ******\r\n"); while(1); #if BSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter(); #endif }
程序的開頭首先初始化調(diào)試串口,用來打印程序運行的一些調(diào)試信息。 此處的代碼是以啟明6M5板子的為例,另外兩塊板子的串口初始化函數(shù)名可能不同,讀者需要注意。
FatFs 使用的第一步工作就是使用 f_mount 函數(shù)掛載文件系統(tǒng)。 f_mount函數(shù)有三個形參,第一個參數(shù)是指向FATFS變量指針,如果賦值為NULL可以取消物理設備掛載。 第二個參數(shù)為邏輯設備編號,使用設備根路徑表示,與物理設備編號掛鉤, 在 diskio.h 中我們定義外部FLASH存儲器的物理編號為0,所以這里使用“0:”。 第三個參數(shù)可選0或1,1表示立即掛載,0表示不立即掛載,延遲掛載。 f_mount函數(shù)會返回一個 FRESULT 類型的值,指示掛載結果的情況。
如果 f_mount 函數(shù)返回值為 FR_NO_FILESYSTEM,說明外部FLASH存儲器還沒有FAT文件系統(tǒng)。 我們就必須對外部FLASH存儲器進行格式化處理,將里面的存儲空間以FAT文件系統(tǒng)的格式進行格式化。 使用 f_mkfs 函數(shù)可以實現(xiàn)格式化操作。 f_mkfs 函數(shù)有三個形參,第一個參數(shù)為邏輯設備編號; 第二個參數(shù)為格式化選項,選擇格式化的類型,比如:FAT16/FAT32/exFAT等,NULL表示使用默認選項進行格式化; 第三個參數(shù)指定工作緩存區(qū);第四個參數(shù)提供工作緩存區(qū)的大小。 格式化成功后需要先取消掛載原來設備,再重新掛載設備。
在設備正常掛載后,就可以進行文件讀寫操作了。使用文件之前,必須使用f_open函數(shù)打開文件,不再使用文件時必須使用f_close函數(shù)關閉文件, 這個跟電腦端操作文件步驟類似。f_open函數(shù)有三個形參,第一個參數(shù)為文件對象指針。第二參數(shù)為目標文件,包含絕對路徑的文件名稱和后綴名。 第三個參數(shù)為訪問文件模式選擇,可以是打開已經(jīng)存在的文件模式、讀模式、寫模式、新建模式、總是新建模式等的或運行結果。比如對于寫測試, 使用FA_CREATE_ALWAYS和FA_WRITE組合模式,就是總是新建文件并進行寫模式。
f_close函數(shù)用于不再對文件進行讀寫操作關閉文件,f_close函數(shù)只要一個形參,為文件對象指針。f_close函數(shù)運行可以確保緩沖區(qū)完全寫入到文件內(nèi)。
成功打開文件之后就可以使用f_write函數(shù)和f_read函數(shù)對文件進行寫操作和讀操作。這兩個函數(shù)用到的參數(shù)是一致的,只不過一個是數(shù)據(jù)寫入,一個是數(shù)據(jù)讀取。 f_write函數(shù)第一個形參為文件對象指針,使用與f_open函數(shù)一致即可。第二個參數(shù)為待寫入數(shù)據(jù)的首地址,對于f_read函數(shù)就是用來存放讀出數(shù)據(jù)的首地址。 第三個參數(shù)為寫入數(shù)據(jù)的字節(jié)數(shù),對于f_read函數(shù)就是欲讀取數(shù)據(jù)的字節(jié)數(shù)。第四個參數(shù)為32位無符號整形指針,這里使用fnum變量地址賦值給它, 在運行讀寫操作函數(shù)后,fnum變量指示成功讀取或者寫入的字節(jié)個數(shù)。之后 我們通過相應函數(shù)的返回值賦值到res_flash,后進行判斷當前程序是否運行成功我們點亮綠燈和藍燈,如果其中有一個失敗我們點亮紅燈。
最后,不再使用文件系統(tǒng)時,使用f_mount函數(shù)取消掛載。
24.4.7.實驗驗證
用USB線連接開發(fā)板“USB TO UART”接口跟電腦,在電腦端打開串口調(diào)試助手, 把編譯好的程序下載到開發(fā)板。在串口調(diào)試助手可看到FatFs測試的調(diào)試信息。

-
FlaSh
+關注
關注
10文章
1752瀏覽量
155633 -
瑞薩
+關注
關注
37文章
22491瀏覽量
90952 -
文件系統(tǒng)
+關注
關注
0文章
304瀏覽量
20996 -
FATFS
+關注
關注
0文章
46瀏覽量
19553
原文標題:FatFs的官網(wǎng)源碼結構及已知問題以及解決方法或補丁——瑞薩RA系列FSP庫開發(fā)實戰(zhàn)指南(87)
文章出處:【微信號:瑞薩嵌入式小百科,微信公眾號:瑞薩嵌入式小百科】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
瑞薩RA系列FSP庫開發(fā)實戰(zhàn)指南之FatFs文件系統(tǒng)介紹
瑞薩e2studio(1)----瑞薩芯片之搭建FSP環(huán)境
怎樣移植FATFS文件到工程文件夾
介紹移植fatfs文件系統(tǒng)步驟
如何在spi_flash上移植建立fatfs文件系統(tǒng)呢
【瑞薩RA4系列開發(fā)板體驗】開發(fā)環(huán)境搭建和新手點燈指南
【瑞薩RA4系列開發(fā)板體驗】體驗過程
【野火啟明6M5開發(fā)板體驗】開箱+認識開發(fā)板+資料
Fatfs(文件系統(tǒng)的移植)
【文件系統(tǒng)】FatFs文件系統(tǒng)在嵌入式芯片LPC18XX上的移植
手把手教你在flash上移植fatfs文件系統(tǒng)(含實時操作系統(tǒng))
瑞薩RA系列FSP庫開發(fā)實戰(zhàn)指南之基于FLASH的FatFs文件系統(tǒng)移植實驗
評論