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