找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 73060|回復(fù): 14
打印 上一主題 下一主題
收起左側(cè)

stm32 HardFault_Handler調(diào)試及問(wèn)題查找方法

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:91350 發(fā)表于 2015-10-29 17:16 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本帖最后由 piaolin 于 2015-10-29 17:20 編輯

  STM32出現(xiàn)HardFault_Handler故障的原因主要有兩個(gè)方面:

1、內(nèi)存溢出或者訪問(wèn)越界。這個(gè)需要自己寫(xiě)程序的時(shí)候規(guī)范代碼,遇到了需要慢慢排查。

2、堆棧溢出。增加堆棧的大小。

出現(xiàn)問(wèn)題時(shí)排查的方法:

發(fā)生異常之后可首先查看LR寄存器中的值,確定當(dāng)前使用堆棧為MSP或PSP,然后找到相應(yīng)堆棧的指針,并在內(nèi)存中查看相應(yīng)堆棧里的內(nèi)容。由于異常發(fā)生時(shí),內(nèi)核將R0~R3、R12、Returnaddress、PSR、LR寄存器依次入棧,其中Return address即為發(fā)生異常前PC將要執(zhí)行的下一條指令地址。

注意:寄存器均是32位,且STM32是小端模式。(參考Cortex-M3權(quán)威)

編寫(xiě)問(wèn)題代碼如下:

  1. void StackFlow(void)

  2. {

  3. int a[3],i;

  4. for(i=0; i<10000; i++)

  5. {

  6. a[i]=1;

  7. }

  8. }

  9. void SystemInit(void)

  10. {





  11. RCC->CR |= (uint32_t)0x00000001;



  12. RCC->CFGR = 0x00000000;



  13. RCC->CR &= (uint32_t)0xFEF6FFFF;



  14. RCC->PLLCFGR = 0x24003010;

  15. StackFlow();



  16. RCC->CR &= (uint32_t)0xFFFBFFFF;

  17. 。。。。。。。。。。。。。。

  18. }
復(fù)制代碼


DEBUG如下圖

SP值為0x20008560,查看堆棧里面的值依次為R0~R3、R12、Return address、PSR、LR, 例如R0(1027 00 00), 顯然堆棧后第21個(gè)字節(jié)到24字節(jié)即為Returnaddress,該地址0x08001FFD即為異常前PC將要執(zhí)行的下一條指令地址(即StackFlow()后面的語(yǔ)句處RCC->CR &= (uint32_t)0xFFFBFFFF)

另一種方法:

默認(rèn)的HardFault_Handler處理方法不是B .這樣的死循環(huán)么?樓主將它改成BXLR直接返回的形式。然后在這條語(yǔ)句打個(gè)斷點(diǎn),一旦在斷點(diǎn)中停下來(lái),說(shuō)明出錯(cuò)了,然后再返回,就可以返回到出錯(cuò)的位置的下一條語(yǔ)句那兒


      Cortex-M3/4的Fault異常是由于非法的存儲(chǔ)器訪問(wèn)(比如訪問(wèn)0地址、寫(xiě)只讀存儲(chǔ)位置等)和非法的程序行為(比如除以0等)等造成的。常見(jiàn)的4種異常及產(chǎn)生異常的情況如下:


BusFault:在fetch指令、數(shù)據(jù)讀寫(xiě)、fetch中斷向量或中斷時(shí)存儲(chǔ)恢復(fù)寄存器棧情況下,檢測(cè)到內(nèi)存訪問(wèn)錯(cuò)誤則產(chǎn)生BusFault。
Memory ManagementFault:訪問(wèn)了內(nèi)存管理單元(MPU)定義的不合法的內(nèi)存區(qū)域,比如向只讀區(qū)域?qū)懭霐?shù)據(jù)。
UsageFault:檢測(cè)到未定義指令或在存取內(nèi)存時(shí)有未對(duì)齊。還可以通過(guò)軟件配置是否檢測(cè)到除0和其它未對(duì)齊內(nèi)存訪問(wèn)也產(chǎn)生該異常,默認(rèn)關(guān)閉,需要在工程初始化時(shí)配置:

