1.背景?
1. 項目中,調試過程或者測試中都會出現程序跑飛問題,這個時候問題特別難查找。
2. 觸發硬件錯誤往往是因為內存錯誤。這種問題特別難查找,尤其是產品到了測試階段,而這個異常復現又比較難的情況下,簡直頭疼。
3. 我目前的工作完全是沒有調試器的,代碼都是寫好了,通過bootloader燒寫進去的。也就意味著不能在void HardFault_Handler(void)中打斷點,然后通過當前lr寄存器中的值查看異常地址。
參考:
STM32進入HardFault_Handler的調試方法-電子工程世界 (eeworld.com.cn)
Keil環境下STM32定位hardfault位置方法和遇到的情況 - 張士玉小黑屋 (zhangshiyu.com)
還參考了很多文章, 有些寫的時候都關掉了, 這里就不列出來了.?
2.裸機程序觸發硬件錯誤
裸機觸發硬件錯誤時, R14(LR)寄存器的值為0xFFFFFFF9,此時看的是MSP指針保存的棧頂地址,然后在Memory Window中輸入這個值(0x20000738),第6個值就是錯誤退的地址(也就是我這個空指針的地址),查看.map文件,顯然我們的0x080001D1在0x080001cd(my_test在flash的首地址)和0x080001d3(my_test1在flash的首地址)之間。因此我們就可以定位出錯誤就在my_test()這個函數中。
?也可以查看匯報,看到地址(不過這里看起來有點讓人蒙圈)
3.RTOS程序觸發硬件錯誤
?RTOS觸發硬件錯誤時, R14(LR)寄存器的值為0xFFFFFFFD, 此時看的是PSP中的棧頂地址。同上分析,我們最終也能通過.map定位錯誤發生的位置。
補充知識:為什么裸機的是0xFFFFFFF9(9:1001)而多線程的是0xFFFFFFFD(1101),如下圖,bit2是區別的關鍵,后面打印錯誤需要用到這一點。
4.在硬件錯誤中斷打印這些值
我們最終的目的,就是在觸發硬件中斷的時候,要將這些值打印出來,從而準確定位出是哪個函數造成的錯誤。?
typedef struct
{u32 R0;u32 R1;u32 R2;u32 R3;u32 R12;u32 LR;u32 PC;u32 xPSR;
}STACK_DATA_TYPE;
void NotOSHardFault_Handler(u32 msp_addr)
{STACK_DATA_TYPE *p; //堆棧中存儲的數據printf("\r\n-----------------ERROR -----------------\r\nHardFault_Handler\r\n");p = (STACK_DATA_TYPE *)msp_addr;printf("R0:0x%08X\r\n", p->R0);printf("R1:0x%08X\r\n", p->R1);printf("R2:0x%08X\r\n", p->R2);printf("R3:0x%08X\r\n", p->R3);printf("R12:0x%08X\r\n", p->R12);printf("LR:0x%08X\r\n", p->LR);printf("PC:0x%08X\r\n", p->PC);printf("xPSR:0x%08X\r\n", p->xPSR);printf("系統即將復位 ...\r\n");
}void HardFault_Handler(void)
{/* Go to infinite loop when Hard Fault exception occurs */
int msp, psp;msp = __get_MSP();NotOSHardFault_Handler(msp);psp = __get_PSP();NotOSHardFault_Handler(psp);while (1){}
}
當前還有一個問題需要解決, 我無法獲取lr寄存器中保存的值, 也就是說我無法準確的確定是進程堆棧出錯還是主堆棧出錯.(我測試了發現在rtos中, 如果讓錯誤放在main函數中, 此時MSP堆棧指向的位置是出錯位置, 如果讓錯誤放在任務中, 此時PSP堆棧指向的位置是出錯位置, 如果在裸機程序中, 通過MSP查找, 當前我用的是keil編譯的,我不知道IAR里面是否有__get_MSP(), __get_PSP())