单片机hardfault的dying message

  网上挺多教hardfault了怎么通过仿真调试找到出问题的语句的,然而在实际产品中,这操作不好落地,实际还是得在代码里获取到单片机的dying message,再看通过日志或者存存储器的形式记录下来。以STM32G4为demo,代码如下:

 

 1 #if defined ( __CC_ARM )
 2     register uint32_t Core_Reg_LR __ASM("lr");
 3     register uint32_t Core_Reg_MSP __ASM("msp");
 4     register uint32_t Core_Reg_PSP __ASM("psp");
 5 
 6     #define Core_Update_LR()
 7     #define Core_Update_MSP()
 8     #define Core_Update_PSP()
 9 
10 #elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
11     uint32_t Core_Reg_LR;
12     uint32_t Core_Reg_MSP;
13     uint32_t Core_Reg_PSP;
14 
15     #define Core_Update_LR()    __ASM volatile ("mov %0, lr" : "=r" (Core_Reg_LR))
16     #define Core_Update_MSP()    __ASM volatile ("MRS %0, msp" : "=r" (Core_Reg_MSP))
17     #define Core_Update_PSP()    __ASM volatile ("MRS %0, psp"  : "=r" (Core_Reg_PSP))
18 
19 #else
20 
21     #error "Core_Update_xxx unimplementation !!!"
22 
23 #endif
24 
25 #define Core_Get_LR()    Core_Reg_LR
26 #define Core_Get_MSP()    Core_Reg_MSP
27 #define Core_Get_PSP()    Core_Reg_PSP
28 
29 #define LR_STACK_PROCESS_STACK_INDICATOR    0x00000004U
30 uint32_t lr, sp;
31 uint32_t stack_r0, stack_r1, stack_r2, stack_r3, stack_r12, stack_lr, stack_pc, stack_xPSR;
32 
33 void HardFault_Handler(void)
34 {
35     /* USER CODE BEGIN HardFault_IRQn 0 */
36     Core_Update_LR();
37     lr = Core_Get_LR();
38     db_printf("lr = 0x%08x\n", lr);
39 
40     if(lr & LR_STACK_PROCESS_STACK_INDICATOR)
41     {
42         Core_Update_PSP();
43         sp = Core_Get_PSP();
44     }
45     else
46     {
47         Core_Update_MSP();
48         sp = Core_Get_MSP();
49     }
50     db_printf("sp = 0x%08x\n", sp);
51 
52     stack_r0 = ((uint32_t*)sp)[0];
53     stack_r1 = ((uint32_t*)sp)[1];
54     stack_r2 = ((uint32_t*)sp)[2];
55     stack_r3 = ((uint32_t*)sp)[3];
56     stack_r12 = ((uint32_t*)sp)[4];
57     stack_lr = ((uint32_t*)sp)[5];
58     stack_pc = ((uint32_t*)sp)[6];
59     stack_xPSR = ((uint32_t*)sp)[7];
60     db_printf("stack_r0 = 0x%08x\n", stack_r0);
61     db_printf("stack_r1 = 0x%08x\n", stack_r1);
62     db_printf("stack_r2 = 0x%08x\n", stack_r2);
63     db_printf("stack_r3 = 0x%08x\n", stack_r3);
64     db_printf("stack_r12 = 0x%08x\n", stack_r12);
65     db_printf("stack_lr = 0x%08x\n", stack_lr);
66     db_printf("stack_pc = 0x%08x\n", stack_pc);
67     db_printf("stack_xPSR = 0x%08x\n", stack_xPSR);
68     /* USER CODE END HardFault_IRQn 0 */
69     while (1)
70     {
71         /* USER CODE BEGIN W1_HardFault_IRQn 0 */
72         /* USER CODE END W1_HardFault_IRQn 0 */
73     }
74 }

 

 

  代码思路跟仿真调试其实是一致的:

  1.得到进入hardfault时的lr

  2.根据lr确认当时使用的栈是MSP还是PSP

  3.得到对应的栈指针

  4.根据栈指针进行偏移,获取进入hardfault时刻的寄存器信息,其中PC寄存器的值为进入hardfault时刻程序将执行的下一条指


  再说点细节:

  1.因为ARM Compiler5基于ARMCC,ARM Compiler6基于ARMCLANG,他们汇编的写法是不一致的,所以获取寄存器的方式是有差异的

  2.进入hardfault后的lr是0xFFFFFFxx这样的形式的,进入中断处理函数后,如果在ISR内通过调用函数的方式获取lr,那获取到的lr就不是hardfault的lr,而是获取lr函数执行时的lr,所以在获取lr要用宏函数或者用force/static inline修饰

  3.db_printf根据实际工况改成对应的存储方式,有外接设备记日志就可以通过对应外设通讯打印,没就存到flash、EMMC、FRAM之类的存储器吧

  4.获取到dying message的PC有可能滞后,要往前看多几条,尤其是PC指向一个函数的头的时候//这个是实际实验结果,还没去分析具体原理,标个TODO吧,有大佬知道内部机制也欢迎分享下

posted @ 2025-05-02 12:07  蓝bleu  阅读(31)  评论(0)    收藏  举报