STM32宕机问题

死机过程

基本概念:
连接寄存器LR:调动子程序时,自动存储下一次返回的地址,其实就是最近调用的那一次函数的地址。

死机的过程:


这里我们最需要关注的是第一步入栈:
其中8个寄存器的顺序是


其中出现异常时LR里面的值是固定的

最后更新堆栈指针,我们根据最后使用的堆栈里面的内容,就可以知道出问题时的最后现场。

死机定位

思路简单来讲就是:
找到死机时候的lr寄存器,然后根据lr寄存器的值,找到此时压入的是psp堆栈,还是msp堆栈。然后根据堆栈里的内容(最后压入堆栈的8个寄存器的值)。其中压入到堆栈里面的return address这个值至关重要,这个是上一次,压入堆栈的最后一个函数,由此就可以定位出死机的位置。

使用keil环境直接debug定位

以实际的例子分析:
首先写一个能够使单片机死机的代码,debug跑起来:

故意使用空指针将程序跑死


debug的时候点开view,选择register,

根据LR的值判断,使用的是psp堆栈,然后打开memory windows,查看地址为0x20002FF0的内存数据,即为最后一次入栈的内容。右键选择,long显示


找到第六个0x08034a17这个地址,view,disassembly window这个窗口,查看反汇编文件,在反汇编窗口,右键,点击show disassembly at address ,输入地址,就可以找到对应汇编文件的位置,同时可以定位到c语言中对应的位置。

这样就可以定位到,死机之前的位置
2. 还有一种方式就是,通过看函数的调用关系,直接看到程序是死在那里的
点击 view call stack window ,直接可以看到,程序死机之前的函数调用关系

这样也可快速定位,原理其实是一样的,都是通过看堆栈数据,第一种方法只是自己手动的走了一遍这个流程而已。

完善的解决方案

因为在实际项目中死机问题很多都是难复现的,产品在使用过程中难以长期debug,导致以上的办法实际上不是很实用。因此,我们需要更加方便的解决办法。
思路:相当于给单片机做一个黑盒子,每当系统崩溃时。我们会记录,重要的寄存器以及堆栈信息,存在flash里面。这样一旦出现问题。我们只要重新debug一下,从flash里面将保存的信息读出来。这样难复现的死机问题,我们也是有办法锁定位置的。

修改.s文件
将原来的HardFault_Handler 内容换成以上新的内容,hard_fault_handler_c 是一个函数,死机时就会跳转到这个函数。
HardFault_Handler PROC    
                 IMPORT  hard_fault_handler_c
                 TST LR, #4 
                 ITE EQ    
                 MRSEQ R0, MSP
                 MRSNE R0, PSP
                 B  hard_fault_handler_c               
                 ENDP

选择一块区域作为死机时,信息存储的地方。

#define ADDR_FLASH_SECTOR_2     ((u32)0x08008000)     //扇区2起始地址, 16 Kbytes
#define SYS_CRASH_INFO_ADDR ADDR_FLASH_SECTOR_2    //记录系统死机时的重要信息  
1
2
定义死机存储信息的结构体

typedef struct {
     unsigned int crash_time;
     unsigned int is_crash;
     /* register info*/
     unsigned long stacked_r0;
     unsigned long stacked_r1; 
     unsigned long stacked_r2;
     unsigned long stacked_r3;   
     unsigned long stacked_r12; 
     unsigned long stacked_lr; 
     unsigned long stacked_pc; 
     unsigned long stacked_psr; 
     unsigned long SHCSR; 
     unsigned long MFSR; 
     unsigned long BFSR;  
     unsigned long UFSR; 
     unsigned long HFSR; 
     unsigned long DFSR; 
     unsigned long MMAR; 
     unsigned long BFAR;
} System_Crash_Info;

void hard_fault_handler_c(unsigned int * hardfault_args)
{    
   static System_Crash_Info crash_info;
   memset(&crash_info, 0, sizeof(System_Crash_Info));
  
   crash_info.is_crash = 1;
   crash_info.crash_time = (unsigned int)HAL_GetTick();
  
   crash_info.stacked_r0 = ((unsigned long) hardfault_args[0]); 
   crash_info.stacked_r1 = ((unsigned long) hardfault_args[1]); 
   crash_info.stacked_r2 = ((unsigned long) hardfault_args[2]); 
   crash_info.stacked_r3 = ((unsigned long) hardfault_args[3]); 
   crash_info.stacked_r12 = ((unsigned long) hardfault_args[4]);   
   crash_info.stacked_lr = ((unsigned long) hardfault_args[5]);  
   crash_info.stacked_pc = ((unsigned long) hardfault_args[6]); 
   crash_info.stacked_psr = ((unsigned long) hardfault_args[7]);

  crash_info.MFSR = (*((volatile unsigned char *)(0xE000ED28))); //存储器管理fault状态寄存器  
   crash_info.BFSR = (*((volatile unsigned char *)(0xE000ED29))); //总线fault状态寄存器  
   crash_info.UFSR = (*((volatile unsigned short int *)(0xE000ED2A)));//用法fault状态寄存器   
   crash_info.HFSR = (*((volatile unsigned long *)(0xE000ED2C)));  //硬fault状态寄存器    
   crash_info.DFSR = (*((volatile unsigned long *)(0xE000ED30))); //调试fault状态寄存器 
   crash_info.MMAR = (*((volatile unsigned long *)(0xE000ED34))); //存储管理地址寄存器 
   crash_info.BFAR = (*((volatile unsigned long *)(0xE000ED38))); //总线fault地址寄存器 
  
   u8 ret = STMFLASH_EraseSector(STMFLASH_GetFlashSector(SYS_CRASH_INFO_ADDR));
   u8 ret2 = STMFLASH_Write(SYS_CRASH_INFO_ADDR, (u32 *)(&crash_info), (3+sizeof(System_Crash_Info))/4);

  while (1); 
}

正常代码里加上

   System_Crash_Info crash_info = {0};
   crash_info = *(System_Crash_Info*)(SYS_CRASH_INFO_ADDR);
   if (crash_info.is_crash != 1) {
       STMFLASH_EraseSector(STMFLASH_GetFlashSector(SYS_CRASH_INFO_ADDR));
   } else {
       CT_PRINTF("code has ever crash\n");
       //这里查看之前死掉时的情况,或者是将crash_info 里的信息打印出来分析也可以
   }

实际的例子:

这样代码重新debug,就可以看到之前死机的情况了。直接看lr寄存器的值,在反汇编的代码里查看一下,就知道代码最后死在哪里了。
————————————————
版权声明:本文为CSDN博主「txsonfire」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_27087571/article/details/84580532

posted @ 2019-09-24 10:15  朽木生  阅读(660)  评论(0编辑  收藏  举报