1. 程式人生 > >STM32CubeMX+FreeRTOS+FatFs+SD卡的除錯心得

STM32CubeMX+FreeRTOS+FatFs+SD卡的除錯心得

  由於本人的習慣,新接觸的東西一般先找個簡單的歷程實現一些簡單的功能,然後再在此基礎上展開,所以除錯參考了眾多前輩的文章,在此表示感謝,如果有需要註明出處的地方,請聯絡我。
  由於專案需要使用ARM實現一些資料轉發等功能,考慮可能需要記錄日誌,於是計劃專案採用STM32作為主控,原因考慮有以下:
1. STM32是目前資源比較多的ARM。簡單的入門可以參考wei xue的教程。
2.價格也比較便宜,很多核心板可以用。
3.開發難度適中,對於之前從裸機(不帶系統)或者51上手的人來說,st提供了stm32cubemx這個配置和程式碼生成工具,極大的簡化了配置的過程。
4.集成了FreeRTOS,麻雀雖小,五臟俱全,量級較輕,bug比較可控。
......總之對於拿來入門和基本的使用是比較夠的。

進入正題。
本次使用的晶片是STM32F407ZGT,介面為SDIO 4線,普通的TF卡。初始程式碼全部由STM32CubeMx生成,CubeMx為4.25.1韌體cube版本為V1.0,目前CubeMx裡面Middleware顯示FatFS版本為R0.12c,FreeRTOS版本為9.0.0。使用和諧版MDK作為程式碼工具。系統的配置可參考weixue,不過用rtos的時候會稍有區別。
然而不得不說,CubeMX的坑還是不少的,其中影響到使用的有以下內容,特此作為記錄:

1. 如使用了FreeRTOS,會要求強制使用DMA模板的Fatfs,所以開啟DMA通道,開中斷,以及開SDIO中斷是必須的。


2. 生成程式碼直接編譯會報個錯,Bad operand types(UnDefOT, Constant) for operator( 之類的,定位到
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY 這一句,然後定位到
configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY<<(8- configPRIO_BITS) ) 這一句,然後定位到
#define configPRIO_BITS         __NVIC_PRIO_BITS 再定位到
configPRIO_BITS 這個定義,(實際是在 stm32F407xx.裡面,不同的arm不一樣)
#define __NVIC_PRIO_BITS          4U,這兒應該是#define __NVIC_PRIO_BITS 4,才行。
經確認除了目前這種配置外,開啟其他外設配置(如IIC或者SPI)等,也會有概率出現這種問題,應該是cubeMX的bug。

3. 生成Fatfs之後,SDIO和fatfs的初始化是這樣的,在main裡面先初始化SDIO,然後開系統StartDefaultTask執行緒,此時再初始化Fatfs。所以將FatFS連線到SDIO的程式碼是在StartDefaultTask中的呼叫MX_FATFS_Init(),其中以下語句將檔案系統連線至SDIO硬體:
retSD = FATFS_LinkDriver(&SD_Driver, SDPath);
以後所有的操作都要使用SDPath這個全域性變數作為控制代碼,如掛載(需要使用者自己新增):
retSD=f_mount(&SDFatFS,SDPath,1);可以得到 對應的SDFatFS作為檔案操作空間,如建立檔案:
retSD = f_open(&SDFile, filename, FA_CREATE_ALWAYS | FA_WRITE);即在當前操作空間建立檔案獲取到檔案控制代碼SDFile。後面即可寫入或關閉檔案。
然而使用自動生成的程式碼的問題就是f_mount(或者其他讀寫操作的時候),會卡很久,然後返回retSD的狀態為0x01(fs_disk_err),原因就是fatfs呼叫底層讀寫SD卡介面的時候出錯了,經網上各位前輩指點,原因在於:
生成的程式碼中sd_diskio.c中,void BSP_SD_ReadCpltCallback(void)和void BSP_SD_WriteCpltCallback(void)作為系統讀寫完成的回撥函式(實際上是通過寫入訊息佇列要求系統排程給fatfs的作為SD卡讀寫完成的標誌的),然後並不正確的是,在這個檔案前面函式HAL_SD_IRQHandler中,呼叫的名稱卻是HAL_SD_TxCpltCallback(hsd)(以讀取為例子,寫入函式也是對應的),而此函式在當前版本中為 __weak void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd),即未被使用者定義的函式,預設什麼事情都不做。所以需要將HAL_SD_IRQHandler呼叫的函式修改為BSP_SD_ReadCpltCallback(void)或者,將其內容修改為BSP_SD_ReadCpltCallback(void)中進行的佇列操作,或者直接將BSP_SD_ReadCpltCallback(void)修改HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)等,(對寫入操作也是如此)。然後就OK了。當然,在以前的版本中,ST似乎是意識到這個問題的,但是由於版本控制比較混亂,後面其他程式碼改了之後此處並未修改,ST在此處實際是有註釋的:
/* USER CODE BEGIN callbackSection */
/* can be used to modify / following code or add new code */
/* USER CODE END callbackSection */
/**
  * @brief Tx Transfer completed callbacks
  * @param hsd: SD handle
  * @retval None
  */
 /*
   ===============================================================================
    Select the correct function signature depending on your platform.
    please refer to the file "stm32xxxx_eval_sd.h" to verify the correct function   

prototype  ===============================================================================

大意就是此處是要根據實際情況該的,可能會有錯誤哦。然後版本更替之後stm32xxxx_eval_sd.h"已經沒有這個檔案了。

上述三個坑填完之後,基本的讀寫測試就可以OK啦。