FatFs 的底層能夠寫一次命令,讀寫多個扇區。FatFs的設計的讀寫的思想就很好,小塊的數據,我就通過Buffer來存儲,大塊的數據,我就直接進行存取,那樣速度,效率高了不少,看圖:html
FatFs文件系統的結構也很清晰,也是看圖:函數
補充一點,FatFs的做者寫了兩個,一個是正宗的FatFs,比較適合大的RAM的設備,另外一個是FatFs/Tiny,比較適合小RAM的系統,好比單片機,FatFs/Tiny佔用較小的RAM,代價是更慢的讀寫速度和更少的API函數。不過兩個都支持FAT12,FAT16,FAT32文件系統。測試
下載下來的FatFs的FatFs有兩個文件夾,一個是 doc ,FatFs的說明,包括特性,系統函數,以及可能的一些問題,另外一個就是源代碼文件夾src了,總共8個文件,diskio.c和diskio.h是硬件層,ff.c和ff.h是FatFs的文件系統層和文件系統的API層,integer.h是文件系統所用到的數據類型的定義,tff.c和tff.h是Tiny的文件系統層和文件系統的API層,還有一個00readme.txt簡要的介紹了FatFSHE FatFs/Tiny,包括他們所支持的API,怎麼配置等等。優化
移植的問題,第一個是數據類型,在integer.h裏面去定義好數據的類型。第二個,就是配置,打開ff.h(我用的FatFs,不是Tiny),_MCU_ENDIAN,選擇你的CPU是大端存儲(big endding)仍是小端存儲(little endding),通常的都用的小端存儲,1是小端,2是大端。這個至關重要,一下子還要談到這裏。其餘的,按照本身的須要來配置了,說明文檔夠清楚了,我就很少說啥了。spa
第三件事情,就是寫底層的驅動函數,包括:設計
全部的函數都牽涉到了選擇第幾個磁盤的問題,若是僅僅用一個,能夠沒必要理會這個drv 參數。
disk_initialize ,若是不須要的話,直接返回0就行
disk_status ,這個嘛,先無論了,直接返回0就OK
disk_read - Read sector(s)
disk_write - Write sector(s)
讀寫扇區,注意參數哦!
disk_ioctl 須要迴應CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZE 三個命令,正確返回0即
RES_OK,不正確返回RES_ERROR。
全部的命令都從 ctrl 裏面去讀,返回值僅僅返回此次操做是否有效,而須要傳遞回去的數據在buff
裏面,如下是個人:
CTRL_SYNC命令,直接返回0;
GET_SECTOR_COUNT,獲得全部可用的扇區數目(邏輯尋址即LBA尋址方式)
GET_BLOCK_SIZE,獲得每一個扇區有多少個字節,好比 *((DWORD*)buff) = 512;
其餘的命令,返回RES_PARERR
disk_ioctl 這個函數僅僅在格式化的時候被使用,在調試讀寫的時候,這個函數直接讓他返回0就OK 了。
get_fattime - 獲得系統的時間,格式請見文檔。不用的話,返回0就行。
這樣移植了,也基本上就成功了,可是在個人板子上面死活不行,每次一執行到幾個宏定義好比
LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) 就產生數據終止異常( DATA ABORT exception),可是網上的一個兄弟的(ouravr上的一個兄弟,用的SD卡,IAR編譯器,平臺是STM32,已經成功了,還公佈了源碼的,這裏沒有問題啊),沒問題。分析下這個幾個宏的意思:
LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) 是在little endding裏面定義的
LD_WORD(ptr) ,LD就是load,WORD在integer.h裏面定義的是16位的無符號數,那這個須要完成的就是載入一個16位的數,或者說是2個字節,後面的 ptr是參數。(WORD)(*(WORD*)(BYTE*)(ptr)) ,先將這個ptr轉換成一個指向BYTE類型數據的指針(BYTE *),在將這個指針轉換成一個指向16位無符號數的指針(WORD *),而後用一個 」 * 「將這個數據取出來,轉換成一個無符號的16位數據,這個僅僅從C語言的角度來看,實際上呢,這個完成的就是從ptr指針指向的位置,取出2個字節,做爲一個16位的無符號數取出,而這2個字節是little endding,即小端模式,低字節是低8位,高字節是高8位。
既然是這樣的,測試了下,定義了一個BYTE buf[512],定義一個WORD類型 zz,用一個指針pt,讓pt指向
buf[0],調用LD_WORD(ptr),zz=LD_WORD(pt);沒問題,將pt指向buf[1],呵呵,問題立刻出來了,數據終止異常,而後測試了指針指向 buf[3],buf[5]等等奇數個,都是這樣的問題,我就鬱悶了啊,TMD,編譯器的問題!!!!不過還好,找到問題了,就能夠解決問題了,在 ff.h裏面的宏定義裏面把這即個東東給註釋掉,而後在ff.c裏面把這幾個宏定義寫成函數,這裏貼一個出來:指針
1 WORD LD_WORD(void *pt) 2 { 3 BYTE *PT = (BYTE*)pt; //定義一個指針,將當前的指針指向的地址的值賦給PT 4 return (WORD)(PT[0]+PT[1]*256); //計算這個16位數,(低8位在前面,高8位在後面),並來個強制類型轉 5 //換,並返回 6 }
須要注意的是,LD_WORD返回的就必須是WORD。這樣作了,編譯器大部分的也能夠編譯經過,可是ADS就是通不過,有3個地方,調試
1 finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */ 2 finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */ 3 finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */
其中,dir的是這樣定義的:const BYTE *dir,編譯器報錯是類型不匹配,所以,這裏的幾個LD_WORD和LD_DWORD重寫,定義成一致的類型便可:code
1 WORD LD_WORD_1(const BYTE *pt) 2 { 3 BYTE *PT = (BYTE*)pt; 4 return (WORD)(PT[0]+PT[1]*256); 5 } 6 7 DWORD LD_DWORD_1(const BYTE *pt) 8 { 9 BYTE *PT = (BYTE*)pt; 10 return ((DWORD)PT[0]+(DWORD)(PT[1]*256)+(DWORD)(PT[2]*65536)+(DWORD)(PT[3]*16777216)); 11 }
然後面改爲: htm
1 finfo->fsize = LD_DWORD_1(&dir[DIR_FileSize]); /* Size */ 2 finfo->fdate = LD_WORD_1(&dir[DIR_WrtDate]); /* Date */ 3 finfo->ftime = LD_WORD_1(&dir[DIR_WrtTime]); /* Time */
編譯,一路OK,而後寫一個文件,出來了!!!!寫文件沒問題,讀也沒問題!@~~~~~測試了經常使用的函數,都沒有問題,包括格式化(f_mkfs,前提是你的disk_ioctl 沒問題),測試
了下速度,讀12.5M的MP3,大約3秒,寫這個12.5M的MP3大約6.5秒,勉強達到要求,再優化下驅動那邊就能夠更快了!~~~~~~~
發個FatFs的官方網址 http://elm-chan.org/fsw/ff/00index_e.html