1. 程式人生 > >基於stm32-spi2的檔案系統(暫存器)

基於stm32-spi2的檔案系統(暫存器)

前段時間移植了一個FATFS檔案系統之後總是有人問我FATFS移植的問題,有時候很多問題都一樣,只是有些人太浮躁,沒有好好的把那不到1K的原始檔給看一遍而已,今天整理檔案,發現之前下載的一個很不錯的介紹FATFS的文章,裡面基本上把程式碼全部都註釋了,奉獻給大家。原文如下,地址已經找不到了,就不在附上了,感謝寫這篇文章的人。

=====================我是分割線============================================
最近做的spi flash,本打算弄個檔案系統,由於之前用過了JFFS、YAFFS和TrueFFS,程式碼量都相當的大,這次想找款程式碼量不那麼嚇人的,學習一下,聽說配置會相對複雜一些。選來選去,最終選定了FatFS,程式碼量足夠的小,最新的R0.09版本只有1個.c檔案(當然,還有一個底層的要自己寫,option資料夾裡的無視),老點版本就更小了。而且更新很頻繁,使用者量也夠大,就選定它了。儘管最後由於硬體和專案原因未能實際的移植它到vxWorks,但學過的還是要記錄下。
    在這裡
http://elm-chan.org/fsw/ff/00index_e.html
下載原始碼,只有800多K,小的可憐,還可以下載示例程式,有AVR、Win32、lpc等多平臺已實現的方案。開啟看src資料夾,一個option資料夾、00readme.txt、diskio.h、ff.c、ff.h、ffconf.h和interger.h。移植時需要修改的檔案主要包括ffconf.h和interger.h,後者是在它的定義與目標平臺上的有衝突,或者用的不習慣時修改的。
    在做具體修改之前,先大概閱讀下FatFS的原始碼,可以先讀integer.h,瞭解所用的資料型別,然後是ff.h,瞭解檔案系統所用的資料結構和各種函式宣告,再就是diskio.h,瞭解與介質相關的資料結構和操作函式。ff.c這個檔案相對較大,可以在最後將所實現的函式大致掃描一遍,之後根據使用者應用層程式呼叫函式的次序仔細閱讀相關程式碼。各個檔案都可以直接用記事本開啟查閱,非常方便。ff.h中的幾個結構體十分重要,列舉如下,首先是最基礎的檔案系統結構體:
  1. /* File system object structure (FATFS) */
  2. typedef struct {
  3.         BYTE        fs_type;                /* FAT子型別,一般在mount時用,置0表示未掛載*/
  4.         BYTE        drv;                        /* 物理驅動號,一般為0*/
  5.         BYTE        csize;                        /* 每個簇的扇區數目(1,2,4...128) */
  6.         BYTE        n_fats;                        /* 檔案分配表的數目(1,2) */
  7.         /*FAT檔案系統依次為:引導扇區、兩個檔案分配表、根目錄區和資料區*/
  8.         BYTE        wflag;                        /* 標記檔案是否被改動過,為1時要回寫*/
  9.         BYTE        fsi_flag;                /* 標記檔案系統資訊是否被改動過,為1時要回寫*/
  10.         WORD        id;                                /* 檔案系統掛載ID */
  11.         WORD        n_rootdir;                /* 根目錄區入口(目錄項)的個數(用於FAT12/16)*/
  12. #if _MAX_SS != 512
  13.         WORD        ssize;                        /* 每扇區的位元組數(用於扇區大於512Byte的flash) */
  14. #endif
  15. #if _FS_REENTRANT
  16.         _SYNC_t        sobj;                        /* 允許重入,即定義同步物件,用在tiny中*/
  17. #endif
  18. #if !_FS_READONLY
  19.         DWORD        last_clust;                /* 最後一個被分配的簇*/
  20.         DWORD        free_clust;                /* 空閒簇的個數*/
  21.         DWORD        fsi_sector;                /* 存放fsinfo的扇區(用於FAT32) */
  22. #endif
  23. #if _FS_RPATH
  24.         DWORD        cdir;                        /* 允許相對路徑時用,儲存當前目錄起始簇(0:root)*/
  25. #endif
  26.         DWORD        n_fatent;                /* FAT入口數(簇的數目 + 2)*/
  27.         DWORD        fsize;                        /* 每個FAT所佔扇區*/
  28.         DWORD        fatbase;                /* FAT起始扇區*/
  29.         DWORD        dirbase;                /* 根目錄起始扇區(FAT32:Cluster#) */
  30.         DWORD        database;                /* 資料目錄起始扇區*/
  31.         DWORD        winsect;                /* 當前緩衝區中儲存的扇區號*/
  32.         BYTE        win[_MAX_SS];        /* 單個扇區快取*/
  33. } FATFS;
