单片机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吧,有大佬知道内部机制也欢迎分享下
浙公网安备 33010602011771号