[cpp] viewplaincopyprint?


  • SCB->CCR |= 0x18; // enable div-by-0 and unaligned fault  


HardFault:在調(diào)試程序過(guò)程中,這種異常最常見(jiàn)。上面三種異常發(fā)生任何一種異常都會(huì)引起HardFault,在上面的三種異常未使能的情況下,默認(rèn)發(fā)生異常時(shí)進(jìn)入HardFault中斷服務(wù)程序。使能前三種異常也要在初始化時(shí)配置:

[cpp] viewplaincopyprint?


  • SCB->SHCSR |= 0x00007000;   // enable Usage Fault, Bus Fault, and MMU Fault  



在默認(rèn)復(fù)位初始化時(shí),HardFault使能,其它三者不使能,因此當(dāng)程序中出現(xiàn)不合法內(nèi)存訪問(wèn)(一般是指針錯(cuò)誤引起)或非法的程序行為(一般就是數(shù)學(xué)里面常見(jiàn)的除0)時(shí)都將產(chǎn)生HardFault中斷。

[url=]2 HardFault調(diào)試方法[/url]假設(shè)IDE環(huán)境為Keil,芯片為STM32F103。
在stm32f10x_it.c中,添加軟件斷點(diǎn),一旦調(diào)試時(shí)出現(xiàn)Hard Fault則會(huì)在停在__breakpoint(0)處。




  •   
  • void HardFault_Handler(void)  
  • {  
  •    
  •   if (CoreDebug->DHCSR & 1) {  //check C_DEBUGEN == 1 -> Debugger Connected  
  •       __breakpoint(0);  // halt program execution here         
  •   }  
  •   while (1)  
  •   {  
  •   }  
  • }  



當(dāng)進(jìn)入HardFault斷點(diǎn)后,菜單欄Peripherals >Core Peripherals >FaultReports打開(kāi)異常發(fā)生的報(bào)告,查看發(fā)生異常的原因。
  

上面的報(bào)告發(fā)生了BUS FAULT,并將Fault的中斷服務(wù)轉(zhuǎn)向Hard Fault。


相對(duì)于檢測(cè)發(fā)生了什么異常,定位異常發(fā)生位置顯得更重要。
(1)打開(kāi)Call Stack窗口(如下圖左側(cè),斷點(diǎn)停在Hard Fault服務(wù)程序中)


(2)在Call Stack的HardFault_Handler上右鍵Show CallerCode(有的Keil版本也可以直接雙擊)


這時(shí)將跳轉(zhuǎn)到發(fā)生異常的源代碼位置(如上圖),異常發(fā)生在p->hour=0這一行。這里錯(cuò)誤很明顯:指針p尚未為成員變量分配內(nèi)存空間,直接訪問(wèn)未分配的內(nèi)粗空間肯定出錯(cuò)。

再說(shuō)明2點(diǎn):
[1] 在復(fù)雜的情況下,即使定位了異常發(fā)生位置也很難容易的改正錯(cuò)誤,要學(xué)會(huì)使用Watch窗口對(duì)發(fā)生錯(cuò)誤的指針變量進(jìn)行跟蹤;
[2]在問(wèn)題不明晰的情況下,嘗試分析反匯編代碼,就自己遇到的,部分情況下的異常發(fā)生在BL等跳轉(zhuǎn)指令處,BL跳轉(zhuǎn)到了不合法的內(nèi)存地址產(chǎn)生異常

Refrences:
[1] Application Note209. Using Cortex-M3 and Cortex-M4 FaultExceptions.


[2] Cortex-M3權(quán)威指南





分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂1 踩

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:91350 發(fā)表于 2015-10-29 17:21 | 只看該作者
看到有朋友遇到Hard Fault 異常錯(cuò)誤,特地找到一篇飛思卡爾工程師寫(xiě)的一片經(jīng)驗(yàn)帖,定位Hard Fault 異常。