複製程式碼 然後是與之相關的檔案和資料夾結構體,附上具體註釋:
  1. /* File object structure (FIL) */
  2. typedef struct {
  3.         FATFS*        fs;                                /* 所在的fs指標*/
  4.         WORD        id;                                /* 所在的fs掛載編號*/
  5.         BYTE        flag;                        /* 檔案狀態*/
  6.         BYTE        pad1;           /* 不知道含義,也未見程式使用*/
  7.         DWORD        fptr;                        /* 檔案讀寫指標*/
  8.         DWORD        fsize;                        /* 大小*/
  9.         DWORD        sclust;                        /* 檔案起始簇(fsize=0時為0) */
  10.         DWORD        clust;                        /* 當前簇*/
  11.         DWORD        dsect;                        /* 當前資料扇區*/
  12. #if !_FS_READONLY
  13.         DWORD        dir_sect;                /* 包含目錄項的扇區 */
  14.         BYTE*        dir_ptr;                /* Ponter to the directory entry in the window */
  15. #endif
  16. #if _USE_FASTSEEK
  17.         DWORD*        cltbl;                        /*指向簇連結對映表的指標*/
  18. #endif
  19. #if _FS_SHARE
  20.         UINT        lockid;                        /* File lock ID (index of file semaphore table) */
  21. #endif
  22. #if !_FS_TINY
  23.         BYTE        buf[_MAX_SS];        /* File data read/write buffer */
  24. #endif
  25. } FIL;
複製程式碼 下面是目錄的:
  1. /* Directory object structure (DIR) */
  2. typedef struct {
  3.         FATFS*        fs;                                /* 同上*/
  4.         WORD        id;
  5.         WORD        index;                        /* 當前讀寫索引號 */
  6.         DWORD        sclust;                        /* 檔案資料區開始簇*/
  7.         DWORD        clust;                        /* 當前簇*/
  8.         DWORD        sect;                        /* 當前扇區*/
  9.         BYTE*        dir;                        /* 扇區快取中當前SFN入口指標,SFN含義未知,猜測和LFN類似,與檔名相關*/
  10.         BYTE*        fn;                                /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
  11. #if _USE_LFN
  12.         WCHAR*        lfn;                        /* Pointer to the LFN working buffer */
  13.         WORD        lfn_idx;                /* Last matched LFN index number (0xFFFF:No LFN) */
  14. #endif
  15. } DIR;
複製程式碼 其他類似f_mount、f_open等介面API就不細說了,在掛載的時候其實真正起作用的是chk_mounted函式,在這裡才會將掛載分割槽的相關資訊分配到FatFS結構體中;還有一個get_fat函式,也比較重要,在f_open和許多目錄操作的函式中都有用到,而且FAT入口這個表達也十分晦澀,而它又呼叫了一個move_window的函式,也是十分晦澀難懂,可能是我英語太爛的緣故吧。實際上,move_window的作用是改變檔案系統的當前工作扇區,如果要遷移到的是當前扇區,直接返回,如果不是,就將原扇區寫回,若是FAT表,還要寫進備份區。
    熟悉了程式碼結構後,現在開始作修改了,首先修改ffconf.h檔案配置與硬體相關的檔案系統特性,然後自己新增一套底層操作即可。先看ffconf.h,裡面定義了很多巨集,可以根據自己需要一一配置:
    先看功能配置:
