最近做的spi flash,本打算弄個(gè)文件系統(tǒng),由于之前用過了JFFS、YAFFS和TrueFFS,代碼量都相當(dāng)?shù)拇?,這次想找款代碼量不那么嚇人的,學(xué)習(xí)一下,聽說配置會(huì)相對復(fù)雜一些。選來選去,最終選定了FatFS,代碼量足夠的小,最新的R0.09版本只有1個(gè).c文件(當(dāng)然,還有一個(gè)底層的要自己寫,option文件夾里的無視),老點(diǎn)版本就更小了。而且更新很頻繁,用戶量也夠大,就選定它了。盡管最后由于硬件和項(xiàng)目原因未能實(shí)際的移植它到vxWorks,但學(xué)過的還是要記錄下。
在這里http://elm-chan.org/fsw/ff/00index_e.html下載源碼,只有800多K,小的可憐,還可以下載示例程序,有AVR、Win32、lpc等多平臺已實(shí)現(xiàn)的方案。打開看src文件夾,一個(gè)option文件夾、00readme.txt、diskio.h、ff.c、ff.h、ffconf.h和interger.h。移植時(shí)需要修改的文件主要包括ffconf.h和interger.h,后者是在它的定義與目標(biāo)平臺上的有沖突,或者用的不習(xí)慣時(shí)修改的。
在做具體修改之前,先大概閱讀下FatFS的源代碼,可以先讀integer.h,了解所用的數(shù)據(jù)類型,然后是ff.h,了解文件系統(tǒng)所用的數(shù)據(jù)結(jié)構(gòu)和各種函數(shù)聲明,再就是diskio.h,了解與介質(zhì)相關(guān)的數(shù)據(jù)結(jié)構(gòu)和操作函數(shù)。ff.c這個(gè)文件相對較大,可以在最后將所實(shí)現(xiàn)的函數(shù)大致掃描一遍,之后根據(jù)用戶應(yīng)用層程序調(diào)用函數(shù)的次序仔細(xì)閱讀相關(guān)代碼。各個(gè)文件都可以直接用記事本打開查閱,非常方便。ff.h中的幾個(gè)結(jié)構(gòu)體十分重要,列舉如下,首先是最基礎(chǔ)的文件系統(tǒng)結(jié)構(gòu)體:
view plaincopy to clipboardprint?
/*?File?system?object?structure?(FATFS)?*/??
typedef?struct?{??
BYTE????fs_type;????????/*?FAT子類型,一般在mount時(shí)用,置0表示未掛載*/??
BYTE????drv;????????????/*?物理驅(qū)動(dòng)號,一般為0*/??
BYTE????csize;??????????/*?每個(gè)簇的扇區(qū)數(shù)目(1,2,4...128)?*/??
BYTE????n_fats;?????????/*?文件分配表的數(shù)目(1,2)?*/??
/*FAT文件系統(tǒng)依次為:引導(dǎo)扇區(qū)、兩個(gè)文件分配表、根目錄區(qū)和數(shù)據(jù)區(qū)*/??
BYTE????wflag;??????????/*?標(biāo)記文件是否被改動(dòng)過,為1時(shí)要回寫*/??
BYTE????fsi_flag;???????/*?標(biāo)記文件系統(tǒng)信息是否被改動(dòng)過,為1時(shí)要回寫*/??
WORD????id;?????????????/*?文件系統(tǒng)掛載ID?*/??
WORD????n_rootdir;??????/*?根目錄區(qū)入口(目錄項(xiàng))的個(gè)數(shù)(用于FAT12/16)*/??
#if?_MAX_SS?!=?512 ??
WORD????ssize;??????????/*?每扇區(qū)的字節(jié)數(shù)(用于扇區(qū)大于512Byte的flash)?*/??
#endif ??
#if?_FS_REENTRANT ??
_SYNC_t?sobj;???????????/*?允許重入,即定義同步對象,用在tiny中*/??
#endif ??
#if?!_FS_READONLY ??
DWORD???last_clust;?????/*?最后一個(gè)被分配的簇*/??
DWORD???free_clust;?????/*?空閑簇的個(gè)數(shù)*/??
DWORD???fsi_sector;?????/*?存放fsinfo的扇區(qū)(用于FAT32)?*/??
#endif ??
#if?_FS_RPATH ??
DWORD???cdir;???????????/*?允許相對路徑時(shí)用,存儲(chǔ)當(dāng)前目錄起始簇(0:root)*/??
#endif ??
DWORD???n_fatent;???????/*?FAT入口數(shù)(簇的數(shù)目?+?2)*/??
DWORD???fsize;??????????/*?每個(gè)FAT所占扇區(qū)*/??
DWORD???fatbase;????????/*?FAT起始扇區(qū)*/??
DWORD???dirbase;????????/*?根目錄起始扇區(qū)(FAT32:Cluster#)?*/??
DWORD???database;???????/*?數(shù)據(jù)目錄起始扇區(qū)*/??
DWORD???winsect;????????/*?當(dāng)前緩沖區(qū)中存儲(chǔ)的扇區(qū)號*/??
BYTE????win[_MAX_SS];???/*?單個(gè)扇區(qū)緩存*/??
}?FATFS;??
/* File system object structure (FATFS) */typedef struct {BYTEfs_type;/* FAT子類型,一般在mount時(shí)用,置0表示未掛載*/BYTEdrv;/* 物理驅(qū)動(dòng)號,一般為0*/BYTEcsize;/* 每個(gè)簇的扇區(qū)數(shù)目(1,2,4...128) */BYTEn_fats;/* 文件分配表的數(shù)目(1,2) *//*FAT文件系統(tǒng)依次為:引導(dǎo)扇區(qū)、兩個(gè)文件分配表、根目錄區(qū)和數(shù)據(jù)區(qū)*/BYTEwflag;/* 標(biāo)記文件是否被改動(dòng)過,為1時(shí)要回寫*/BYTEfsi_flag;/* 標(biāo)記文件系統(tǒng)信息是否被改動(dòng)過,為1時(shí)要回寫*/WORDid;/* 文件系統(tǒng)掛載ID */WORDn_rootdir;/* 根目錄區(qū)入口(目錄項(xiàng))的個(gè)數(shù)(用于FAT12/16)*/#if _MAX_SS != 512WORDssize;/* 每扇區(qū)的字節(jié)數(shù)(用于扇區(qū)大于512Byte的flash) */#endif#if _FS_REENTRANT_SYNC_tsobj;/* 允許重入,即定義同步對象,用在tiny中*/#endif#if !_FS_READONLYDWORDlast_clust;/* 最后一個(gè)被分配的簇*/DWORDfree_clust;/* 空閑簇的個(gè)數(shù)*/DWORDfsi_sector;/* 存放fsinfo的扇區(qū)(用于FAT32) */#endif#if _FS_RPATHDWORDcdir;/* 允許相對路徑時(shí)用,存儲(chǔ)當(dāng)前目錄起始簇(0:root)*/#endifDWORDn_fatent;/* FAT入口數(shù)(簇的數(shù)目 + 2)*/DWORDfsize;/* 每個(gè)FAT所占扇區(qū)*/DWORDfatbase;/* FAT起始扇區(qū)*/DWORDdirbase;/* 根目錄起始扇區(qū)(FAT32:Cluster#) */DWORDdatabase;/* 數(shù)據(jù)目錄起始扇區(qū)*/DWORDwinsect;/* 當(dāng)前緩沖區(qū)中存儲(chǔ)的扇區(qū)號*/BYTEwin[_MAX_SS];/* 單個(gè)扇區(qū)緩存*/} FATFS;
然后是與之相關(guān)的文件和文件夾結(jié)構(gòu)體,附上具體注釋:
view plaincopy to clipboardprint?
/*?File?object?structure?(FIL)?*/??
typedef?struct?{??
FATFS*??fs;?????????????/*?所在的fs指針*/??
WORD????id;?????????????/*?所在的fs掛載編號*/??
BYTE????flag;???????????/*?文件狀態(tài)*/??
BYTE????pad1;???????????/*?不知道含義,也未見程序使用*/??
DWORD???fptr;???????????/*?文件讀寫指針*/??
DWORD???fsize;??????????/*?大小*/??
DWORD???sclust;?????????/*?文件起始簇(fsize=0時(shí)為0)?*/??
DWORD???clust;??????????/*?當(dāng)前簇*/??
DWORD???dsect;??????????/*?當(dāng)前數(shù)據(jù)扇區(qū)*/??
#if?!_FS_READONLY ??
DWORD???dir_sect;???????/*?包含目錄項(xiàng)的扇區(qū)?*/??
BYTE*???dir_ptr;????????/*?Ponter?to?the?directory?entry?in?the?window?*/??
#endif ??
#if?_USE_FASTSEEK ??
DWORD*??cltbl;??????????/*指向簇鏈接映射表的指針*/??
#endif ??
#if?_FS_SHARE ??
UINT????lockid;?????????/*?File?lock?ID?(index?of?file?semaphore?table)?*/??
#endif ??
#if?!_FS_TINY ??
BYTE????buf[_MAX_SS];???/*?File?data?read/write?buffer?*/??
#endif ??
}?FIL;??
/* File object structure (FIL) */typedef struct {FATFS*fs;/* 所在的fs指針*/WORDid;/* 所在的fs掛載編號*/BYTEflag;/* 文件狀態(tài)*/BYTEpad1; /* 不知道含義,也未見程序使用*/DWORDfptr;/* 文件讀寫指針*/DWORDfsize;/* 大小*/DWORDsclust;/* 文件起始簇(fsize=0時(shí)為0) */DWORDclust;/* 當(dāng)前簇*/DWORDdsect;/* 當(dāng)前數(shù)據(jù)扇區(qū)*/#if !_FS_READONLYDWORDdir_sect;/* 包含目錄項(xiàng)的扇區(qū) */BYTE*dir_ptr;/* Ponter to the directory entry in the window */#endif#if _USE_FASTSEEKDWORD*cltbl;/*指向簇鏈接映射表的指針*/#endif#if _FS_SHAREUINTlockid;/* File lock ID (index of file semaphore table) */#endif#if !_FS_TINYBYTEbuf[_MAX_SS];/* File data read/write buffer */#endif} FIL;
下面是目錄的:
view plaincopy to clipboardprint?
/*?Directory?object?structure?(DIR)?*/??
typedef?struct?{??
FATFS*??fs;?????????????/*?同上*/??
WORD????id;??
WORD????index;??????????/*?當(dāng)前讀寫索引號?*/??
DWORD???sclust;?????????/*?文件數(shù)據(jù)區(qū)開始簇*/??
DWORD???clust;??????????/*?當(dāng)前簇*/??
DWORD???sect;???????????/*?當(dāng)前扇區(qū)*/??
BYTE*???dir;????????????/*?扇區(qū)緩存中當(dāng)前SFN入口指針,SFN含義未知,猜測和LFN類似,與文件名相關(guān)*/??
BYTE*???fn;?????????????/*?Pointer?to?the?SFN?(in/out)?{file[8],ext[3],status[1]}?*/??
#if?_USE_LFN ??
WCHAR*??lfn;????????????/*?Pointer?to?the?LFN?working?buffer?*/??
WORD????lfn_idx;????????/*?Last?matched?LFN?index?number?(0xFFFF:No?LFN)?*/??
#endif ??
}?DIR;??
/* Directory object structure (DIR) */typedef struct {FATFS*fs;/* 同上*/WORDid;WORDindex;/* 當(dāng)前讀寫索引號 */DWORDsclust;/* 文件數(shù)據(jù)區(qū)開始簇*/DWORDclust;/* 當(dāng)前簇*/DWORDsect;/* 當(dāng)前扇區(qū)*/BYTE*dir;/* 扇區(qū)緩存中當(dāng)前SFN入口指針,SFN含義未知,猜測和LFN類似,與文件名相關(guān)*/BYTE*fn;/* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */#if _USE_LFNWCHAR*lfn;/* Pointer to the LFN working buffer */WORDlfn_idx;/* Last matched LFN index number (0xFFFF:No LFN) */#endif} DIR;
其他類似f_mount、f_open等接口API就不細(xì)說了,在掛載的時(shí)候其實(shí)真正起作用的是chk_mounted函數(shù),在這里才會(huì)將掛載分區(qū)的相關(guān)信息分配到FatFS結(jié)構(gòu)體中;還有一個(gè)get_fat函數(shù),也比較重要,在f_open和許多目錄操作的函數(shù)中都有用到,而且FAT入口這個(gè)表達(dá)也十分晦澀,而它又調(diào)用了一個(gè)move_window的函數(shù),也是十分晦澀難懂,可能是我英語太爛的緣故吧。實(shí)際上,move_window的作用是改變文件系統(tǒng)的當(dāng)前工作扇區(qū),如果要遷移到的是當(dāng)前扇區(qū),直接返回,如果不是,就將原扇區(qū)寫回,若是FAT表,還要寫進(jìn)備份區(qū)。
熟悉了代碼結(jié)構(gòu)后,現(xiàn)在開始作修改了,首先修改ffconf.h文件配置與硬件相關(guān)的文件系統(tǒng)特性,然后自己添加一套底層操作即可。先看ffconf.h,里面定義了很多宏,可以根據(jù)自己需要一一配置:
先看功能配置:
_FS_TINY:文件系統(tǒng)為標(biāo)準(zhǔn)的還是微型的,默認(rèn)為標(biāo)準(zhǔn)的(0);
_FS_READONLY:文件系統(tǒng)是否為只讀,默認(rèn)為可讀寫(0),若只讀則f_write、f_sync、 f_unlink、f_mkdir、f_chmod、f_rename、f_truncate和f_getfree不可用;
_FS_MINIMIZE:裁剪文件系統(tǒng)的功能,默認(rèn)為全部功能(0),若為1、2則會(huì)移除大部分鏈接、目錄等功能;
_USE_STRFUNC:是否允許字符串操作,默認(rèn)為不允許(0),這個(gè)看個(gè)人需求,一般情況下設(shè)置為1即可,如果工作在windows下,為保證文件兼容性(如換行符’\n’和回車符’\r’)建議將此項(xiàng)設(shè)置為2;
_USE_MKFS:是否允許使用f_mkfs函數(shù),默認(rèn)為0,用于創(chuàng)建文件夾,建議開啟;
_USE_FORWARD:用于允許f_forward函數(shù),只有開啟tiny文件系統(tǒng)時(shí)才用到,該函數(shù)用于將讀寫的數(shù)據(jù)立即轉(zhuǎn)存到數(shù)據(jù)流中,以節(jié)省RAM空間;
_USE_FASTSEEK:是否開啟快速索引,默認(rèn)為0,開啟后,會(huì)使用FIL結(jié)構(gòu)體中的cltbl元素來加快搜索;
_CODE_PAGE:指定目標(biāo)系統(tǒng)使用的OEM代碼頁,默認(rèn)為日語(932),改為936簡體中文;OEM是什么意思呢?在OS編碼中,unicode是一種雙字節(jié)字符編碼,無論中文還是英文,或者其他語言統(tǒng)一到2個(gè)字節(jié),它與現(xiàn)有的任何編碼(ASCII,GB等)都不兼容。WindowsNT(2000)的內(nèi)核即使用該編碼,所有數(shù)據(jù)進(jìn)入內(nèi)核前轉(zhuǎn)換成UNICODE,退出內(nèi)核后在轉(zhuǎn)換成版本相關(guān)的編碼(通常稱為OEM,在簡體中文版下即為GB);
_USE_LEN、_MAX_LEN、_LFN_UNICODE:這三個(gè)的意思不是很清楚,但是確定是與長文件名有關(guān)的,不建議開啟,否則又要多加函數(shù),麻煩;
_FS_RPATH:是否允許相對路徑,讓我選擇就不開啟,否則邏輯變得復(fù)雜不說,代碼量也變多了一些;
再看硬件相關(guān)配置:
_VOLUMES:磁盤(flash)邏輯卷數(shù),默認(rèn)為1,不建議修改;
_MAX_SS:扇區(qū)大小,默認(rèn)512Byte,最大可設(shè)置4096Byte;
_MULTI_PARTITION:分區(qū)選項(xiàng),默認(rèn)為0,即一個(gè)分區(qū),若想要多分區(qū)可自行設(shè)置;
_USE_ERASE:是否允許扇區(qū)擦除,默認(rèn)為0,若開啟則要在disk_ioctl函數(shù)中添加擦除命令代碼;
最后是文件系統(tǒng)配置:
_WORD_ACCESS:數(shù)據(jù)遞進(jìn)格式,默認(rèn)為0,即以字節(jié)為單位遞進(jìn),兼容性更強(qiáng),若你的系統(tǒng)最新單位為字(2Byte),則可設(shè)為1;
_FS_REENTRANT、_FS_TIMEOUT、_SYNC_t:這三個(gè)選項(xiàng)與文件系統(tǒng)是否允許重入有關(guān),所直白點(diǎn),就是能否被多線程同時(shí)訪問,像RTOS中,一般建議開啟,_SYNC_t可定義為對應(yīng)OS中的操作對象,windows下為HANDLE,uCos中為OS_EVENT,vxWorks中為SEMAPHORE。另外,開啟后還需要添加ff_req_grant、ff_rel_grant和ff_del_syncobj三個(gè)函數(shù),實(shí)際上實(shí)現(xiàn)的功能就是申請互斥量、釋放互斥量和刪除互斥量的意思,可以定義OS封裝即可;
_FS_SHARE:和上面的類似,表示文件系統(tǒng)最大允許同時(shí)打開多少文件,默認(rèn)為0,即只能打開一個(gè)。
在配置這些選項(xiàng)的時(shí)候,可以根據(jù)定義閱讀ff.c文件中的相關(guān)代碼,基本上能對整體的結(jié)果有了了解,完成了ffconf.h后,再就是編寫底層接口了,在新一點(diǎn)的FatFs中,并未提供函數(shù)接口模版,可以下老版的拷過來,也可以打開doc文件夾下的幫助文檔00index_e.htm文件,里面有底層函數(shù)接口的格式及各個(gè)參數(shù)的描述。至于底層驅(qū)動(dòng),我只做過spi flash的,這個(gè)可以參考我上一篇文章。需要注意的是,底層讀寫函數(shù)中的參數(shù)sector指的是扇區(qū)的序號,需要自己換算成驅(qū)動(dòng)接口中的字節(jié)位置。
到這里,移植基本完成了,如果你的文件系統(tǒng)出現(xiàn)LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))有問題(數(shù)據(jù)異常終止DATA ABORT exception之類的)的情況,請百度搜索“轉(zhuǎn)一篇比較詳細(xì)介紹FatFS文件系統(tǒng)移植的文章”就可以搞定了,那里有詳細(xì)的解決辦法。
?
評論