Kinetis MCU 采用 Cortex-M4 的內(nèi)核,該內(nèi)核的 Fault 異?梢圆东@非法的內(nèi)存訪問(wèn)和非法的編程行為。Fault異常能夠檢測(cè)到以下幾類非法行為:
·        總線 Fault:  在取址、數(shù)據(jù)讀/寫(xiě)、取中斷變量、進(jìn)入/退出中斷時(shí)寄存器堆棧操作(入棧/出棧)時(shí)檢測(cè)到內(nèi)存訪問(wèn)錯(cuò)誤。
·        存儲(chǔ)器管理 Fault: 檢測(cè)到內(nèi)存訪問(wèn)違反了內(nèi)存保護(hù)單元(MPU, MemoryProtection Unit)定義的區(qū)域。
·        用法 Fault:  檢測(cè)到未定義的指令異常,未對(duì)其的多重加載/存儲(chǔ)內(nèi)存訪問(wèn)。如果使能相應(yīng)控制位,還可以檢測(cè)出除數(shù)為零以及其他未對(duì)齊的內(nèi)存訪問(wèn)。

·        硬 Fault:  如果上述的總線 Fault、存儲(chǔ)器管理 Fault、用法 Fault 的處理程序不能被執(zhí)行(例如禁能了總線 Fault、存儲(chǔ)器管理Fault、用法Fault 的異常或者在這些異常處理程序中又出現(xiàn)了新的Fault)則觸發(fā)硬Fault。
       MQX 操作系統(tǒng)啟動(dòng)的時(shí)候會(huì)安裝上默認(rèn)的異常中斷處理函數(shù),當(dāng)系統(tǒng)異常時(shí)會(huì)產(chǎn)生一個(gè)“unexpected”中斷,內(nèi)核就會(huì)自動(dòng)調(diào)用異常處理函數(shù),同時(shí)也將運(yùn)行用戶自定義的處理函數(shù),來(lái)實(shí)現(xiàn)特殊故障的定位方法。
       默認(rèn)情況下,MQX把出現(xiàn)異常的任務(wù)掛起,避免故障進(jìn)一步擴(kuò)大。通過(guò)TAD 任務(wù)感知調(diào)試插件的Task summary 功能,我們可以觀察到出現(xiàn)異常的任務(wù)情況。
開(kāi)發(fā)人員在調(diào)試期間,需要弄清楚系統(tǒng)異常觸發(fā)了哪類Fault,由什么原因觸發(fā)了Fault 以及定位觸發(fā)Fault 的代碼。在這種情況下,可以利用自定義的Fault 中斷處理程序來(lái)分析
Fault 出錯(cuò)原因。
     為了解釋所述的 Fault 中斷處理程序的原理,這里重述一下當(dāng)系統(tǒng)產(chǎn)生異常時(shí) MCU 的處理過(guò)程:
·        有一個(gè)壓棧的過(guò)程,若產(chǎn)生異常時(shí)使用 PSP(進(jìn)程棧指針),就壓入到 PSP 中,若產(chǎn)生異常時(shí)使用MSP(主棧指針),就壓入MSP 中。
·        會(huì)根據(jù)處理器的模式和使用的堆棧,設(shè)置 LR 的值(當(dāng)然設(shè)置完的LR 的值再壓棧)。
·        異常保存,硬件自動(dòng)把 8 個(gè)寄存器的值壓入堆棧(8 個(gè)寄存器依次為 xPSR、PC、LR、R12以及 R3~R0)。如果異常發(fā)生時(shí),當(dāng)前的代碼正在使用PSP,則上面8 個(gè)寄存器壓入PSP; 否則就壓入MSP。
       當(dāng)系統(tǒng)產(chǎn)生異常時(shí),我們需要兩個(gè)關(guān)鍵寄存器值,一個(gè)是 PC ,一個(gè)是 LR (鏈接寄存器),通過(guò) LR找到相應(yīng)的堆棧,再通過(guò)堆棧找到觸發(fā)異常的PC 值。將產(chǎn)生異常時(shí)壓入棧的 PC 值取出,并與反匯編的代碼對(duì)比就能得到哪條指令產(chǎn)生了異常。
        這里解釋一下關(guān)于 LR 寄存器的工作原理。如上所述,當(dāng) Cortex-M4 處理器接受了一個(gè)異常后,寄存器組中的一些寄存器值會(huì)被自動(dòng)壓入當(dāng)前?臻g里,這其中就包括鏈接寄存器(LR )。這時(shí)的 LR 會(huì)被更新為異常返回時(shí)需要使用的特殊值(EXC_RETURN)。關(guān)于
