色婷婷AⅤ一区二区三区|亚洲精品第一国产综合亚AV|久久精品官方网视频|日本28视频香蕉

          "); //-->

          博客專欄

          EEPW首頁 > 博客 > FreeRTOS 中如何定位 HardFault?

          FreeRTOS 中如何定位 HardFault?

          發(fā)布人:魚鷹談單片機 時間:2022-09-22 來源:工程師 發(fā)布文章

          大家好,我是魚鷹,因為一些事情,這次更新來的有點遲。但還是爭取大家每次都能從魚鷹公眾號中學(xué)到一些實實在在的技術(shù),提高自己的核心競爭力。

          感謝大家一直以來對魚鷹的支持圖片圖片。

          今天繼續(xù)聊聊開發(fā)中常見的 HardFault,這個問題應(yīng)該從學(xué)習(xí) STM32 開發(fā)以來就一直伴隨著我們,很多人遇到這種問題也是不知道該如何定位。

          如果只是獨立開發(fā),遇到這種問題,一般都是看代碼、修改代碼等等這些常規(guī)手段,因為自己寫的代碼最熟悉,改動一般也不會太大,容易縮小范圍,也更容易定位。

          但現(xiàn)在的產(chǎn)品越來越復(fù)雜,目前的開發(fā)模式都是合作開發(fā),每個人負(fù)責(zé)各自的模塊,這樣的項目代碼量大、復(fù)雜度高,也就更難定位問題。

          而有的時候,剛?cè)肼氁患夜?,什么代碼都不熟悉,又出現(xiàn)了 HardFault,更是讓人崩潰,分分鐘有跑路的沖動(你和代碼,有一個能跑就行圖片)。

          此時,有一個能解決這種疑難雜癥的大牛是能大大節(jié)省時間的,而我在公司也解決不少類似的問題,所以經(jīng)驗也算豐富,充當(dāng)?shù)囊彩沁@一類角色。

          而魚鷹定位 Hardfault 的方法一般是靠 KEIL在線調(diào)試+C語言+權(quán)威指南 中的知識搞定。

          目前魚鷹的解 BUG 差不多是這樣的:

          1、必現(xiàn),代碼熟悉的情況下,幾個小時內(nèi)搞定。

          2、偶現(xiàn),根據(jù)出現(xiàn)情況決定解決問題的時間,一般出現(xiàn)個四五次,基本就能定位。

          3、難現(xiàn)。這種一般要掛一個記錄儀實時記錄運行情況。

          經(jīng)歷了這么多,已經(jīng)很少有能讓魚鷹需要花費幾天時間才能解決的 Hardfault 問題了(猶記得剛來深圳時,因為別人寫的一個 BUG 導(dǎo)致的 Hardfault,不得已加了幾天通宵,要不是偶然機會還不一定能搞定)。

          這里打個小廣告,如果難解決,可以有償請魚鷹解決 Hardfault 問題哦。

          不過最近工作上因為用了 C++,這個基礎(chǔ)不是很熟悉,解決 Hardfault 的速度又下降了。而工程編譯優(yōu)化等級 -O2 也加大了不少調(diào)試難度,因此掌握下面的方法是很重要的:

          總結(jié) MDK 幾種編譯優(yōu)化設(shè)置的方法


          關(guān)于 Hardfault,魚鷹以前也是分享了不少筆記的,不知道有多少人認(rèn)真看過。

          HardFault 之 INVSTAE 錯誤定位(一)

          見鬼,過年回來后板子就 hardfault 了?

          今天,魚鷹繼續(xù)分享關(guān)于在 FreeRTOS 定位 Hardfault 方法。

          這里需要一個大佬寫的組件 :CmBacktrace(事實上,如果能在線調(diào)試,魚鷹是不需要借助這個組件的,但是難復(fù)現(xiàn)的情況下用這個組件還是比較香的)。

          gitee 倉庫https://gitee.com/Armink/CmBacktrace

          這個組件估計很多道友都聽說過,也用過,但魚鷹想說的是,有些道友在用的組件可能比較老,沒有下面這種追蹤功能,建議大家更新一下。

          圖片

          上面可以看到出錯時,函數(shù)的調(diào)用棧(有時可能是錯誤的,需要實際分析,僅做參考)


          _call_main ->  main -> fult_test_by_div0

          相當(dāng)實用。


          同時,本篇筆記不僅適用于在 FreeRTOS 定位 Hardfault,實際上uCOS、rt-thread 等其它 RTOS 照樣可以修改后使用(裸機更不用說了)。 

          倉庫例子支持的平臺:裸機、rt-thread、ucoss-ii、freertos。

          這里重點在如何移植這個組件到 freertos 中(實際上,倉庫的說明文檔也非常詳細(xì),可以參考)。由于 freertos  也是不斷更新中,所以這個組件的例子不能完全適用于新版本,而魚鷹剛好移植好了,在此記錄一下,方便大家移植。


          1、將倉庫中的 cm_backtrace(源碼文件) 整個文件夾拷貝到自己的工程文件夾下。

          圖片

          2、在自己的工程中添加這些文件(我們可以打開 demos -> os -> freertos 工程查看)

          圖片

          只有兩個文件,相當(dāng)簡單。

          一個是核心源碼,另外一個則是匯編代碼,代碼執(zhí)行入口。

          注意,根據(jù) IDE 不同,選擇的匯編文件也不同:

          圖片


          其實就是將 startup_stm32f10x_hd.s 中的hardfault 默認(rèn)處理函數(shù)重定位到 cmb_fault.S 中了。

          圖片

          注意這里有一個weak,這樣鏈接的時候就不會鏈接這個,而是 cmb_fault.S 這個:

          圖片

          為了更方便的定位問題,我們后面還需要修改一下這個代碼才行。

          注意,如果你的啟動文件內(nèi)的 hardfault 代碼被修改了,而你不懂匯編,建議恢復(fù)成上面那種,不然可能運行不正常。

          3、主函數(shù)中初始化代碼。

          圖片

          這里的字符串需要和這個一樣(根據(jù)自己的工程名修改):

          圖片

          所以建議用英文建工程。這個在輸出錯誤信息的時候用的上,否則每次查看調(diào)用棧都需要修改一下,比較麻煩。


          如果開啟了內(nèi)部看門狗,建議關(guān)閉一下:





          // HAL 庫__HAL_DBGMCU_FREEZE_IWDG1();// 標(biāo)準(zhǔn)庫DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE);

          在斷言失敗的位置添加該函數(shù) cm_backtrace_assert:

          圖片

          這樣斷言失敗了也能看到調(diào)用棧了。


          4、FreeROTS 內(nèi)核文件修改(內(nèi)核版本 V10.2.1)

              為了分析出錯的代碼,必須知道每個任務(wù)的棧信息,而 FreeRTOS 可能沒有這些信息,因此,我們需要添加進(jìn)去。

           task.c

          圖片

          FreeRTOS.h

          圖片

          注意,老版本freertos 是只要修改一處的,但新版本需要修改兩處,否則會斷言失敗,運行不下去。

          建議把注釋也一起添加進(jìn)去。


          UBaseType_t     uxSizeOfStack;      /*< Support For CmBacktrace >*/


          相關(guān)函數(shù)修改  task.c    prvInitialiseNewTask() :

          圖片


          task.c 文件最后添加如下代碼用于獲取棧地址、大小、名字:

          圖片

          為方便復(fù)制,在此貼代碼


          /*-----------------------------------------------------------*//*< Support For CmBacktrace >*/uint32_t * vTaskStackAddr(){    return pxCurrentTCB->pxStack;}
          uint32_t vTaskStackSize(){    #if ( portSTACK_GROWTH > 0 )    return (pxNewTCB->pxEndOfStack - pxNewTCB->pxStack + 1);    #else /* ( portSTACK_GROWTH > 0 )*/    return pxCurrentTCB->uxSizeOfStack;    #endif /* ( portSTACK_GROWTH > 0 )*/}
          char * vTaskName(){    return pxCurrentTCB->pcTaskName;}/*-----------------------------------------------------------*/

          5、根據(jù)所屬 RTOS 平臺和芯片內(nèi)核修改組件配置信息

          cmb_cfg.h

          圖片

          1)需要定義打印輸出函數(shù),一般用 printf 打印,也可以用你自定義的一些打印函數(shù),功能和 printf 類似即可。


          #define cmb_println(...)               printf(__VA_ARGS__);printf("\r\n")

          2)使能 RTOS 支持


          #define CMB_USING_OS_PLATFORM

          3)具體 RTOS 選擇 FreeRTOS


          #define CMB_OS_PLATFORM_TYPE           CMB_OS_PLATFORM_FREERTOS

          4)芯片內(nèi)核根據(jù)實際選擇,目前支持 M0、M3、M4、M7。


          #define CMB_CPU_PLATFORM_TYPE          CMB_CPU_ARM_CORTEX_M3

          5)打印虛擬棧,可以將出錯時的原始棧信息打印出來,可能對分析有些幫助


          #define CMB_USING_DUMP_STACK_INFO

          6)語言支持:英語。實際也支持中文,但建議使用英語(不配置,默認(rèn)就是英語)


          #define CMB_PRINT_LANGUAGE             CMB_PRINT_LANGUAGE_ENGLISH

          7) 如果是 C++ 編譯的,有可能出錯,可以在開頭定義這個:


          #define __CLANG_ARM


          7、根據(jù)需要修改組件,方便使用(這些看看能不能有機會合并到大佬的分支里面)

          1)因為功能涉及范圍小,因此可以將相關(guān)頭文件包含形式改成這種,這樣就不需要改頭文件路徑了,移植更方便:

          #include <cm_backtrace.h>-->>#include "./cm_backtrace.h"
          #include <cmb_cfg.h>-->>#include "./cmb_cfg.h"
          #include "cmb_def.h"-->>#include "./cmb_def.h"

          main 中也不需要包含頭文件,而是在需要位置直接聲明這個函數(shù)即可,因為外部只需要調(diào)用這個函數(shù)




          #include <cm_backtrace.h-->void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver);

          這樣一來,就不需要添加頭文件的路徑了。

          或者使用相對路徑的方式添加頭文件:


          #include "../../driver/cm_backtrace/cm_backtrace.h"


          另外,我們可以讓程序進(jìn)入 Hardfault 前,讓代碼自動停止,這樣我們能更好的利用在線調(diào)試代碼,《 傳說中的軟件斷點到底是什么?。











          HardFault_Handler    PROC    LDR     r0, =0xE000EDF0; DEMCR    LDR     r0,[r0,#0x00]    AND     r0,r0,#0x00000001    CBZ     r0,not_in_debug    BKPT    0not_in_debug    MOV     r0, lr                  ; get lr    MOV     r1, sp                  ; get stack pointer (current is MSP)    BL      cm_backtrace_fault

          因為剛進(jìn)入 Hardfault 時的信息最全,又不想每次打斷點,上面的代碼很好的實現(xiàn)了功能,同時也不會影響程序的正常運行(會自動判斷是否處于調(diào)試模式)。

          8、實驗。

          上面都搞定了,就可以驗證一下效果了。這里我們我們可以模擬仿真看看情況。(修改工程配置,這些內(nèi)容魚鷹以前分享過,不多說)

          圖片

          運行倉庫例子后,應(yīng)該能打印下面的信息,告訴我們出現(xiàn)了 div 0 錯誤

          圖片

          而你移植好的工程也應(yīng)該打印類似的信息(加入測試代碼 :fault_test_by_div0();)如果打印不出來,有兩種可能:

          1、打印函數(shù)沒初始化好就進(jìn)入了Hardfault

          2、打印函數(shù)有問題。


          圖片

          之后我們復(fù)制最后一行,然后運行倉庫 tools 里面的 add2line 工具看看調(diào)用棧信息:

          圖片

          在 git bash 中可能會執(zhí)行失敗,可以加入程序路徑,當(dāng)然也可以將該工具路徑加入到Windows 環(huán)境變量中。有可能提示找不到 axf 文件,把這個文件拷貝到工具下即可。

          正確的做法是,將該工具放到 C 盤目錄下,同時添加環(huán)境變量,之后就可以在 axf 目錄下打開 gitbash 或者 cmd 窗口執(zhí)行命令即可。

          這里只作為演示,就不多介紹這些了。


          最后再簡單介紹一下這個組件的實現(xiàn)原理:

          如果出現(xiàn)hardfault, 首先進(jìn)入?yún)R編文件的 HardFault_Handler 處理,這里會得到當(dāng)前的棧指針和 LR,并且根據(jù) LR 確定出錯的棧是哪個《STM32 兩個棧,你用哪一個?》(這里是 PSP 棧)

          根據(jù)錯誤寄存器信息,確定哪種錯誤(這里為 除 0 錯誤。

          然后對棧信息 和 LR 、PC 進(jìn)行 FLASH 上的匯編代碼分析,找出可能的跳轉(zhuǎn)指令,這里找到的兩個跳轉(zhuǎn)地址為 0x08001f96 0x08000368,進(jìn)而得到調(diào)用棧。


          因此,要能準(zhǔn)確的得到調(diào)用棧,有兩點重要前提條件(建議優(yōu)化等級 -O0):

          1、棧未被破壞

          2、芯片運行代碼和 axf 文件保持一致。

          即使如此,你也不能保證找出的調(diào)用棧就是正確的,比如你使用 fault_test_by_unalign() 測試,得到的結(jié)果如下: 

          圖片

          中間多了一個 fputc。

          因此,這些打印信息只能作為參考使用。

          但在線調(diào)試不同,它更專業(yè),不容易出現(xiàn)錯誤調(diào)用關(guān)系。

          圖片

          魚鷹想分享的內(nèi)容到此就結(jié)束了,下期再見!


          *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



          關(guān)鍵詞: 單片機

          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