<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>
"); //-->

博客專(zhuān)欄

EEPW首頁(yè) > 博客 > FreeRTOS 中如何定位 HardFault?

FreeRTOS 中如何定位 HardFault?

發(fā)布人:魚(yú)鷹談單片機 時(shí)間:2022-09-22 來(lái)源:工程師 發(fā)布文章

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

感謝大家一直以來(lái)對魚(yú)鷹的支持圖片圖片。

今天繼續聊聊開(kāi)發(fā)中常見(jiàn)的 HardFault,這個(gè)問(wèn)題應該從學(xué)習 STM32 開(kāi)發(fā)以來(lái)就一直伴隨著(zhù)我們,很多人遇到這種問(wèn)題也是不知道該如何定位。

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

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

而有的時(shí)候,剛入職一家公司,什么代碼都不熟悉,又出現了 HardFault,更是讓人崩潰,分分鐘有跑路的沖動(dòng)(你和代碼,有一個(gè)能跑就行圖片)。

此時(shí),有一個(gè)能解決這種疑難雜癥的大牛是能大大節省時(shí)間的,而我在公司也解決不少類(lèi)似的問(wèn)題,所以經(jīng)驗也算豐富,充當的也是這一類(lèi)角色。

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

目前魚(yú)鷹的解 BUG 差不多是這樣的:

1、必現,代碼熟悉的情況下,幾個(gè)小時(shí)內搞定。

2、偶現,根據出現情況決定解決問(wèn)題的時(shí)間,一般出現個(gè)四五次,基本就能定位。

3、難現。這種一般要掛一個(gè)記錄儀實(shí)時(shí)記錄運行情況。

經(jīng)歷了這么多,已經(jīng)很少有能讓魚(yú)鷹需要花費幾天時(shí)間才能解決的 Hardfault 問(wèn)題了(猶記得剛來(lái)深圳時(shí),因為別人寫(xiě)的一個(gè) BUG 導致的 Hardfault,不得已加了幾天通宵,要不是偶然機會(huì )還不一定能搞定)。

這里打個(gè)小廣告,如果難解決,可以有償請魚(yú)鷹解決 Hardfault 問(wèn)題哦。

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

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


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

HardFault 之 INVSTAE 錯誤定位(一)

見(jiàn)鬼,過(guò)年回來(lái)后板子就 hardfault 了?

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

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

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

這個(gè)組件估計很多道友都聽(tīng)說(shuō)過(guò),也用過(guò),但魚(yú)鷹想說(shuō)的是,有些道友在用的組件可能比較老,沒(méi)有下面這種追蹤功能,建議大家更新一下。

圖片

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


_call_main ->  main -> fult_test_by_div0

相當實(shí)用。


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

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

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


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

圖片

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

圖片

只有兩個(gè)文件,相當簡(jiǎn)單。

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

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

圖片


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

圖片

注意這里有一個(gè)weak,這樣鏈接的時(shí)候就不會(huì )鏈接這個(gè),而是 cmb_fault.S 這個(gè):

圖片

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

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

3、主函數中初始化代碼。

圖片

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

圖片

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


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





// HAL 庫__HAL_DBGMCU_FREEZE_IWDG1();// 標準庫DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE);

在斷言失敗的位置添加該函數 cm_backtrace_assert:

圖片

這樣斷言失敗了也能看到調用棧了。


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

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

 task.c

圖片

FreeRTOS.h

圖片

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

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


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


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

圖片


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

圖片

為方便復制,在此貼代碼


/*-----------------------------------------------------------*//*< 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、根據所屬 RTOS 平臺和芯片內核修改組件配置信息

cmb_cfg.h

圖片

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


#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)芯片內核根據實(shí)際選擇,目前支持 M0、M3、M4、M7。


#define CMB_CPU_PLATFORM_TYPE          CMB_CPU_ARM_CORTEX_M3

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


#define CMB_USING_DUMP_STACK_INFO

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


#define CMB_PRINT_LANGUAGE             CMB_PRINT_LANGUAGE_ENGLISH

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


#define __CLANG_ARM


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

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 中也不需要包含頭文件,而是在需要位置直接聲明這個(gè)函數即可,因為外部只需要調用這個(gè)函數




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

這樣一來(lái),就不需要添加頭文件的路徑了。

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


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


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











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 時(shí)的信息最全,又不想每次打斷點(diǎn),上面的代碼很好的實(shí)現了功能,同時(shí)也不會(huì )影響程序的正常運行(會(huì )自動(dòng)判斷是否處于調試模式)。

8、實(shí)驗。

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

圖片

運行倉庫例子后,應該能打印下面的信息,告訴我們出現了 div 0 錯誤

圖片

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

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

2、打印函數有問(wèn)題。


圖片

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

圖片

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

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

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


最后再簡(jiǎn)單介紹一下這個(gè)組件的實(shí)現原理:

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

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

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


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

1、棧未被破壞

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

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

圖片

中間多了一個(gè) fputc。

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

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

圖片

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


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



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

相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>