fatfs源码阅读
fatfs源碼閱讀
使用fatfs文件的第一步,就是調用F_mount函數注冊一個工作空間。
?
F_mount函數的原型如下:
?
?
第一個參數根據網上大神的答復,是外設類型,如果是sd卡就是0,flash等等其他的外設就是其他得數,據說有定義,不過我沒找到。
第二個參數FATFS指針就是工作空間的指針,個人感覺有點lwip網卡數據結構的感覺。
?
?
FATFS數據結構及解釋如下,個人感覺了解FATFS這個工作空間數據結構是什么東西就行:
typedef?struct?{????BYTE????fs_type;??????/*?FAT?sub-type?(0:Not?mounted)?*/????BYTE????drv;??????????/*?Physical?drive?number?*/????BYTE????csize;????????/*每簇里有多少個扇區?(一般一簇是4KB?一扇區是512B)*/????BYTE????n_fats;???????/*?FAT表的數目??一般是兩個?*/????BYTE????wflag;????????/*?win[?]?dirty?flag?*/????BYTE????fsi_flag;?????/*?fsinfo?區域臟數據標志?*/????WORD????id;???????????/*?File?system?mount?ID?*/????WORD????n_rootdir;????/*?Number?of?root?directory?entries?(FAT12/16)?*/#if?_MAX_SS?!=?512????WORD????ssize;????????/*?扇區大小??對應著sd卡中的block??一般是512B*/
#endif
#if?_FS_REENTRANT????_SYNC_t?sobj;?????????/*?Identifier?of?sync?object?*/#endif
#if?!_FS_READONLY????DWORD???last_clust;???/*?Last?allocated?cluster?*/????DWORD???free_clust;???/*?空閑簇的數目*/????DWORD???fsi_sector;???/*?fsinfo?sector?(FAT32)?*/
#endif#if?_FS_RPATH????DWORD???cdir;?????????/*?Current?directory?cluster?(0:root)?*/#endif????DWORD???n_fatent;?????/*?Number?of?FAT?entries?(==?Number?of?clusters?+?2)?*/????DWORD???fsize;????????/*FAT表對應著多少個扇區*/????DWORD???fatbase;??????/*FAT表的起始扇區?*/????DWORD???dirbase;??????/*?根目錄的起始扇區*/????DWORD???database;?????/*?數據區的起始扇區?*/????DWORD???winsect;??????/*?Current?sector?appearing?in?the?win[?]?*/????BYTE????win[_MAX_SS];?/*?Disk?access?window?for?Directory,?FAT?(and?file?data?on?tiny?cfg)?*/}?FATFS;
?
?
注冊的FATFS并不在f_mount函數中完成對其屬性的初始化,而是第一次調用f_open發現還沒有初始化的時候在完成初始化。f_mount函數中做得事情很簡單,如下:
?
?
?
FRESULT?f_mount?(
BYTE?vol, /*?Logical?drive?number?to?be?mounted/unmounted?*/
FATFS?*fs /*?Pointer?to?new?file?system?object?(NULL?for?unmount)*/
)
{
FATFS?*rfs;
?
?
if?(vol?>=?_VOLUMES) /*?Check?if?the?drive?number?is?valid?*/
return?FR_INVALID_DRIVE;
rfs?=?FatFs[vol]; /*?Get?current?fs?object?*/
?
if?(rfs)?{
rfs->fs_type?=?0; /*?Clear?old?fs?object?*/
}
?
if?(fs)?{
fs->fs_type?=?0; /*?Clear?new?fs?object?*/
}
FatFs[vol]?=?fs; /*?Register?new?fs?object?*/
?
return?FR_OK;
}
?
?
我刪去了一些沒用的條件編譯,發現最后的代碼是上面的代碼,其實就是將fatfs文件系統已經定義好的FatFs[vol]指針數組的0號指向我們傳進來的工作空間。
?------------------------------------------------------------------------------------------------------------
這就是一個文件對象的屬性:
typedef?struct?{????FATFS*??fs;???????????/*?指向屬于自己的文件系統?*/????WORD????id;???????????/*?Owner?file?system?mount?ID?*/????BYTE????flag;?????????/*文件狀態標識*/????BYTE????pad1;????DWORD???fptr;?????????/*?文件讀寫指針?(Byte?offset?origin?from?top?of?the?file)?*/????DWORD???fsize;????????/*文件大小*/????DWORD???sclust;???????/*文檔開始扇區?*/????DWORD???clust;????????/*?當前扇區?*/????DWORD???dsect;????????/*?Current?data?sector?*/
#if?!_FS_READONLY????DWORD???dir_sect;?????/*?Sector?containing?the?directory?entry?*/????BYTE*???dir_ptr;??????/*?Ponter?to?the?directory?entry?in?the?window?*/
#endif
#if?!_FS_TINY????BYTE????buf[_MAX_SS];?/*?Data?read/write?buffer?*/
#endif}?FIL;
?
上面的是一個文件對象的屬性,f_open函數就是用來填充它,而f_close函數則是主要用來將其fs屬性置為空,以此讓此文件對象失效。
?
F_open:
打開一個文件時,有五個選項:
???????FA_READ:以讀方式打開文件
???????FA_WRITE:以寫方式打開文件,和上一個組合就變成了以讀寫的方式打開文件
???????FA_OPEN_EXISTING:打開的文件必須已經存在,如不存在函數失敗
???????FA_OPEN_ALWAYS:如果不存在這個文件,則新建,如果存在這個文件,則在后面添加。用這種方法打開文件要之后調用?f_lseek方法。
???????FA_CREATE_NEW:創建一個新文件,如果已經存在,則函數失敗。
???????FA_CREATE_ALWAYS:創建一個新文件,如果文件存在,則覆蓋。
?
?
在f_open函數中,調用了下面這個函數:
?
這個函數會進行檢查,如果文件系統對象還不是有效的還不是有效的,則會初始化它。
?
F_close:
?????用來將文件對象的fs屬性置為空,以此讓此文件對象失效。其中調用f_sync函數,來將緩沖區的數據寫入sd卡。
?
?
?----------------------------------------------------------------------------------------------------------
比較正常的兩個函數F_READ和F_WRITE。
?
F_READ:
?
第一個參數:文件對象
第二個參數:讀取所用的的緩沖區
第三個參數:想要讀多少字節的數據
第四個參數:這個參數實際的功能是作為一個返回值,告訴使用者函數完成后實際上讀了多少數值,那么如果函數完成后*byteread<bytetoread,那就說明這個文件本身沒有bytetoread那么大。
?
F_WRITE:
?
這個函數只有在_FS_READONLY?==?0時才可以用。
第一個參數:文件對象
第二個參數:針對寫入所用的的緩沖區
第三個參數:想要寫入少字節的數據
第四個參數:這個參數也是函數的一個返回值,當函數成功執行之后,如果第四個指針所指向的值小于第三個傳進去的參數,那就說明,磁盤滿了。
?
詳細步驟如下:
準備開始寫,先查看一下當前的文件的字節數是不是512的整數倍,如果是的話再查看flag標志中,通過查看可以得出緩沖區是不是又一個完整扇區的數據,如果有的話先將緩沖區中一個扇區的數據寫入sd卡,寫入512*最大整數倍的數據。剩下的存入緩沖區中。
準備開始寫,先查看一下當前的文件的字節數是不是512的整數倍,如果不是,說明緩沖區中還有部分數據,那么就用要寫入的數據中的一部分填滿緩沖區,更新flag標志,然后回到上一段文字。
?
所謂的緩沖區就是文件對象的buf[]屬性。
?--------------------------------------------------------------------------------------------------------------
? ? f_read和f_write函數都只能從文件開頭開始讀寫,那么這個時候就需要一個函數來移動當前指針,配合f_read和f_write函數,這個函數就是f_lseek。
函數原型如下:
?
第一個參數:文件對象的指針
第二個參數:從文件開始處向后移動多少
?
Offset是指相對于文件起始處的字節數。
在寫模式寫了一個超過文件大小的offset,文件將被拓展,并且拓展區域的數據是未定義的。這可以用來迅速的創建一個大文件。
F_lseek函數成功后,為確保指針已經以已經成功移動,必須檢查文件對象中的fptr的值,如果其不為所期望的值則說明:1.文件以只讀打開,指針只能移動到文件結束處。2.磁盤滿了。
?
示例:
?
------------------------------------------------------------------------------------------------------------
F_truncate:
?
縮短文件大小,將文件尾縮短到當前指針處,如果當前指針已經在文件末尾,則不會發生任何改變。
?
f_sync:
?
介紹這個函數之前先詳細的說一下fatfs的f_write的流程,此流程為讀文件系統代碼得出:
準備開始寫,先查看一下當前的文件的字節數是不是512的整數倍,如果是的話再查看flag標志中,通過查看可以得出緩沖區是不是又一個完整扇區的數據,如果有的話先將緩沖區中一個扇區的數據寫入sd卡,寫入512*最大整數倍的數據。剩下的存入緩沖區中。
準備開始寫,先查看一下當前的文件的字節數是不是512的整數倍,如果不是,說明緩沖區中還有部分數據,那么就用要寫入的數據中的一部分填滿緩沖區,更新flag標志,然后回到上一段文字。
?
那么也就說我們不斷操作的過程中,其實有可能有一部分文本信息并未真正的寫入sd卡中,而是存儲在文件的buf數組也就是緩沖區里。那么f_sync的作用就是把緩沖區的數據強制寫入sd卡中,這樣的話,如果寫入文件的持續時間長,周期性的的f_sync一次,可以盡量的保證斷電等意外突發情況數據損失減小。
??
對,你沒猜錯,f_close函數中會調用這個函數以保證關閉文件之后所有數據均以寫入sd卡。所以這個函數利f_close唯一的區別就在于函數調用之后文件不失效。
?
因為sd卡每次最少寫入512字節,所以這次寫入不足512字節,那么就用無用的數據填補夠512字節,而這部分數據其實還留在緩沖區,下次再寫的時候直接用新的數據將這次不足的還有一部分無用信息的數據覆蓋掉。(本段為猜測)
?
?
F_opendir:
?
可以打開一個目錄,也可以創建一個目錄。該目錄對象可以不經過任意函數直接丟棄。
?
f_readdir:
?
讀取一個目錄中的項目(這個項目可能是個文件,也可能是個目錄),將返回的這一個項目的信息存在FILINFO中,一次返回一個,不停的重復調用這個函數,就能得到這個目錄下所有項目的信息了。當讀完之后,再次調用這個函數,FILINFO中的f_name屬性為空,可以此來判斷是不是讀取完了所有的該目錄下的項目。
???注:fatfs支持長文件名,需_USE_LFN宏為真,FILINFO數據結構中有相應的屬性處理長文件名。FILINFO數據結構如下:
?
???
使用示例:
?
?f_getfree:
?
????這個函數使用來得到空閑的簇的的數目并把文件系統的信息通過第三個參數返回。通過文件系統數據結構的屬性同樣能計算出空閑的簇數。
?
f_stat:
?
獲取一個文件或者目錄的相關信息。返回到FILINFO結構體中。
?
f_mkdir:
?
就是新建一個目錄。
用法如下:
?
?
?
f_unlink:
?
????刪除一個文件或者目錄,但要注意以下幾點:
1.文件或者目錄不能使只讀的。
2.目錄的話一定要是空的并且不能是當前目錄。
3.文件的話不能是打開的。
?
f_chmod:
?
????第一個參數:文件名
第二個參數:待被設置的屬性標志
第三個參數:要被修改的屬性標志
?
首先文件的屬性有以下幾種:
?
那么這個函數的第二個參數和第三個參數是什么意思呢?第二個參數是指函數要把文件要設置成那種屬性,第三個參數是指函數要修改那些屬性,也就是說出現在第三個參數而沒有出現在第二個參數就是要清除的標志,比如:
?
就是代表設置只讀標志,清除讀檔標志。
?
?
f_utime:
?
修改一個目錄的時間戳,把想要修改成的數據通過第二個參數傳入。
使用例子:
?
?
?
f_rename:
?
????可以用來重命名一個文件,也可用來移動一個文件,如下:
?
????注意,移動文件不能跨設備移動,比如sd卡的文件只能在sd卡內移動,也不能移動一個已經打開的文件。
?
f_chdir:
?
?????修改某個設備的當前目錄。當設備初始打開的時候,當前目錄自動設置為根目錄。用例如下:
?
?
f_chdrive:
?
修改當前設備。初始的當前設備為0號設備。
?
?
f_getcwd:
?
恢復當前設備上的當前目錄。
?
?
f_forward:(看的很粗)
?
?
從文件中讀取數據并直接將數據轉發到輸出流,而不使用數據緩沖區,適用于小存儲系統。
?
?
f_mkfs:
?
?????新建一個文件系統,其實感覺主要作用是格式化。
?
?
f_fdisk:(沒好好看)
?
分區,最多可以分四個,創建分區表到設備的MBR。
?
?
以下函數大概介紹一下:
?
?
?????這四個輸入輸出函數,前三個封裝的是f_read和f_write,最后一個封裝的是f_putc和f_puts。
?
?
f_tell:
?
得到當前的讀寫指針,其實是一個宏,如下:
?
?
f_eof:
?
檢查當前的讀寫指針是否打到了文件結尾。
?
?
f_size:
?
又是一個宏,返回的是文件大小。
?
?
?
f_error:
?
測試文件是否出錯。同樣是個宏。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
來自為知筆記(Wiz)
分類: stm32單片機 好文要頂 關注我 收藏該文 DChipNau
關注 - 1
粉絲 - 9 +加關注 0 0 ? 上一篇:stm32的fsmc
? 下一篇:stm32和cortex M3學習內核簡單總結
</div><div class="postDesc">posted @ <span id="post-date">2016-03-08 20:30</span> <a href="https://www.cnblogs.com/dchipnau/">DChipNau</a> 閱讀(<span id="post_view_count">189</span>) 評論(<span id="post_comment_count">0</span>) <a href="https://i.cnblogs.com/EditPosts.aspx?postid=5255483" rel="nofollow">編輯</a> <a href="#" onclick="AddToWz(5255483);return false;">收藏</a></div> </div> <script type="text/javascript">var allowComments=true,cb_blogId=255187,cb_entryId=5255483,cb_blogApp=currentBlogApp,cb_blogUserGuid='5862811b-e990-e511-9fc1-ac853d9f53cc',cb_entryCreatedDate='2016/3/8 20:30:00';loadViewCount(cb_entryId);var cb_postType=1;</script>
總結
- 上一篇: zephyr 系统--- 内存池使用方法
- 下一篇: Linux 的虚拟文件系统(强烈推荐)