FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明
??FatFs 是用于小型嵌入式系統(tǒng)的通用 FAT/exFAT 文件系統(tǒng)模塊。FatFs 模塊的編寫符合 ANSI C(C89),并與磁盤 I/O 層完全分離,因此它獨立于硬件平臺。 它可以集成到資源有限的小型微控制器中,例如 8051,PIC,AVR,ARM,Z80,RX 等。此外,還提供用于微型微控制器的 Petit FatFs 模塊。
??看本文時需要有點 FAT 文件系統(tǒng)的基礎(chǔ),可以參考FatFs 之三 FAT文件系統(tǒng)基礎(chǔ)、FAT 數(shù)據(jù)格式、引導(dǎo)、編碼。
變更記錄
??具體參見源碼文件中的 /source/00history.txt 即可!也可以去官網(wǎng)查看。從中我們可以看到修復(fù)的各問題,尤其是源碼文件的變動。例如:在 R0.13c 中,原來獨立文件 integer.h 中的內(nèi)容被直接包含在了 ff.h 中,原來的 integer.h 被刪除!對比如下圖所示:
源碼目錄文件
??目前,最新版本為 R0.13c。相比于之前的版本,源碼有了一定的變化(參見上圖)。FatFs 的源碼包中,文件非常簡單。其源碼目錄結(jié)構(gòu)如下所示(對于簡單的文件以注釋的形式給出,核心源碼下文會詳細(xì)說明):
FatFs R0.13c │ LICENSE.txt // 版權(quán)說明 ├─documents // 配套的說明文檔 └─source00history.txt // 更新歷史記錄00readme.txt // 對于以下每個文件的功能簡介diskio.c // FATFS 與硬件的接口實現(xiàn)文件模板diskio.h // FATFS 與硬件的接口實現(xiàn)文件模板ff.c // FATFS 核心源代碼ff.h // FATFS 核心源代碼ffconf.h // FATFS 的配置文件ffsystem.c // 定義了:可選的與操作系統(tǒng)對接的各接口相關(guān)實現(xiàn)示例。ffunicode.c // 提供了 Unicode 相關(guān)的轉(zhuǎn)換函數(shù)以下主要介紹 source 目錄下的源代碼部分!
ffconf.h
??該文件是 FatFs 的配置文件。用戶需要根據(jù)自己的需求,來修改該文件中的各配置項。在 FatFs 默認(rèn)的配置文件中,很多我們常用的函數(shù)都是被禁用的,必須修改下面的配置項來啟用。這也就意味著,直接使用默認(rèn)的配置文件一般是無法滿足我們的需求的!接下來詳細(xì)介紹一下每個配置項的具體含義及使用時需要注意的問題。以下為 FatFs 源碼中默認(rèn)的配置項說明。
FatFs Functional Configurations
FFCONF_DEF
定義了 FatFs 的版本號,與實際功能無關(guān)。 主要是 FatFs 自身用來防止出錯。具體在 ff.h 中,會檢查該文件中的改宏值是否與 ff.h 中版本一致。在 ff.h 中,有如下語句:
#if FF_DEFINED != FFCONF_DEF #error Wrong configuration file (ffconf.h). #endifFunction Configurations 功能配置
FF_FS_READONLY
定義 FatFs 是否工作在只讀模式。
- 0:讀/寫。默認(rèn)值。
- 1:只讀。只讀模式下,寫相關(guān)的函數(shù) f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 以及其他和寫操作相關(guān)的函數(shù)都將被移除。
FF_FS_MINIMIZE
用來極限精簡 FatFs,此選項定義最小化級別,以刪除一些基本 API 函數(shù),如下所示
- 0:所有基本的 API 函數(shù)都可用。默認(rèn)值。
- 1:f_stat,f_getfree,f_unlink,f_mkdir,f_chmod,f_utime,f_truncate和f_rename函數(shù)被刪除。
- 2:除了移除 1 中的函數(shù),還將移除 f_opendir, f_readdir and f_closedir
- 3:除了移除 2 中的函數(shù),還將移除 f_lseek
FF_USE_STRFUNC
定義字符操作的相關(guān)函數(shù) f_gets(), f_putc(), f_puts() 及 f_printf() 是否有效。
- 0:禁用所有的字符串相關(guān)操作函數(shù)。默認(rèn)值。
- 1:啟用,但是沒有 LF-CRLF 轉(zhuǎn)換。即:不會忽略回車符 \r
- 2:啟用,且?guī)в?LF-CRLF轉(zhuǎn)換。即:忽略回車符 \r
FF_USE_FIND
定義目錄讀取的相關(guān)函數(shù)(f_findfirst(), f_findnext())是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用
- 2: 啟用,且會檢查是否匹配 altname[]。在 1 情況下,匹配條件只有文件名。此情況下,如果 altname[]匹配也認(rèn)為是匹配的。FF_FS_MINIMIZE 必須為 0 或 1
FF_USE_MKFS
定義函數(shù) f_mkfs() 是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用
FF_USE_FASTSEEK
定義快速 seek 模式是否有效。
- 0: 禁用。默認(rèn)值
- 1: 啟用。會額外記錄很多信息,以供在 f_lseek 中使用
FF_USE_EXPAND
定義函數(shù) f_expand() 是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用
FF_USE_CHMOD
定義屬性操作函數(shù) f_chmod() 和 f_utime() 是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用。此外必須定義 FF_FS_READONLY 為 0。即:不能開啟只讀模式
FF_USE_LABEL
定義卷標(biāo)函數(shù) f_getlabel() 和 f_setlabel() 是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用
FF_USE_FORWARD
定義函數(shù) f_forward() 是否有效
- 0: 禁用。默認(rèn)值
- 1: 啟用
Locale and Namespace Configurations 本地化和命名空間設(shè)置
FF_CODE_PAGE
指定要在目標(biāo)系統(tǒng)上使用的 OEM 代碼頁,錯誤的編碼頁設(shè)置將導(dǎo)致讀寫文件失敗。支持的編碼頁如下:
| 0 | 包括以下的所有代碼頁并由 f_setcp() 配置 |
| 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) |
如果路徑名未使用任何非ASCII字符,則任何代碼頁設(shè)置之間都沒有區(qū)別,將它設(shè)置為 437 即可。
關(guān)于編碼頁,可以參考一下博文 FatFs 之 路徑規(guī)則、字符編碼、編碼頁、卷管理詳解。
FF_USE_LFN
??此選項可切換對長文件名(LFN)的支持。 啟用 LFN 時,需要將 Unicode 支持模塊 ffunicode.c 添加到項目中。 當(dāng)使用堆棧作為工作緩沖區(qū)時,請注意堆棧溢出。 當(dāng)使用堆內(nèi)存作為工作緩沖區(qū)時,需要將 ffsystem.c 添加到項目中,并實現(xiàn)其中的內(nèi)存管理函數(shù) ff_memalloc 和 ff_memfree。
- 0: 不啟用。默認(rèn)值。FF_MAX_LFN 無效
- 1: 啟用。且 LFN 在代碼段 BSS 上具有靜態(tài)工作緩沖區(qū)。 始終不是線程安全的。
- 2: 啟用。且在 STACK 上具有動態(tài)工作緩沖區(qū)的 LFN。需要注意棧溢出的問題。
- 3: 啟用。且在 HEAP 上具有動態(tài)工作緩沖區(qū)的 LFN。此時,必須要啟用 ffsystem.c 中的動態(tài)內(nèi)存申請函數(shù) ff_memalloc() 和 ff_memfree()
注意:長文件名與上面的編碼頁有關(guān)系!有些編碼頁就是 Unicode 字符集,也因此需要長文件名支持!
關(guān)于長文件名,可以參考一下博文 FatFs 之 路徑規(guī)則、字符編碼、編碼頁、卷管理詳解。
FF_MAX_LFN
??啟用 LFN ,會增加 (FF_MAX_LFN + 1) * 2 字節(jié)的固定緩沖區(qū)空間。具體使用方式見 ff.c 文件的開頭部分的宏定義即可。如果是 exFAT 文件系統(tǒng),則還需要再占用 (FF_MAX_LFN + 44) / 15 * 32 字節(jié)的緩沖區(qū)空間。且 exFAT 必須啟用長文件名支持
??FF_MAX_LFN 以 UTF-16 代碼單位定義工作緩沖區(qū)的大小,它可以在 12 到 255 的范圍內(nèi)。建議將 255 設(shè)置為完全支持LFN規(guī)范。長文件名是微軟的專利,使用中與上面的編碼有關(guān)系。
FF_LFN_UNICODE
??此選項可在 API 上切換文件名的字符編碼。選擇 Unicode 時,FF_CODE_PAGE 實際上沒有任何意義,除了與遺留系統(tǒng)的兼容性,例如 MS-DOS 和任何不支持 LFN 的系統(tǒng)。 FatFs 支持代碼點達(dá)到 U + 10FFFF。
- 0: ANSI/OEM in current CP (TCHAR = char)。默認(rèn)值
- 1: Unicode in UTF-16 (TCHAR = WCHAR)
- 2: Unicode in UTF-8 (TCHAR = char)
- 3: Unicode in UTF-32 (TCHAR = DWORD)
此外,字符串輸入/輸出函數(shù)的行為也會受到此選項的影響。如果關(guān)閉了長文件名支持,則該項無效!
FF_LFN_BUF 和 FF_SFN_BUF
??這組選項在 FILINFO 結(jié)構(gòu)中定義了文件名成員 fname[] 和 altname[] 的大小,FILINFO 結(jié)構(gòu)用于讀取目錄項。這些值應(yīng)該足夠讀取文件名。讀取文件名的最大可能長度取決于API上的字符編碼,如下所示:
| ANSI/OEM at SBCS | 255 items | 12 items |
| ANSI/OEM at DBCS | 510 items | 12 items |
| Unicode in UTF-16/32 | 255 items | 12 items |
| Unicode in UTF-8 | 765 items | 34 items |
如果名稱成員的大小不足以用于LFN,則該項目將被視為沒有LFN。 如果未啟用LFN,則這些選項無效。
FF_STRF_ENCODE
??當(dāng) API 上的字符編碼為 Unicode(FF_LFN_UNICODE> = 1)時,字符串 I/O 函數(shù),f_gets,f_putc,f_puts和 f_printf 將轉(zhuǎn)換其中的字符編碼。 此選項定義了要通過這些函數(shù)讀取/寫入的文件的字符編碼。 當(dāng)LFN 未啟用或 FF_LFN_UNICODE 為 0 時,字符串函數(shù)在沒有任何編碼轉(zhuǎn)換的情況下工作,此選項無效。
| 0 | ANSI/OEM in current code page |
| 1 | Unicode in UTF-16LE |
| 2 | Unicode in UTF-16BE |
| 3 | Unicode in UTF-8 |
FF_FS_RPATH
定義是否支持相對路徑
- 0: 禁用。且相對路徑相關(guān)的函數(shù)也被移除
- 1: 支持。f_chdir() 和 f_chdrive() 有效
- 2: 支持。除了提供 1 中的函數(shù),還提供 f_getcwd()
Volume/Drive Configurations 驅(qū)動器/卷配置
FF_VOLUMES
此選項配置要使用的卷數(shù)(邏輯驅(qū)動器最多10個),最小值 1。
FF_STR_VOLUME_ID
??此選項定義對字符串卷ID的支持。當(dāng)為驅(qū)動器前綴啟用任意卷ID字符串時,可以使用 FF_VOLUME_STRS 預(yù)定義的字符串或用戶定義的字符串作為路徑名稱中的驅(qū)動器前綴。無論該選項是什么,數(shù)字驅(qū)動器號總是有效的,而且該選項還可以啟用驅(qū)動器前綴的任何一種格式。
| 0 | 僅 DOS/Windows 風(fēng)格的數(shù)字ID前綴可用 | 0:/filename |
| 1 | DOS/Windows 風(fēng)格的字符串ID前綴也可用 | flash:/filename |
| 2 | Unix 風(fēng)格的字符串ID前綴也可用 | /flash/filename |
FF_VOLUME_STRS
??此選項定義每個邏輯驅(qū)動器的卷ID字符串。項目數(shù)不得少于FF_VOLUMES。 卷ID字符串的有效字符是A-Z,a-z和0-9,但是,它們在不區(qū)分大小寫的情況下進行比較。
??如果 FF_STR_VOLUME_ID == 0,則此選項無效。 如果 FF_STR_VOLUME_ID > = 1且未定義此選項,則需要定義用戶定義的卷字符串表,如下所示。 該表不應(yīng)動態(tài)修改。
FF_MULTI_PARTITION
??禁用(0)或啟用(1)。 此選項可切換多分區(qū)功能。 默認(rèn)情況下(0),每個邏輯驅(qū)動器號綁定到相同的物理驅(qū)動器號,并且僅安裝物理驅(qū)動器中的卷。 啟用后,每個邏輯驅(qū)動器都綁定到用??戶定義的分區(qū)解析表 VolToPart [] 中列出的物理驅(qū)動器上的分區(qū)。 此外,還將提供 f_fdisk功能。
FF_MIN_SS, FF_MAX_SS
??這組選項定義了低級磁盤 I/O 接口,disk_read 和 disk_write函數(shù)使用的扇區(qū)大小范圍。 有效值為 512、1024、2048 和 4096。FF_MIN_SS定義最小扇區(qū)大小,FF_MAX_SS 定義最大扇區(qū)大小。 始終為存儲卡和硬盤設(shè)置 512。 但是,板載閃存和某些類型的光學(xué)介質(zhì)可能需要更大的值。 當(dāng)FF_MAX_SS > FF_MIN_SS 時,啟用對可變扇區(qū)大小的支持,并且需要對 disk_ioctl 函數(shù)實現(xiàn) GET_SECTOR_SIZE 命令。
FF_USE_TRIM
定義是否支持 ATA-TRIM
- 0: 禁止
- 1: 支持。此時,disk_ioctl() 函數(shù)需要實現(xiàn) CTRL_TRIM 命令。
FF_FS_NOFSINFO
??取值 0 到 3。如果您需要知道FAT32 卷上的正確可用空間,請設(shè)置此選項的第0位,并且在卷 mount 后第一時間執(zhí)行 f_getfree函數(shù),將強制進行完整的FAT掃描。 位1 控制最后分配的簇編號的使用。
| bit0=0 | Use free cluster count in the FSINFO if available. |
| bit0=1 | Do not trust free cluster count in the FSINFO. |
| bit1=0 | Use last allocated cluster number in the FSINFO to find a free cluster if available. |
| bit1=1 | Do not trust last allocated cluster number in the FSINFO. |
System Configurations (嵌入式)操作系統(tǒng)相關(guān)的配置
FF_FS_TINY
??取值為 正常(0)或微小(1)。 在微小的配置中,文件對象 FIL 的大小減少了 FF_MAX_SS 字節(jié)。 不是從文件對象中消除私有數(shù)據(jù)緩沖區(qū),而是將文件系統(tǒng)對象 FATFS 中的公共扇區(qū)緩沖區(qū)用于文件數(shù)據(jù)傳輸。
FF_FS_EXFAT
??此選項定義對 exFAT 文件系統(tǒng)的支持,Enabled(1)或 Disabled(0)。開啟之后,將同時支持 exFAT、FAT/FAT32。要啟用 exFAT,還必須啟用LFN 并為全功能 exFAT 功能配置 FF_LFN_UNICODE> = 1 和 FF_MAX_LFN == 255。 請注意,由于需要64位整數(shù)類型,啟用 exFAT會丟棄ANSI C(C89)兼容性。
FF_FS_NORTC
??此選項控制時間戳功能,0:使用RTC;1:不使用 RTC。 如果系統(tǒng)沒有任何 RTC 功能或不需要有效時間戳,請將 FF_FS_NORTC 設(shè)置為1 以禁用時間戳功能。此時,FatFs 修改的每個對象都有一個由 FF_NORTC_MON,FF_NORTC_MDAY 和 FF_NORTC_YEAR 定義的固定時間戳。 要使用時間戳功能,請設(shè)置 FF_FS_NORTC == 0 并將 get_fattime 函數(shù)添加到項目中以從 RTC 獲取當(dāng)前時間。 此選項對只讀配置無效。
FF_NORTC_MON, FF_NORTC_MDAY, FF_NORTC_YEAR
這組選項定義了在無 RTC 系統(tǒng)中使用的時間。 此選項在只讀配置或 FF_FS_NORTC == 0 時無效。
FF_FS_LOCK
??選項切換文件鎖定功能,以控制打開重復(fù)文件和打開對象的非法操作。 請注意,文件鎖定功能獨立于重入。 只讀配置時,此選項必須為 0。
| 0 | 禁用文件鎖定功能。 為避免文件操作錯誤導(dǎo)致文件崩潰,應(yīng)用程序需要避免非法打開,刪除和重命名為打開的對象。 |
| >0 | 啟用文件鎖定功能。 該值定義在文件鎖定控制下可以同時打開多少文件/子目錄。 使用FR_LOCKED將拒絕對打開對象的非法操作。 |
FF_FS_REENTRANT
??此選項可切換 FatFs 模塊本身的重入(線程安全),取值為 禁用(0)或啟用(1)。 請注意,對不同卷的文件/目錄訪問始終是可重入的,無論此選項如何,它都可以同時工作。但是,卷管理函數(shù) f_mount,f_mkfs 和 f_fdisk 始終不可重入。
??只有文件/目錄訪問同一個卷,換句話說,獨占使用每個文件系統(tǒng)對象,才能受此功能的控制。 要啟用此功能,用戶需要將 ffsystem.c 添加到自己的項目中,同時實現(xiàn)其中的同步處理程序 ff_req_grant,ff_rel_grant,ff_del_syncobj和ff_cre_syncobj。
FF_FS_TIMEOUT
??當(dāng)?shù)却龝r間太長時,使用 FF_FS_TIMEOUT中止文件功能的時間滴答數(shù)。 當(dāng) FF_FS_REENTRANT == 0 時,此選項無效。
FF_SYNC_t
??此選項定義 OS 相關(guān)的同步對象類型。 例如 HANDLE,ID,OS_EVENT *,SemaphoreHandle_t等。用于OS 定義的頭文件需要包含在 ff.c范圍內(nèi)的某處。 當(dāng) FF_FS_REENTRANT == 0 時,此選項無效。
ff.c/h
??這兩個文件是 FatFs 的核心源碼文件。所有的 FatFs 的操作函數(shù)均位于這個了文件中。我們在使用時,使用的 API 也均是出自于這兩個文件。
?? 其中各函數(shù)比較多,后續(xù)單獨來介紹!
ffsystem.c
??當(dāng)使用了操作系統(tǒng)時,該文件必須要由用戶來實現(xiàn)!源碼包中的 ffsystem.c 給出了需要實現(xiàn)的各函數(shù)接口!用戶需要根據(jù)自己使用的操作系統(tǒng),修改其中的各接口的實現(xiàn)。注意:即使不使用系統(tǒng),如果我們啟用了長文件名,且長文件名的配置為 3,則也必須要實現(xiàn)以下兩個動態(tài)內(nèi)存申請函數(shù)! 下面我們看看該文件中的各個函數(shù)接口
動態(tài)內(nèi)存申請
??該文件中的第一部分函數(shù)就是動態(tài)內(nèi)存申請接口。當(dāng)然,其受制于在ffconf.h中的配置項FF_USE_LFN的限制。也就說,如果不使用長文件名,則 FatFs 不需要使用動態(tài)內(nèi)存申請。 標(biāo)準(zhǔn)接口和標(biāo)準(zhǔn)C語言類似,就是名字稍有變化,有以下兩個:
??為什么要自定義這兩個函數(shù)?在使用了嵌入式操作系統(tǒng)之后,嵌入式操作系統(tǒng)一般都會提供對內(nèi)存的管理功能。其一般也會重新定義動態(tài)內(nèi)存的相關(guān)函數(shù),而不是直接使用標(biāo)準(zhǔn) C 語言定義的 malloc 和 free 函數(shù)。
可重入性
??該文件中的第二部分函數(shù)是與系統(tǒng)緊密相關(guān)的相關(guān)的函數(shù)接口。這主要用來處理在多任務(wù)操作系統(tǒng)中,多線程操作相關(guān)的問題。在單線程進程(單任務(wù))中,只存在一個控制流。因此,這些進程所執(zhí)行的代碼無需重入或著說是線程安全的。但是在多線程(多任務(wù))程序中,相同的功能和資源可以通過多個控制流并發(fā)訪問。
??FatFs 使用了同步對象(Synchronization Object)這個稱呼,在實際系統(tǒng)中,通常為 信號量 或者 互斥量。當(dāng)然,也不局限于這兩種。
??以下為 FatFs 默認(rèn)提供的示例代碼。其中給出了常用的系統(tǒng)下的基本操作。如果使用其中的一種,則直接修改注釋即可,否則,需要根據(jù)自己使用的系統(tǒng)來修改。
創(chuàng)建同步對象
該函數(shù)在 f_mount() 函數(shù)中被調(diào)用,用來為指定的卷創(chuàng)建一個同步對象。
int ff_cre_syncobj ( /* 1:成功, 0:失敗 */BYTE vol, /* 對應(yīng)卷(邏輯驅(qū)動器號) */FF_SYNC_t* sobj /* 返回創(chuàng)建的同步信號量的指針 */ ) {/* Win32 */*sobj = CreateMutex(NULL, FALSE, NULL);return (int)(*sobj != INVALID_HANDLE_VALUE);/* uITRON */ // T_CSEM csem = {TA_TPRI,1,1}; // *sobj = acre_sem(&csem); // return (int)(*sobj > 0);/* uC/OS-II */ // OS_ERR err; // *sobj = OSMutexCreate(0, &err); // return (int)(err == OS_NO_ERR);/* FreeRTOS */ // *sobj = xSemaphoreCreateMutex(); // return (int)(*sobj != NULL);/* CMSIS-RTOS */ // *sobj = osMutexCreate(&Mutex[vol]); // return (int)(*sobj != NULL); }刪除同步對象
這個函數(shù)在f mount()函數(shù)中調(diào)用,以刪除使用ff_cre_syncobj ()函數(shù)創(chuàng)建的同步對象。
int ff_del_syncobj ( /* 返回值:1:成功, 0:刪除失敗 */FF_SYNC_t sobj /* 要刪除的同步對象 */ ) {/* Win32 */return (int)CloseHandle(sobj);/* uITRON */ // return (int)(del_sem(sobj) == E_OK);/* uC/OS-II */ // OS_ERR err; // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); // return (int)(err == OS_NO_ERR);/* FreeRTOS */ // vSemaphoreDelete(sobj); // return 1;/* CMSIS-RTOS */ // return (int)(osMutexDelete(sobj) == osOK); }鎖定卷
此函數(shù)在輸入文件函數(shù)時調(diào)用,以鎖定卷。
int ff_req_grant ( /* 返回值:1:獲取鎖定許可, 0:失敗 */FF_SYNC_t sobj /* Sync object to wait */ ) {/* Win32 */return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);/* uITRON */ // return (int)(wai_sem(sobj) == E_OK);/* uC/OS-II */ // OS_ERR err; // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); // return (int)(err == OS_NO_ERR);/* FreeRTOS */ // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);/* CMSIS-RTOS */ // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); }釋放卷
此函數(shù)在離開文件函數(shù)以解鎖卷時調(diào)用。
void ff_rel_grant (FF_SYNC_t sobj /* Sync object to be signaled */ ) {/* Win32 */ReleaseMutex(sobj);/* uITRON */ // sig_sem(sobj);/* uC/OS-II */ // OSMutexPost(sobj);/* FreeRTOS */ // xSemaphoreGive(sobj);/* CMSIS-RTOS */ // osMutexRelease(sobj); }ffunicode.c
??該文件很大,用來處理 Unicode 編碼相關(guān)的功能。如果在以上配置中,啟用了相關(guān)功能,則需要將該文件添加到自己的項目中。用戶無需修改其中的任何內(nèi)容。所有函數(shù)均由 FatFs 調(diào)用!
?? FatFs 建議,如果系統(tǒng)中存在 Unicode 相關(guān)的處理函數(shù),則應(yīng)該以系統(tǒng)自帶的為準(zhǔn),盡量減少對該文件的使用即可!
diskio.c/h
??由于 FatFs 模塊是獨立于平臺和存儲介質(zhì)的文件系統(tǒng)層,因此它與物理設(shè)備(例如存儲卡,硬盤和任何類型的存儲設(shè)備)完全分離。 低級設(shè)備控制模塊不是 FatFs 模塊的一部分,需要由使用者提供。
??diskio.c/h 這兩個文件用于實現(xiàn) FatFs 與硬件的交互。其中定義了 FatFs 與硬件交互使用的接口。FatFs 中的這兩個文件僅僅是個模板,用戶需要根據(jù)需要來實現(xiàn)其中的各接口,并不能直接使用!其中:
- diskio.h: 其中聲明了各接口的形式以及一些 FatFs 使用的宏,用戶不得更改!ff.c 直接包含該頭文件,并使用該文件中定義的一些內(nèi)容!
- diskio.c: 其中存放了需要用戶實現(xiàn)的各接口!用戶需要根據(jù)自己的硬件平臺(RAM、MMC、USB等)以及 ffcong.h中的配置(例如,配置項 FF_USE_TRIM定義為 1,則需要在 disk_ioctl() 函數(shù)中實現(xiàn)對 CTRL_TRIM 命令的處理)
??一般的使用了 FatFs 的第三方庫,都會提供該文件的實現(xiàn)!我們可以參考。例如,在 STM32 的USB 驅(qū)動庫中,有個名為 usbh_msc_fatfs.c 的文件,它其實就是 diskio.c 改了個名字而已。里面的函數(shù)就是 diskio.h 所聲明的那些函數(shù)!也因此在使用 STM32 的USB 驅(qū)動庫 + FatFs 時,我們不再需要 diskio.c了,但是 diskio.h 仍然是必須的!
下面我們來看看具體的各函數(shù)
DSTATUS disk_status (BYTE pdrv);
FatFs 調(diào)用此函數(shù),查詢當(dāng)前驅(qū)動器狀態(tài)
- 參數(shù):
- pdrv:用于標(biāo)識目標(biāo)設(shè)備的物理驅(qū)動器號。 單驅(qū)動系統(tǒng)始終為零。
- 返回值:當(dāng)前驅(qū)動器狀態(tài)以下面描述的狀態(tài)標(biāo)志的組合返回(狀態(tài)宏值是按位定義并使用的,具體見 diskio.h)。 FatFs 僅使用了 STA_NOINIT 和 STA_PROTECT 這兩個。
- STA_NOINIT:表示設(shè)備尚未初始化且未準(zhǔn)備好工作。 此標(biāo)志在系統(tǒng)重置,介質(zhì)刪除或 disk_initialize 功能失敗時設(shè)置。 在disk_initialize函數(shù)成功后清除它。 必須捕獲異步發(fā)生的任何介質(zhì)更改并將其反映到狀態(tài)標(biāo)志,否則自動安裝功能將無法正常工作。 如果系統(tǒng)不支持介質(zhì)更改檢測,則應(yīng)用程序需要在每次更改介質(zhì)后使用 f_mount 函數(shù)顯式重新裝入卷。
- STA_NODISK:表示驅(qū)動器中沒有介質(zhì)。 在固定磁盤驅(qū)動器上清除這項始終被清除。 請注意,FatFs 不會使用此標(biāo)志。
- STA_PROTECT:表示介質(zhì)受寫保護。 在沒有寫保護功能的驅(qū)動器上始終清除此值。 如果設(shè)置了STA_NODISK,則此項無效。
DSTATUS disk_initialize (BYTE pdrv);
FatFs 調(diào)用此函數(shù),以初始化存儲設(shè)備
- 參數(shù):
- pdrv:用于標(biāo)識目標(biāo)設(shè)備的物理驅(qū)動器號。 單驅(qū)動系統(tǒng)始終為零。
- 返回值:與 disk_status 的返回值相同!
此函數(shù)初始化存儲設(shè)備并使其準(zhǔn)備好進行通用讀/寫。 當(dāng)函數(shù)成功時,返回值中的STA_NOINIT標(biāo)志被清除。此功能需要受FatFs模塊的控制。 應(yīng)用程序不得調(diào)用此函數(shù),否則可能會破壞卷上的FAT結(jié)構(gòu)。 要重新初始化文件系統(tǒng),請改用f_mount函數(shù)。
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
FatFs 調(diào)用此函數(shù),以從存儲設(shè)備的扇區(qū)讀取數(shù)據(jù)
- 參數(shù):
- pdrv:用于標(biāo)識目標(biāo)設(shè)備的物理驅(qū)動器號。
- buff:指向存儲讀取數(shù)據(jù)的字節(jié)數(shù)組的第一項的指針。 讀取數(shù)據(jù)的大小將是扇區(qū)大小*計數(shù)字節(jié)。
- sector:32 位 LBA(Logical Block Address,邏輯區(qū)塊地址) 的開始扇區(qū)號。
- count:要讀取的扇區(qū)數(shù)。
- 返回值:
- RES_OK:成功
- RES_ERROR:在讀取操作期間發(fā)生了不可恢復(fù)的硬錯誤。
- RES_PARERR:無效的參數(shù)
- RES_NOTRDY:設(shè)備尚未初始化。
??對通用存儲設(shè)備(例如存儲卡,hadddisk和光盤)的讀/寫操作是以稱為扇區(qū)的數(shù)據(jù)字節(jié)塊為單位完成的。 FatFs支持512到4096字節(jié)范圍內(nèi)的扇區(qū)大小。 當(dāng)FatF配置為固定扇區(qū)大小(FF_MIN_SS == FF_MAX_SS,這是大多數(shù)情況)時,讀/寫功能必須以該扇區(qū)大小工作。 當(dāng)FatFs配置為可變扇區(qū)大小(FF_MIN_SS <FF_MAX_SS)時,在disk_initialize函數(shù)成功后立即使用disk_ioctl函數(shù)查詢media的扇區(qū)大小。
??buff 指定的內(nèi)存地址并不總是與字邊界對齊,因為參數(shù)定義為BYTE* 類型。 未對齊的讀/寫請求可以在直接傳輸時發(fā)生。 如果總線體系結(jié)構(gòu)(尤其是DMA控制器)不允許未對齊的內(nèi)存訪問,則應(yīng)在此函數(shù)中處理該問題。 下面介紹了一些解決方法以避免此問題。
- 在此函數(shù)中使用某些方法將字轉(zhuǎn)換為字節(jié)來進行傳輸。
- 在 f_read() 調(diào)用中,避免包含整個扇區(qū)的長讀取請求。 - 任何直接傳輸都不會發(fā)生。
- 在 f_read(fp, dat, btw, bw) 調(diào)用中,確保 (((UINT)dat & 3) == (f_tell(fp) & 3)) 為真。 - 保證了 buff 的字對齊。
此外,DMA 可能無法訪問內(nèi)存區(qū)域。如果內(nèi)存區(qū)域位于通常用于堆棧的緊密耦合的內(nèi)存中,情況就是這樣。使用雙緩沖傳輸,或者避免將任何文件 I/O 緩沖區(qū)、FATFS 和 FIL 結(jié)構(gòu)定義為堆棧中的局部變量。
通常,多扇區(qū)讀請求不能被分割成對存儲設(shè)備的單扇區(qū)事務(wù),否則讀吞吐量會變得更差。
DRESULT disk_write (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
FatFs 調(diào)用此函數(shù),將數(shù)據(jù)寫入存儲設(shè)備的扇區(qū)
- 參數(shù):
- pdrv:用于標(biāo)識目標(biāo)設(shè)備的物理驅(qū)動器號。
- buff:指向要寫入的字節(jié)數(shù)組的第一項的指針。 要寫入的數(shù)據(jù)大小是扇區(qū)大小*計數(shù)字節(jié)。
- sector:32 位 LBA(Logical Block Address,邏輯區(qū)塊地址) 的開始扇區(qū)號。
- count:要寫的扇區(qū)數(shù)。
- 返回值:
- RES_OK:成功
- RES_ERROR:在寫入操作期間發(fā)生了不可恢復(fù)的硬錯誤。
- RES_WRPRT:介質(zhì)是寫保護的
- RES_PARERR:無效的參數(shù)
- RES_NOTRDY:設(shè)備尚未初始化。
??指定的內(nèi)存地址并不總是與字邊界對齊,因為參數(shù)定義為 BYTE*。 有關(guān)更多信息,請參閱 disk_read 函數(shù)的說明。
??通常,多扇區(qū)寫請求(計數(shù)> 1)不得拆分為存儲設(shè)備的單扇區(qū)事務(wù),否則文件寫吞吐量將大幅降低。
??FatFs期望磁盤控制層的寫入功能延遲。 該函數(shù)返回后,寫操作可能并沒有完成,因為媒體介質(zhì)可能正在執(zhí)行寫操作或者 僅僅是將數(shù)據(jù)放到了媒體介質(zhì)的緩沖區(qū)中。但是從此函數(shù)返回后,buff 中的數(shù)據(jù)將無效。 寫完成請求由 disk_ioctl 函數(shù)的 CTRL_SYNC 命令完成。 因此,如果實現(xiàn)延遲寫入功能,則將改善文件系統(tǒng)的寫入吞吐量。
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
FatFs 調(diào)用此函數(shù),來控制設(shè)備特定的功能和除通用讀/寫之外的其他功能。
- 參數(shù):
- pdrv:用于標(biāo)識目標(biāo)設(shè)備的物理驅(qū)動器號。
- cmd:命令代碼,在 diskio.h 中有定義。
- buff:指向參數(shù)的指針,是否有意義取決于命令代碼。 如果命令代碼不需要傳遞該參數(shù),則忽略即可。
- 返回值:
- RES_OK (0):成功
- RES_ERROR:出錯
- RES_PARERR:命令代碼或參數(shù)無效。
- RES_NOTRDY:設(shè)備尚未初始化。
FatFs模塊僅需要五個與設(shè)備無關(guān)的命令,如下所述:
| CTRL_SYNC | 用來確保設(shè)備已完成了掛起的寫入過程。 如果磁盤 I/O 模塊或存儲設(shè)備具有回寫高速緩存,則必須立即將標(biāo)記為臟的高速緩存數(shù)據(jù)寫回介質(zhì)。 如果在 disk_write 函數(shù)內(nèi)完成對介質(zhì)的每個寫操作,此命令中無需任何處理。 |
| GET_SECTOR_COUNT | 將驅(qū)動器上的可用扇區(qū)數(shù)返回到 buff 指向的 DWORD 類型的變量中。 f_mkfs 和 f_fdisk 函數(shù)使用此命令來確定要創(chuàng)建的卷/分區(qū)大小。 在配置了 FF_USE_MKFS == 1 時,必須要實現(xiàn)對該名利的處理。 |
| GET_SECTOR_SIZE | 將設(shè)備的扇區(qū)大小返回到 buff 指向的 WORD 類型的變量中。 此命令的有效返回值為 512,1024,2048和4096. 僅當(dāng)FF_MAX_SS > FF_MIN_SS 時才需要此命令。 當(dāng) FF_MAX_SS == FF_MIN_SS 時,從不使用此命令,并且設(shè)備必須以該扇區(qū)大小工作。 |
| GET_BLOCK_SIZE | 將以扇區(qū)為單位將閃存介質(zhì)的擦除塊大小返回到 buff 指向的 DWORD 類型的變量中。 允許值為 1 到 32768的 2 次方。如果擦除塊大小未知或非閃存介質(zhì),則返回1。 該命令僅由 f_mkfs 函數(shù)使用,它嘗試對齊擦除塊邊界上的數(shù)據(jù)區(qū)域。 在FF_USE_MKFS == 1 時 必須實現(xiàn)對改命令的處理 |
| CTRL_TRIM | 通知設(shè)備不再需要扇區(qū)塊上的數(shù)據(jù),并且可以擦除它。 扇區(qū)塊由 buff 指向的DWORD數(shù)組 {,} 指定。 這是一個與 ATA 設(shè)備修剪完全相同的命令。 如果不支持此功能或閃存設(shè)備不支持此命令,則無需執(zhí)行此操作。 FatFs 不檢查結(jié)果代碼,即使扇區(qū)塊沒有被很好地擦除,文件功能也不會受到影響。 該命令在刪除簇和 在 f_mkfs 函數(shù)中被調(diào)用。 在 FF_USE_TRIM == 1 時必須實現(xiàn)對改命令的處理 |
FatFs從不使用任何依賴于設(shè)備的命令或用戶定義的命令。下表顯示了一個可能對某些應(yīng)用程序有用的非標(biāo)準(zhǔn)命令示例。
| CTRL_FORMAT | 在媒體上創(chuàng)建物理格式。 如果 buff 不為 null,則它是指向進度通知的回調(diào)函數(shù)的指針。 |
| CTRL_POWER_IDLE | 將設(shè)備置于空閑狀態(tài)。 如果設(shè)備通過通用讀/寫功能進入活動狀態(tài),則可能不會設(shè)置當(dāng)前狀態(tài)標(biāo)志中的STA_NOINIT。 |
| CTRL_POWER_OFF | 將設(shè)備置為關(guān)閉狀態(tài)。 如果需要,關(guān)閉設(shè)備電源并取消初始化設(shè)備接口。 必須設(shè)置當(dāng)前狀態(tài)標(biāo)志中的STA_NOINIT。 設(shè)備通過disk_initialize功能進入活動狀態(tài)。 |
| CTRL_LOCK | 鎖定介質(zhì)彈出機制。 |
| CTRL_UNLOCK | 解鎖媒體彈出機制。 |
| CTRL_EJECT | 彈出介質(zhì)盒。 功能成功后,將設(shè)置狀態(tài)標(biāo)志中的STA_NOINIT和STA_NODISK。 |
| CTRL_GET_SMART | 閱讀SMART信息。 |
| MMC_GET_TYPE | 獲取卡類型。 類型標(biāo)志bit0:MMCv3,bit1:SDv1,bit2:SDv2 +和bit3:LBA存儲在buff指向的BYTE變量中。 (MMC / SDC特定命令) |
| MMC_GET_CSD | 將CSD寄存器讀入buff指向的16字節(jié)緩沖區(qū)。 (MMC / SDC特定命令) |
| MMC_GET_CID | 將CID寄存器讀入buff指向的16字節(jié)緩沖區(qū)。 (MMC / SDC特定命令) |
| MMC_GET_OCR | 將OCR寄存器讀入buff指向的4字節(jié)緩沖區(qū)。 (MMC / SDC特定命令) |
| MMC_GET_SDSTAT | 將SDSTATUS寄存器讀入buff指向的64字節(jié)緩沖區(qū)。 (SDC特定命令) |
| ATA_GET_REV | 將修訂字符串轉(zhuǎn)換為buff指向的16字節(jié)緩沖區(qū)。 (ATA / CFC特定命令) |
| ATA_GET_MODEL | 將模型字符串放入buff指向的40字節(jié)緩沖區(qū)中。 (ATA / CFC特定命令) |
| ATA_GET_SN | 將序列號字符串放入buff指向的20字節(jié)緩沖區(qū)中。 (ATA / CFC特定命令) |
| ISDIO_READ | 讀取由buff指向的命令結(jié)構(gòu)指定的iSDIO寄存器塊。 (FlashAir特定命令) |
| ISDIO_WRITE | 將數(shù)據(jù)塊寫入由buff指向的命令結(jié)構(gòu)指定的iSDIO寄存器。 (FlashAir特定命令) |
| ISDIO_MRITE | 更改由buff指向的命令結(jié)構(gòu)指定的iSDIO寄存器中的位。 (FlashAir特定命令) |
移植說明
第一步:源碼文件整理
??根據(jù)上面的介紹,其中,ff.c、ff.h、ffconf.h、diskio.c、diskio.h 這六個文件是最基本的,必須要包含在我們的項目中!除此之外,根據(jù)自己的配置:
- 如果需要 FatFs 支持可重入, 則 必須要包含 ffsystem.c, 且必須實現(xiàn) ffsystem.c 中的 ff_req_grant(), ff_rel_grant(), ff_del_syncobj() 和 ff_cre_syncobj() 這四個函數(shù)!
- 如果需要 FatFs 支持長文件名,則必須要包含 ffunicode.c,如果配置長文件名模式為 3 (FF_USE_LFN == 3),則必須同時包含 ffsystem.c, 且必須實現(xiàn) ffsystem.c 中的 ff_memalloc() 和 ff_memfree() 這兩個函數(shù)。
第二步:修改配置
??根據(jù)自己的需要修改 ffconf.h 中的各個配置項!
第三步:實現(xiàn)必要的函數(shù)
??首先要實現(xiàn) diskio.c/h 其中各接口!需要注意的是,一般的使用了 FatFs 的第三方庫,都會提供該文件的實現(xiàn)!例如,在 STM32 的USB 驅(qū)動庫中,有個名為 usbh_msc_fatfs.c 的文件,它其實就是 diskio.c 改了個名字而已。里面的函數(shù)就是 diskio.h 所聲明的那些函數(shù)!也因此在使用 STM32 的USB 驅(qū)動庫 + FatFs 時,我們不再需要 diskio.c了,但是 diskio.h 仍然是必須的!
下表顯示了需要實現(xiàn)的函數(shù)(部分需要看自己在ffconf.h 的配置):
在 FatFs 源碼的 diskio.c 中給出的示例是以默認(rèn)的配置來說的,在 ffconf.h 中,有如下配置:
我們必須根據(jù)自己的需求來更改。結(jié)合上文對于這個文件中每個函數(shù)的說明來實現(xiàn)即可!
第四步:實現(xiàn) get_fattime
??如果在 ffconf.h 中配置了 FF_FS_NORTC == 0,則必須自己實現(xiàn)函數(shù) DWORD get_fattime (void); 該函數(shù)返回一個 4 字節(jié)的時間戳!例如在上面的 STM32 的 USB 中,在 usbh_msc_fatfs.c 中 有 該函數(shù)的實現(xiàn),但是默認(rèn)為空(根據(jù)配置,用戶需要修改該函數(shù)實現(xiàn))!
??如果在 ffconf.h 中配置了 FF_FS_NORTC == 1,則可省略該函數(shù)的實現(xiàn)!具體見上文的說明即可!
該函數(shù)的格式:DWORD get_fattime (void);
- 參數(shù):
- 無
- 返回值:當(dāng)前本地時間應(yīng)作為包含在DWORD值中的位字段返回。 位字段如下:
- bit31:25:從 1980 年開始的年份 ,取值 0 ~ 127。例如 37 for 2017)
- bit24:21:月份(1…12)
- bit20:16:日期 (1…31)
- bit15:11:小時(0…23)
- bit10:5:分鐘 (0…59)
- bit4:0:秒 / 2 ( 0…29, e.g. 25 for 50)
第五步:應(yīng)用層使用
??經(jīng)過以上四步之后,就可以在項目中使用 FatFs了。可用的接口全部在于 ff.h 中,其與標(biāo)準(zhǔn) C 語言中各函數(shù)用法基本一致!
error: #20: identifier “BYTE” is undefined
??這個問題應(yīng)該是是由于沒有正確包含頭文件導(dǎo)致的。在 R0.13c 的時候,作者更新: Supported stdint.h for C99 and later. (integer.h was included in ff.h),然后刪除了原來的 integer.h。這樣,diskio.h 中也刪除了對于該文件的包含。這樣就導(dǎo)致了 diskio.h 中某些數(shù)據(jù)類型未定義!如下圖:
解決方法就是在 diskio.h 中包含數(shù)據(jù)類型的定義文件。根據(jù) 00history.txt 中的更新說明,現(xiàn)在的數(shù)據(jù)類型定義就在 ff.h 中。
參考
總結(jié)
以上是生活随笔為你收集整理的FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SourceInsight 4.0 之四
- 下一篇: ARM 之十 ARMCC(Keil) m