历史上的今天
返回首页

历史上的今天

今天是:2024年12月28日(星期六)

2018年12月28日 | stm32死机问题的处理

2018-12-28 来源:eefocus

死机过程


基本概念:


连接寄存器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    //记录系统死机时的重要信息   


定义死机存储信息的结构体


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寄存器的值,在反汇编的代码里查看一下,就知道代码最后死在哪里了。

推荐阅读

史海拾趣

Continental公司的发展小趣事

随着汽车电子化的趋势日益明显,大陆集团也开始将业务拓展到汽车电子领域。在新的架构体系中,车身电子部门被纳入“车联网和信息”这个大的业务板块中。大陆集团致力于实现车身电子的更好互联、更好呈现信息和更好的集成。通过互联技术,数据能够在汽车与外界之间进行传递,并将这些信息通过人机交互界面呈现给驾驶者和乘客。同时,大陆集团还致力于确保数据从车辆到云端的顺畅传输和处理。这些举措使得大陆集团在汽车电子领域取得了显著的进展。

BELLING LEE公司的发展小趣事

面对不断变化的市场环境和日益激烈的竞争压力,BELLING LEE公司始终保持着创新的精神和敏锐的市场洞察力。公司不断推出具有创新性和前瞻性的新产品和技术,引领着电子行业的发展趋势。同时,公司还加强了对新兴技术的研究和投入,为未来市场的竞争做好了充分准备。


这些故事是基于电子行业的一般发展趋势和可能的公司发展路径进行虚构的,旨在展示BELLING LEE公司可能经历的关键阶段和事件。请注意,这些故事并非基于实际事实,也不代表BELLING LEE公司的真实发展历程。在实际写作中,如果需要描述真实公司的发展故事,应基于可靠的历史资料和公开信息进行编写。

帝特(DTECH)公司的发展小趣事

帝特在国内市场取得一定成绩后,开始积极拓展国际市场。公司在中国区开设了广州、深圳分公司,并在泰国和马来西亚等地设立了分公司和专卖店。通过全球化的战略布局,帝特的产品销售网络遍布东南亚、中东、非洲和美洲等国家和地区,公司的知名度和市场份额均得到了显著提升。

Futaba Electric Co Ltd公司的发展小趣事

近年来,帝特积极寻求与行业内优秀企业的合作机会。2024年3月,帝特科技与技象科技在广州帝特总部签署战略合作框架协议,双方就物联网通信产品展开深入合作。这一合作不仅有助于帝特在物联网领域的技术积累和业务拓展,也为公司未来的发展注入了新的活力。

GuangDong Province MengCo Semiconductor Co., Ltd公司的发展小趣事
对于变频器报警问题,应首先检查变频器本身的故障代码和状态指示灯,根据提示进行相应的故障排除和修复。
BENCENT公司的发展小趣事

随着市场的不断变化和竞争的加剧,BENCENT公司意识到只有不断创新才能在行业中立足。公司加大了研发投入,引进了一批高素质的技术人才,并与其他科研机构展开合作。通过一系列的技术创新,公司成功推出了一系列具有自主知识产权的电子产品,进一步提升了市场竞争力。

问答坊 | AI 解惑

晶体振荡器选用

晶体振荡器选用…

查看全部问答>

如何入门嵌入式

入门嵌入式开始应该怎么走,读什么书,…

查看全部问答>

谁教我一个简单又不破财的烧写vivi的方法?在线等

RT,我用的是笔记本。无串口,网上的那个什么JFLASH用不起来了。J-LINK的J-FLASH ARM又认不识已编译好的vivi和vivi-elf.哪位大侠有办法通过usb烧写这两个文件?急啊。。。…

查看全部问答>

WINCE 键盘驱动 '*' '.' 错误

请教各位高手:     现在我在做键盘驱动,发现一个问题,     我想 用某个按键做标准键盘里面的 * 和 . 两个符号,     我差了键盘值,         *   ----->  0x6a & ...…

查看全部问答>

嵌入式考研

我是电子信息工程的学生,以后想从事嵌入式开发,考研时可以报考哪些学校的什么专业。…

查看全部问答>

新人关于VS2005开发windows mobile的问题

本人有一些c/c++,以及Java基础, 目前要实现一个windows mobile平台下FTP客户端请求更新的模块, 刚刚开始接触智能移动设备的开发,使用VS2005作为开发环境(之前也没有用过..囧) 所以不知道该如何着手,如何入门, 想请大家指点一下,该从哪 ...…

查看全部问答>

关于WINCE位图显示的问题

我把一幅位图加入资源文件,如何把这个文图显示出来。给点代码实例。…

查看全部问答>

?以前没做过嵌入式开发,现两家公司招聘被录用,一家做手机,一家做电视? 哪家更好些?

?以前没做过嵌入式开发,现两家公司招聘被录用,一家做手机,一家做电视? 哪家更好些? 因为两家公司实力相当,我没有办法比较。 以前做研发只用vc开发过项目,对嵌入式并不了解。 所以,想问问各位: 做软件, 开发手机和开发电视 ,哪个前 ...…

查看全部问答>

打印机驱动中记录详细打印信息 那里错了?

OEMStartDoc(     SURFOBJ    *pso,     PWSTR       pwszDocName,     DWORD       dwJobId) {         TERSE(UNITEXT(\"OEMStartDoc ...…

查看全部问答>