移植方法參見我的另一篇博客:《stm32--FatFs移植(SPIFlash)》。
本文僅記錄在初次移植完成后,遇到的問題,和解決的過程。
調試記錄:
- 問題1:f_open返回3,即磁盤沒有准備好。
- 原因:這是因為邏輯驅動器是按默認(0)初始化的,而在宏定義中把SPIFlash定義為了1。將SPIFlash定義為0即可。
- 問題2:開機是否格式化?如果不格式化,SPIFlash無法創建創建文件系統;又不可能每次開機都格式化。
- 處理:f_getfree檢測FAT卷空間,如果返回是FR_NO_FILESYSTEM,說明沒有格式化過,進行格式化。
- 問題3:格式化失敗(返回FR_DISK_ERR)【此時的BLOCK_SIZE參數是錯誤的】
- 原因:diskio.c中底層寫入函數入口判斷錯誤,入口判斷參數是否正常時 if(sector > SEC_MAX || sector + count > SEC_MAX) return RES_PARERR; 出錯,第二個判斷條件應為sector + count - 1而非sector + count。
- 問題4:只能格式化為FAT12格式。
- 調試過程:仿真跟蹤f_mkfs源代碼:
- 如果輸入參數為FAT,函數中會減去63個扇區用於存放分區表,此時就只剩4033個扇區了;之后由
這段代碼,sz_vol為4033,pau為1,得出n_clst為4033<4085(MAX_FAT12),於是設為FAT12格式
- 如果輸入參數為FAT+SFD,sz_vol值為4096,之后由
這段代碼,pau為2,再執行1中代碼,n_clst為2048,依然是FAT12格式
- 輸入參數FAT+SFD,並在輸入參數中強制pau為1,倒是不會置為FAT12格式,但是格式化返回FR_MKFS_ABORTED錯誤。查找錯誤原因,發現問題來自
這段代碼。sz_blk由disk指令Get_Block_Size得到,這里我把這個值設為了4096(一個扇區),這樣最后得到的n_clst為0,小於MAX_FAT12,返回錯誤代碼。實際上這個值是以扇區為單位,應設為1。
- 問題5:不正確的格式化。【此時對FAT文件系統結構的理解是錯誤的】
- sz_fat只有3,於是后面格式化只格式化了3個扇區。可是sz_fat和n_clst是互斥的:如果sz_fat很大就意味着n_clst很小,無法格式化為FAT16;如果n_clst格式化為FAT16那就意味着sz_fat是小於10的數。
- 此時嘗試創建文件,“果然”可以創建成功、但無法正確保存:每次重啟后能載入文件系統,但獲取不到之前創建的文件。
- 於是開始走向錯誤的嘗試路徑:嘗試減小單個扇區的定義大小、單次擦除多個扇區。
- 嘗試將單個扇區自定義為1k,block定義為4扇區,修改disk_read函數、disk_wirte函數、disk_ioctl函數,以及格式化函數f_mkfs的輸入參數。
- 結果:不可行。實際操作中會有寫入單個扇區的情況(分區:1系統保留區-x數據區-y頁表區),如此,在寫入第一個數據扇區的時候,會執行一次erase函數,擦除剛剛寫入的系統保留區。類似的情況也會出現在其他地方。
- 修改為僅在初始扇區為4的倍數時,允許擦除。如此不會擦除正確寫入的數據,但有可能無法成功修改某些單個小扇區。
- 修改為,在寫入扇區時,如果判斷到初始扇區不為4的倍數或結束扇區不為4的倍數,先將4k真實扇區數據讀出,再擦除真實扇區,然后將讀出的數據寫入需寫入扇區前的部分,最后將需寫入的數據寫入。
- 依舊不可行,格式化能返回ok,但重啟后出現無文件系統的錯誤,說明文件系統寫入還是有問題。
- 解決問題5中文件無法正確保存的問題
- 通過USB程序,將模塊連到電腦上格式化。
- 用8k/扇區格式化,能格式化成功。但將格式化好的模塊下載fatfs程序時,fatfs讀取出現問題,因為fatfs最大支持的單扇區大小為4096字節。
- 用我的電腦4k/扇區格式化,一直格式化失敗,串口打印出log發現寫入都沒有問題,寫入后的讀取卻讀不到有效內容。
- 在程序里面加上一段打印打碼,執行對0扇區的寫入后,打印寫入到0扇區的內容,從而判斷到底是什么導致了格式化失敗。結果,加上這段代碼后,格式化成功。
- 判斷為需要在寫入0扇區后延時一段時間再讀取才能成功。加入延時函數后,格式化沒有問題了。
- 將格式化好的模塊下載fatfs程序后,讀取無問題,獲取剩余空間大小也沒有問題。但是,在f_open創建文件、f_write進行寫入操作、f_close關閉文件后,下次用f_open打開這個文件(OPEN_EXISTING選項)時,依然返回找不到文件的錯誤。
- 經過各種調試,發現在disk_write函數中,每次進出打斷點,這樣進行的f_write和f_close操作,能成功保存文件、修改頁表,下次f_open能夠讀到這個文件。
- 判斷是延時問題,嘗試在disk_write出口處加上100ms延時,問題解決。實驗發現,延時最短到15ms的時候,可以正常保存文件。最終將延時設為20ms。
- 通過USB程序,將模塊連到電腦上格式化。
- 解決格式化失敗的問題:
- 既然無法保存的問題出自寫入后的延時,那么之前的格式化之后文件無法保存的問題是否也出自這里?
- 之前的判斷認為是fatfs將頁表存在末4個扇區內,所以導致文件無法保存的原因是只格式化了前面的數個扇區,沒有成功格式化。現在認為這個判斷是錯誤的。
- 因為經過調試發現,前幾個扇區都是用於存儲fatfs相關內容的。之前以為是數據區的sz_fat,目前看來應該是fat文件頁表;之前認為是文件頁表的sz_dir,實際上是fat目錄表。
- 也就是說,fat16文件系統的結構是這樣的:(參見https://blog.csdn.net/sikuon/article/details/75222224)
- rsv:系統保留區(0扇區的DBR,可能存在的分區表,以及其他保留扇區),位於第0--x扇區。
- fat:文件頁表區(有時會有FAT2作為FAT的備份),位於x+1--y扇區。
- dir:文件目錄表區,位於y+1--z扇區
- data:數據區,位於z+1--末扇區
- 那么,昨天遇到的情況,格式化只格了前幾個扇區,是正確的操作。如此說來,GET_BLOCK_SIZE的功能的確如我所理解的那樣,是扇區數量。block_size就是一次性擦除的扇區數量。
- 如此,格式化的參數有兩種選擇:
- 根據昨天的經驗,用f_mkfs("", FM_FAT, 0, work_buffer, FF_MAX_SS);這樣的參數格式化,只能格式化為FAT12(或許應該再試試,因為最初用這種參數格式化時,用的BLOCK_SIZE是4096--與此無關,只要不是SFD格式,都會-63扇區作為起始扇區)。
- 根據昨天的經驗,用f_mkfs("", FM_FAT+FM_SFD, 4096, work_buffer, FF_MAX_SS);這樣的參數,能成功格式化為FAT16,但這是軟盤格式,電腦上不一定可用。
- 最終試驗發現第一種會格式化為FAT12,第二種可行。
- 在最開始遇到的一個問題,后來在另一個用到Fatfs的板子上又遇到了:
- 現象:開機f_getfree函數返回FR_NO_FILESYSTEM,進入格式化;f_mkfs函數返回FR_OK,格式化成功;下一步f_open函數又返回FR_NO_FILESYSTEM錯誤。
- 原因:SPIFlash虛焊。焊好后恢復正常。