EXC_RETURN 的定義如下,其為 32 位數(shù)值,高 28 位置 1,第 0 位到第三位則提供了異常返回機(jī)制所需的信息,如下表所示?梢(jiàn)其中第 2 位標(biāo)示著進(jìn)入異常前使用的棧是 MSP還是PSP。在異常處理過(guò)程結(jié)束時(shí),MCU 需要根據(jù)該值來(lái)分配 SP 的值。這也是本方法中用來(lái)判斷所使用堆棧的原理,其實(shí)現(xiàn)方法可以從后面_init_hardfault_isr 中看到。
   另外,我們可以利用 MQX 的控制臺(tái)串口輸出Fault 異常信息來(lái)幫助調(diào)試。編寫(xiě)Fault 處理程序時(shí),將啟動(dòng)代碼中默認(rèn)的Fault 處理程序跟換成自己需要的Fault 處理程序。需要注意的是,由于是在中斷中進(jìn)行打印輸出,MQX的控制臺(tái)串口只能使用POLL 輪詢模式的驅(qū)動(dòng),不能使用中斷模式的驅(qū)動(dòng)。
     用戶可以編寫(xiě)自定義的硬 Fault 處理程序_int_hardfault_isr,修改 MQX 的中斷向量定義vector.c,把里面的DEFAULT_VECTOR 代碼段換成下面的代碼。當(dāng)系統(tǒng)出現(xiàn)硬Fault 異常時(shí),將會(huì)調(diào)用自定義的Fault 處理_int_hardfault_isr函數(shù)。在這個(gè)函數(shù),我們可以通過(guò)StackTrace-back 回溯出現(xiàn)問(wèn)題的代碼。
我們可以在_int_hardfault_isr 函數(shù)里將出現(xiàn)異常時(shí)的寄存器、堆棧、狀態(tài)寄存器等信息打印出來(lái)。如果系統(tǒng)出現(xiàn)異常時(shí),一般情況都會(huì)通過(guò)串口控制臺(tái)打印出LR,PC的值。然后根據(jù)編譯器生成的map 文件,找到出現(xiàn)問(wèn)題的具體函數(shù)。
     從上圖的串口輸出我們可以看到 PC 和 LR 寄存器值,PC 的值為 0x56c6,我們根據(jù)匯編代碼可以找到出現(xiàn)問(wèn)題的指令。從而大大縮小了查找出現(xiàn)問(wèn)題的范圍,可以幫助開(kāi)發(fā)人員快速定位問(wèn)題的根本原因。

附錄Fault異常中斷處理代碼:
  1. // hard fault handler in C,
  2. // with stack frame location as input parameter
  3. void hard_fault_handler_c (unsigned int * hardfault_args)
  4. {
  5.   unsigned int stacked_r0;
  6.   unsigned int stacked_r1;
  7.   unsigned int stacked_r2;
  8.   unsigned int stacked_r3;
  9.   unsigned int stacked_r12;
  10.   unsigned int stacked_lr;
  11.   unsigned int stacked_pc;
  12.   unsigned int stacked_psr;

  13.   stacked_r0 = ((unsigned long)hardfault_args[0]);
  14.   stacked_r1 = ((unsigned long)hardfault_args[1]);
  15.   stacked_r2 = ((unsigned long)hardfault_args[2]);
  16.   stacked_r3 = ((unsigned long)hardfault_args[3]);

  17.   stacked_r12 = ((unsigned long)hardfault_args[4]);
  18.   stacked_lr = ((unsigned long)hardfault_args[5]);
  19.   stacked_pc = ((unsigned long)hardfault_args[6]);
  20.   stacked_psr = ((unsigned long) hardfault_args[7]);

  21.   printf ("\n\n[Hard faulthandler - all numbers in hex]\n");
  22.   printf ("R0 = %x\n",stacked_r0);
  23.   printf ("R1 = %x\n",stacked_r1);
  24.   printf ("R2 = %x\n",stacked_r2);
  25.   printf ("R3 = %x\n",stacked_r3);
  26.   printf ("R12 = %x\n",stacked_r12);
  27.   printf ("LR [R14] = %x  subroutine call return address\n",stacked_lr);
  28.   printf ("PC [R15] = %x  program counter\n", stacked_pc);
  29.   printf ("PSR = %x\n",stacked_psr);

  30.   /******************* Add yourdebug trace here ***********************/
  31.   _int_kernel_isr();
  32. }

  33. /* hard fault interrupt handler */
  34. void _int_hardfault_isr( )
  35. {
  36.   __asm("TST LR, #4");
  37.   __asm("ITE EQ");
  38.   __asm("MRSEQ R0,MSP");
  39.   __asm("MRSNE R0,PSP");
  40.   __asm("Bhard_fault_handler_c");
  41. }
