在STM32F401上移植uC/OS的一個小問題 [原創]

helesheng發表於2021-08-15

STM32F401xx是意法半導體新推出的Cortex-M4核心的MCU,相較於已經非常流行的STM32F407xx和STM32F427xx等相同核心的MCU而言,其特點是功耗僅為128uA/MHz,且存在64pin封裝的小封裝器件。我設計一款新產品使用了STM32F401xx,想把自己熟悉的uC/OS-II實時作業系統移植到這款MCU上。懶得從底層開始從頭移植,偷懶從原子的“探索者”開發板移植的uC/OS-II開始修改。完成後在板子上一跑,發現系統時鐘總存在約5%的偏差。仔細搜尋程式碼後才發現,問題在把原子的uC/OS-II移植直接用在STM32F401上時的一個小bug,把發現問題的過程和解決辦法分享給大家。

以下原創內容歡迎網友轉載,但請註明出處:http://cnblogs.com/helesheng

一、把探索者開發板移植的uC/OS-II用在STM32F401xx上

原子的探索者開發板使用了STM32F407ZG,其核心是和32F401xx 相同的Cortex-M4,外設也基本相同。簡單地將原子在探索者開發板上移植的uC/OS-II工程中的Device修改為STM32F401RC,編譯後發現大量的預定義找不到。想到可能是代表處理器的預定義的巨集沒有修改,在Option中的C/C++頁中,將Preprocessor Symbols改為代表401的“STM32F401xx,USE_STDPERIPH_DRIVER”,直接編譯通過,心中一陣竊喜。

圖1 修改預定義

二、uC/OS-II系統時鐘偏差的小問題

執行之前在其它款STM32上寫的uC/OS-II程式,開始一切順利,但隨後用示波器仔細分析各個任務內部及相互間的時間關係時,發現總存在+5%左右的時間誤差,即實際系統時鐘的週期只有理論系統時鐘的95%左右。以每秒系統節拍數OS_TICKS_PER_SEC設定為200為例,每個OSTimeDly(1);的任務延遲僅為4750us。

開始以為是原子編寫的延遲函式void delay_ms(u16 nms);的問題,仔細閱讀程式碼後發現,這個函式只是在呼叫系統延遲函式OSTimeDly();的基礎之上,將無法由系統延遲實現的us級延遲改由硬延遲實現。雖然我個人非常不贊同這種做法,因為這會造成uC/OS-II時間排程的盲區,從而影響uC/OS-II系統的實時性。但這不至於造成系統時鐘的偏差,繼續查詢問題的原因。

既然呼叫系統延遲函式OSTimeDly的過程沒有問題,那麼只可能是系統時鐘本身出了問題。Cortex-M核心的uC/OS移植都會用了SysTick定時器的中斷構建系統時鐘,因此在SysTick上找原因。開啟MDK,連線自制的STM32F401電路板和模擬器,進入Debug模式,執行程式,讓uC/OS完成初始化配置,然後暫停程式;在外設(peripheral)選單中找到SysTick定時器,介面如下圖所示。

 

圖2 System tick定時器狀態監視器

其中重灌暫存器中的值為0xC350,即50_000,仔細一想發現有問題——STM32F401的主頻為84MHz,那要達到200Hz的系統時鐘,無論如何都不可能把System Tick的初值配置為50_000啊!於是開始查詢uC/OS移植程式碼中配置System Tick的部分,原來在探索者開發板移植程式碼中初始化延遲函式delay_init();中。看看原子的程式碼。

圖3 delay_init();函式原來的程式碼

原子的程式碼簡潔清晰,將System Tick的時鐘配置為AHB時鐘的1/8,在計算System Tick的初值。其中紅圈中的一句是計算AHB時鐘的八分之一,為後續計算定時器初始值做準備。但進一步仔細檢視後發現變數SYSCLK中存放的AHB時鐘數是以MHz作為單位的,對STM32F401xx而言,就是84。84無法整除8,而賦值語句左邊的變數reload卻是int型變數,從而導致了紅圈中的一句計算誤差。這樣即使後一句將reload的單位切換回了Hz,也無法挽回前一句無法整除造成的計算誤差。而這真是uC/OS系統時鐘誤差5%的真正原因!對這兩句進行簡單修改——現將SYSCLK折算為以Hz為單位,即可保證足夠的計算精度。

圖4 修改過的delay_init();函式程式碼

修改過後,uC/OS的系統時鐘又恢復了原有的準確性。

究其原因並不是原子探索者開發板移植的uC/OS的問題,因為探索者採用的STM32F407ZG執行在168MHz,能夠整除8,不存在這個問題。這裡給大家展示這個過程,一是給大家在STM32F401上移植uC/OS做些探索,二是分享一下嵌入式除錯和查詢問題的點滴思路和心得。

 

相關文章