zoukankan      html  css  js  c++  java
  • Cortex-M3开发经验(三):在HardFault中打印栈信息

    Cortex-M3开发经验(三):在HardFault中打印栈信息

    在《Cortex-M3开发经验(二):确认发生HardFault的地方》中,我们提到如何查找出错地方。但是这有一个问题,就是必须链接调试器。那么在某些情况下,我们无法连接调试器,那么就无法读取到栈信息了吗?我们可以在进入HardFault时,获取栈指针,然后通过串口的方式打印出来吗?

    说干就干,有好的想法,也必须有实际的行动验证自己的想法。

    如何获取栈指针?

    卡住我们的第一个问题就是如何获取栈指针了。就是如何获取SP,MSP(主堆栈指针),PSP(进程堆栈指针)的值了。

    在《Cortex-M3权威指南》中,有这么一段话:CM3微控制器内核中共有两个堆栈指针,于是也就是支持两个堆栈,当引用R13(写作SP)时,引用到的是当前正在使用的那一個(MSP或PSP),另一个必须用特殊的指令来访问(MRS和MSR指令)。

    也就是说,我们需要用汇编的指令来获取栈指针。

    uint32_t get_msp_addr()
    {
        __asm("mrs r0, msp");
        __asm("bx lr");
    }
    
    uint32_t get_psp_addr()
    {
        __asm("mrs r0, psp");
        __asm("bx lr");
    }
    
    uint32_t get_sp_addr()
    {
        __asm("mov r0, sp");
        __asm("bx lr");
    }
    

    注:每个编译器所支持C嵌入汇编的方式不同,也可能一些编译器不支持__asm指令。

    通过栈指针获取内核寄存器的值

    uint32_t reg_buff[10];
    uint32_t *sp = NULL;
    
    void HardFault_Handler()
    {
        sp = (uint32_t*)get_msp_addr();
        reg_buff[0] = *(sp++);
        reg_buff[1] = *(sp++);
        reg_buff[2] = *(sp++);
        reg_buff[3] = *(sp++);
        reg_buff[4] = *(sp++);
        reg_buff[5] = *(sp++);
        reg_buff[6] = *(sp++);
        reg_buff[7] = *(sp++);
        reg_buff[8] = *(sp++);
        reg_buff[9] = *(sp++);
        while(1){}
    }
    

    编译,运行!

    结果有点意料之外!

    LR的值和PC的值跟我们之前单步调试的不一样!偏移了12个字节。为什么?后面单步看了一下后发现,我们在HardFault中调用了get_msp_addr这个函数,而调用函数就意味着使用栈空间。如果我把reg_buff放到HardFault中,这样就不止偏移12个字节了!

    有没有更好的方法啊!?

    我们在进入HardFault_Handler函数之前就获取SP指针的值,并作为参数传入到HardFault_Handler中不就可以了吗?

    谁在调用中断处理函数?

    要解决上面的问题,我们就需要知道内核在哪里调用中断函数的,这样我们才能修改对应的中断处理函数,使其可以接收参数。

    《Cortex-M3开发经验(二):确认发生HardFault的地方》中,我们提到过,在发生中断/异常时,内核会去中断向量表中找到对应的中断,找到中断的入口地址。那么我们就看看中断向量表在哪?

    最终,在startup_xxx.S文件中找到了向量表的定义[1]。我们也找到了HardFault_Handler的定义

    .weak	HardFault_Handler
    .type	HardFault_Handler, %function
    
    HardFault_Handler:
    	B .
    

    虽然可能不了解汇编,不知道什么意思,但也能猜测出大概的意思,也可能查资料。发现B是跳转指令,这应该就是跳转到同名的C函数中。那么我们可以修改为:

    .weak	HardFault_Handler
    .type	HardFault_Handler, %function
    
    HardFault_Handler:
    	MOV		r0, lr
    	MOV		r1, sp
    	BL		hardfault_handler
    

    这样就吧LR的值和SP的值传入到hardfault_handler函数中去了[2]

    编译,运行!

    这次的结果就是我们想要的了。


    1. 这是一个启动文件,里面包含了该芯片启动需要的一些过程。 ↩︎

    2. R0~R3寄存器保存的是函数调用时所传入的参数,同时也可作为函数返回值。hardfault_handler定义如下:void hardfault_handler(uint32_t lr, uint32_t sp) ↩︎

  • 相关阅读:
    ckeditor添加插入flv视频的插件
    使用JWPlayer在网页中嵌入视频
    java使用ffmpeg和mencoder做视频格式转换
    spring支持的websocket
    tomcat支持的websocket服务
    MicrosoftRootCertificateAuthority2011.cer 下载
    java读取json文件进行解析,String转json对象
    记一次nmap扫描信息收集过程
    java随机分配端口占用其它服务端口问题完美解决
    申请Let's Encrypt永久免费SSL证书
  • 原文地址:https://www.cnblogs.com/Oushangrong/p/11022858.html
Copyright © 2011-2022 走看看