復(fù)制代碼

fault_isr.c.zip

1.42 KB, 下載次數(shù): 51, 下載積分: 黑幣 -5

vectors.c.zip

3.43 KB, 下載次數(shù): 41, 下載積分: 黑幣 -5

如何定位Kinetis MCU Hard Fault異常.pdf

362.58 KB, 下載次數(shù): 101, 下載積分: 黑幣 -5

回復(fù)

使用道具 舉報(bào)

板凳
ID:130367 發(fā)表于 2016-7-13 21:51 | 只看該作者
正在調(diào)試STM32F205,OS操作系統(tǒng)調(diào)度USB功能,樓主的方法正中,非常感謝!  
回復(fù)

使用道具 舉報(bào)

地板
ID:60851 發(fā)表于 2016-8-3 16:57 | 只看該作者
謝謝樓主分享
回復(fù)

使用道具 舉報(bào)

5#
ID:163338 發(fā)表于 2017-2-3 12:18 | 只看該作者
我剛好需要,謝謝!
回復(fù)

使用道具 舉報(bào)

6#
ID:166396 發(fā)表于 2017-2-24 14:15 | 只看該作者
謝謝樓主,正需要
回復(fù)

使用道具 舉報(bào)

7#
ID:19303 發(fā)表于 2017-4-20 09:19 | 只看該作者
非常好,正好遇到同樣的問(wèn)題
回復(fù)

使用道具 舉報(bào)

8#
ID:221889 發(fā)表于 2017-7-24 00:12 | 只看該作者
需要,感謝 。。。。。。。!
回復(fù)

使用道具 舉報(bào)

9#
ID:221889 發(fā)表于 2017-7-24 10:39 | 只看該作者
測(cè)試 ,需要下載
回復(fù)

使用道具 舉報(bào)

10#
ID:250419 發(fā)表于 2017-11-17 15:02 | 只看該作者
謝謝樓主分享
回復(fù)

使用道具 舉報(bào)

11#
無(wú)效樓層,該帖已經(jīng)被刪除
12#
ID:321859 發(fā)表于 2018-12-25 10:31 | 只看該作者

謝謝樓主分享
回復(fù)

使用道具 舉報(bào)

13#
ID:551850 發(fā)表于 2019-5-31 14:37 | 只看該作者
感謝分享 很有用
回復(fù)

使用道具 舉報(bào)

14#
ID:245771 發(fā)表于 2019-8-24 10:16 | 只看該作者
如果運(yùn)行幾個(gè)小時(shí)后才出現(xiàn)硬件錯(cuò)誤,而且查看代碼定位到HAL_SPI_TransmitReceive應(yīng)該查什么
回復(fù)

使用道具 舉報(bào)

15#
無(wú)效樓層,該帖已經(jīng)被刪除
16#
ID:276761 發(fā)表于 2020-12-20 17:29 | 只看該作者
感謝分享 很有用
回復(fù)

使用道具 舉報(bào)

17#
ID:345850 發(fā)表于 2023-7-4 17:15 | 只看該作者
謝謝樓主,正需要
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表