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

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

底層設(shè)備驅(qū)動(dòng)函數(shù)是存放在diskio.c文件,我們的目的就是把diskio.c中的函數(shù)接口與串行Flash芯片驅(qū)動(dòng)連接起來。總共有五個(gè)函數(shù),分別為設(shè)備狀態(tài)獲取(disk_status)、設(shè)備初始化(disk_initialize)、扇區(qū)讀取(disk_read)、扇區(qū)寫入(disk_write)、其他控制(disk_ioctl)。
接下來,我們對(duì)每個(gè)函數(shù)結(jié)合串行Flash芯片驅(qū)動(dòng)做詳細(xì)講解。
24.4.2.2
修改配置文件
ffconf.h文件是FatFs的配置文件。
下面是ffconf.h文件中,需要修改的部分,只把需要修改的部分放出來:
列表1:
代碼清單24?1 FatFs的配置文件:ffconf.h
左右滑動(dòng)查看完整內(nèi)容
/********************/ /* 下面是經(jīng)過修改部分 */ /********************/ #define FF_USE_MKFS 1 //此選項(xiàng)切換是否啟用 f_mkfs() 函數(shù),用于格式化 Flash、SD卡等 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ #define FF_CODE_PAGE 936 //此選項(xiàng)指定使用的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 //此選項(xiàng)切換對(duì)長文件名的支持 #define FF_MAX_LFN 255 //設(shè)置長文件名的最長長度 /* 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 //此選項(xiàng)設(shè)置是否啟用 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 //要使用的卷(邏輯驅(qū)動(dòng)器)的數(shù)量。范圍(1-10) /* Number of volumes (logical drives) to be used. (1-10) */ #define FF_MIN_SS 512 #define FF_MAX_SS 4096 //這組選項(xiàng)配置支持的扇區(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() 中指定所需操作的設(shè)備的扇區(qū)大小 * 在 case GET_SECTOR_SIZE 項(xiàng)中指定 */ #define FF_FS_NORTC 1 //設(shè)置為1關(guān)閉時(shí)間戳 啟用時(shí)間戳功能需要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, /* 指向要注冊(cè)的文件系統(tǒng)對(duì)象的指針(NULL:卸載) */
const TCHAR* path, /* 要裝載/卸載的邏輯驅(qū)動(dòng)器號(hào) */
BYTE opt /* 裝載選項(xiàng):0=不裝載(延遲裝載),1=立即裝載 */
)
FRESULT f_open (
FIL* fp, /* 指向空白文件對(duì)象的指針 */
const TCHAR* path, /* 指向文件名的指針 */
BYTE mode /* 訪問模式和打開模式標(biāo)志 */
)
這就要求我們要知道 path 這個(gè)參數(shù)的可輸入值是什么,也就是我們要了解FatFs的文件命名規(guī)范。
24.4.3.1.文件命名格式
FatFs模塊上的路徑名格式與DOS/Windows的文件名規(guī)范類似,如下所示:
[drive#:][/]"directory 目錄"/"file 文件"
FatFs 模塊支持長文件名(LFN) 和 8.3 格式的文件名 (SFN)。長文件名(LFN) 可在 FF_USE_LFN >= 1 時(shí)使用。 子目錄用 \ 或 / 分隔,方式與DOS/Windows API相同。 重復(fù)的分隔符和終止分隔符將被忽略,例如:“//animal///cat/”。 唯一不同的是,F(xiàn)atFs中指定邏輯驅(qū)動(dòng)器(FAT卷)的標(biāo)題驅(qū)動(dòng)器前綴是數(shù)字(0-9)+冒號(hào),而在DOS/Windows中是字母(A-Z)+冒號(hào)。 邏輯驅(qū)動(dòng)器編號(hào)是指定要訪問的卷的標(biāo)識(shí)符。 如果省略驅(qū)動(dòng)器前綴,則假定邏輯驅(qū)動(dòng)器編號(hào)為默認(rèn)驅(qū)動(dòng)器。
注解
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)下,因?yàn)槲募Q有8.3格式的限制,凡文件主檔名超過8字節(jié)或擴(kuò)展名超過3字節(jié)的文件名,都被稱為“長文件名”,在Windows下正常的文件名置換于DOS(或“命令提示字符”)環(huán)境下則可能無法完整顯示,如“Program files”資料夾可能會(huì)顯示成其對(duì)應(yīng)的8.3文件名“PROGRA~1”。
24.4.3.2.FF_FS_RPATH
在默認(rèn)配置 (FF_FS_RPATH==0) 中, 是沒有當(dāng)前目錄的概念的。 卷上的每個(gè)對(duì)象始終以從根目錄開始的完整路徑名指定。 不可以使用點(diǎn)目錄名稱(“.”,“..”)。標(biāo)題分隔符被忽略, 它可以存在或省略。默認(rèn)驅(qū)動(dòng)器固定為驅(qū)動(dòng)器0。
啟用相對(duì)路徑功能時(shí)(FF_FS_RPATH>=1),如果存在分隔符, 則從根目錄跟隨指定的路徑。如果沒有分隔符,則從默認(rèn)驅(qū)動(dòng)器的當(dāng)前目錄開始。 路徑名也允許使用點(diǎn)目錄名。 當(dāng)前目錄由f_chdir函數(shù)設(shè)置,默認(rèn)驅(qū)動(dòng)器為f_chdrive函數(shù)設(shè)置的當(dāng)前驅(qū)動(dòng)器。