_FS_TINY:檔案系統為標準的還是微型的,預設為標準的(0);
_FS_READONLY:檔案系統是否為只讀,預設為可讀寫(0),若只讀則f_write、f_sync、 f_unlink、f_mkdir、f_chmod、f_rename、f_truncate和f_getfree不可用;
_FS_MINIMIZE:裁剪檔案系統的功能,預設為全部功能(0),若為1、2則會移除大部分連結、目錄等功能;
_USE_STRFUNC:是否允許字串操作,預設為不允許(0),這個看個人需求,一般情況下設定為1即可,如果工作在windows下,為保證檔案相容性(如換行符’\n’和回車符’\r’)建議將此項設定為2;
_USE_MKFS:是否允許使用f_mkfs函式,預設為0,用於建立資料夾,建議開啟;
_USE_FORWARD:用於允許f_forward函式,只有開啟tiny檔案系統時才用到,該函式用於將讀寫的資料立即轉存到資料流中,以節省RAM空間;
_USE_FASTSEEK:是否開啟快速索引,預設為0,開啟後,會使用FIL結構體中的cltbl元素來加快搜索;
_CODE_PAGE:指定目標系統使用的OEM內碼表,預設為日語(932),改為936簡體中文;OEM是什麼意思呢?在OS編碼中,unicode是一種雙位元組字元編碼,無論中文還是英文,或者其他語言統一到2個位元組,它與現有的任何編碼(ASCII,GB等)都不相容。WindowsNT(2000)的核心即使用該編碼,所有資料進入核心前轉換成UNICODE,退出核心後在轉換成版本相關的編碼(通常稱為OEM,在簡體中文版下即為GB);
_USE_LEN、_MAX_LEN、_LFN_UNICODE:這三個的意思不是很清楚,但是確定是與長檔名有關的,不建議開啟,否則又要多加函式,麻煩;
_FS_RPATH:是否允許相對路徑,讓我選擇就不開啟,否則邏輯變得複雜不說,程式碼量也變多了一些;
    再看硬體相關配置:
_VOLUMES:磁碟(flash)邏輯卷數,預設為1,不建議修改;
_MAX_SS:扇區大小,預設512Byte,最大可設定4096Byte;
_MULTI_PARTITION:分割槽選項,預設為0,即一個分割槽,若想要多分割槽可自行設定;
_USE_ERASE:是否允許扇區擦除,預設為0,若開啟則要在disk_ioctl函式中新增擦除命令程式碼;
    最後是檔案系統配置:
_WORD_ACCESS:資料遞進格式,預設為0,即以位元組為單位遞進,相容性更強,若你的系統最新單位為字(2Byte),則可設為1;
_FS_REENTRANT、_FS_TIMEOUT、_SYNC_t:這三個選項與檔案系統是否允許重入有關,所直白點,就是能否被多執行緒同時訪問,像RTOS中,一般建議開啟,_SYNC_t可定義為對應OS中的操作物件,windows下為HANDLE,uCos中為OS_EVENT,vxWorks中為SEMAPHORE。另外,開啟後還需要新增ff_req_grant、ff_rel_grant和ff_del_syncobj三個函式,實際上實現的功能就是申請互斥量、釋放互斥量和刪除互斥量的意思,可以定義OS封裝即可;
_FS_SHARE:和上面的類似,表示檔案系統最大允許同時開啟多少檔案,預設為0,即只能開啟一個。
    在配置這些選項的時候,可以根據定義閱讀ff.c檔案中的相關程式碼,基本上能對整體的結果有了瞭解,完成了ffconf.h後,再就是編寫底層介面了,在新一點的FatFs中,並未提供函式介面模版,可以下老版的拷過來,也可以開啟doc資料夾下的幫助文件00index_e.htm檔案,裡面有底層函式介面的格式及各個引數的描述。至於底層驅動,我只做過spi flash的,這個可以參考我上一篇文章。需要注意的是,底層讀寫函式中的引數sector指的是扇區的序號,需要自己換算成驅動介面中的位元組位置。
    到這裡,移植基本完成了,如果你的檔案系統出現LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))有問題(資料異常終止DATA ABORT exception之類的)的情況,請百度搜索“轉一篇比較詳細介紹FatFS檔案系統移植的文章”就可以搞定了,那裡有詳細的解決辦法