此外,驅(qū)動(dòng)器前綴可以采用預(yù)定義的任意字符串。 當(dāng)選項(xiàng)FF_STR_VOLUME_ID == 1 時(shí),也可以將任意字符串卷 ID 用作驅(qū)動(dòng)器前綴。 例如 “flash:file1.txt”、“ram:temp.dat” 或 “sd:” 。 如果 srting 與任何卷 ID 都不匹配,則該函數(shù)將失敗并返回 FR_INVALID_DRIVE。
當(dāng)FF_STR_VOLUME_ID == 2 時(shí),可以使用 Unix 樣式的驅(qū)動(dòng)器前綴。 例如 “/flash/file1.txt”、“/ram/temp.dat” 或 “/sd” 。
FF_STR_VOLUME_ID 和 FF_VOLUME_STRS 宏
#define FF_STR_VOLUME_ID 0 //FF_STR_VOLUME_ID開關(guān)支持任意字符串形式的卷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: 此選項(xiàng)切換對(duì)字符串卷 ID 的支持。
FF_STR_VOLUME_ID 為不同數(shù)值時(shí),對(duì)應(yīng)的可選項(xiàng)| 數(shù)值 | 描述 | 示例 |
|---|---|---|
| 0 | 只能使用數(shù)字 ID 中的 DOS/Windows 樣式驅(qū)動(dòng)器前綴。 | 1:/filename |
| 1 | 還可以使用字符串 ID 中的 DOS/Windows 樣式驅(qū)動(dòng)器前綴。 | flash:/filename |
| 2 | 也可以使用字符串ID中的Unix樣式驅(qū)動(dòng)器前綴。 | /flash/filename |
FF_VOLUME_STRS: 此選項(xiàng)定義每個(gè)邏輯驅(qū)動(dòng)器的卷 ID 字符串。項(xiàng)目數(shù)量不得少于FF_VOLUMES。 卷 ID 字符串的有效字符是 A-Z、a-z 和 0-9,不區(qū)分大小寫。 如果FF_STR_VOLUME_ID == 0,則此選項(xiàng)不起作用。 如果 FF_STR_VOLUME_ID >= 1 并且未定義此選項(xiàng), 則需要定義用戶定義的卷字符串表,如下所示。不應(yīng)動(dòng)態(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ù), 來實(shí)現(xiàn)函數(shù)的功能,提供給FatFs調(diào)用就好了。
FatFs文件系統(tǒng)與存儲(chǔ)設(shè)備的連接函數(shù)在diskio.c文件中,主要有5個(gè)函數(shù)需要我們編寫的。
宏定義和存儲(chǔ)設(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;
}
存儲(chǔ)設(shè)備初始化函數(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;
}
存儲(chǔ)設(shè)備數(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;
}
存儲(chǔ)設(shè)備數(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: /* 同時(shí)擦除扇區(qū)個(gè)數(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) {
/* 返回當(dāng)前時(shí)間戳 */
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 */
}
由于之前在配置文件關(guān)閉了 RTC 和時(shí)間戳功能,因此其實(shí)不需要實(shí)現(xiàn)這個(gè)返回當(dāng)前時(shí)間戳函數(shù)。
由 FF_FS_NORTC 決定是否啟用,F(xiàn)F_FS_NORTC設(shè)置為1以禁用時(shí)間戳功能。
24.4.5.FatFs 基本 API 函數(shù)說明
更多關(guān)于 FatFs 接口函數(shù)的說明請(qǐng)看官方的說明: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 需要每個(gè)邏輯驅(qū)動(dòng)器(FAT 卷)的工作區(qū)域(文件系統(tǒng)對(duì)象)。 在執(zhí)行任何文件/目錄操作之前,需要使用邏輯驅(qū)動(dòng)器的f_mount函數(shù)注冊(cè)文件系統(tǒng)對(duì)象。 完成此過程后,文件/目錄 API 函數(shù)已準(zhǔn)備好工作。 某些卷管理功能(f_mkfs、f_fdisk和f_setcp)不需要文件系統(tǒng)對(duì)象。
f_mount函數(shù)將文件系統(tǒng)對(duì)象注冊(cè)/注銷到 FatFs 模塊,如下所示:
確定由 path 指定的邏輯驅(qū)動(dòng)器。
清除并注銷卷的已注冊(cè)工作區(qū)(如果存在)。
如果 fs 不為 NULL,則清除新工作區(qū)并將其注冊(cè)到卷中。
如果指定了強(qiáng)制裝入,則對(duì)卷執(zhí)行卷裝入過程。
opt:安裝選項(xiàng)。0:現(xiàn)在不掛載(要在第一次訪問卷時(shí)掛載),1:強(qiáng)制掛載卷以檢查它是否準(zhǔn)備好工作。
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ù)目眾多,在尋址時(shí)比較困難, 所以操作系統(tǒng)就將多個(gè)的扇區(qū)組合在一起,形成一個(gè)更大的單位, 再對(duì)這個(gè)單位進(jìn)行整體的操作。也可以理解為文件的磁盤空間分配單位。 當(dāng)簇的大小為 32768 字節(jié)時(shí),大小為 100 字節(jié)的文件將占用 32768 字節(jié)的磁盤空間。 隨著簇大小的增加,磁盤使用的空間效率會(huì)變得很低,但與此同時(shí),讀/寫效率也會(huì)提高。 因此,簇的大小是空間效率和讀/寫效率之間的權(quán)衡。
opt:若為NULL,則啟用默認(rèn)參數(shù)
f_mkfs opt 默認(rèn)參數(shù)
static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
由work來指向用于格式化過程的工作緩沖區(qū)的指針。
由len來確定工作緩沖區(qū)的大小(以字節(jié)為單位)。至少需要FF_MAX_SS。
提示
當(dāng) FF_FS_READONLY == 0 且 FF_USE_MKFS == 1 時(shí)可用。
24.4.5.3.f_setlabel
f_setlabel 函數(shù)原型如下:
FRESULT f_setlabel ( const TCHAR* label /* [IN] Volume label to be set */ );
當(dāng)字符串具有驅(qū)動(dòng)器前綴時(shí),卷標(biāo)將設(shè)置為驅(qū)動(dòng)器前綴指定的卷。 Unix 樣式的卷 ID 不能用于指定卷。 如果未指定驅(qū)動(dòng)器號(hào),則卷標(biāo)將設(shè)置為默認(rèn)驅(qū)動(dòng)器。 如果給定卷標(biāo)的長度為零,則將刪除卷上的卷標(biāo)。卷標(biāo)的格式如下所示:
在 FAT 卷上轉(zhuǎn)換 OEM 代碼頁時(shí)最多 11 個(gè)字節(jié)。 在 exFAT 卷中最多 11 個(gè)字符。 FAT 卷允許的字符數(shù)為:SFN 允許的字符不包括點(diǎn)。低寫字符向上轉(zhuǎn)換。 exFAT 卷允許的字符為:LFN 允許的字符包括點(diǎn)。將保留小寫字符。 空格可以嵌入卷標(biāo)中的任何位置。尾隨空格在 FAT 音量處被截?cái)唷?/p>
提示
當(dāng)FF_FS_READONLY == 0 且FF_USE_LABEL == 1 時(shí)可用。
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 */ );
打開一個(gè)文件并創(chuàng)建一個(gè)文件對(duì)象。 完成對(duì)文件的訪問后應(yīng)該使用f_close函數(shù)將其關(guān)閉。 如果在關(guān)機(jī)、移出介質(zhì)或重新裝入之前對(duì)文件進(jìn)行了任何更改且未調(diào)用f_close函數(shù)將其關(guān)閉,則文件可能會(huì) collapsed (損壞)。
可選mode選項(xiàng):

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

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