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)?

原文地址:https://www.cnblogs.com/Oushangrong/p/11022858.html

时间: 2024-10-01 05:54:27

Cortex-M3开发经验(三):在HardFault中打印栈信息的相关文章

NDK编程中如何在C文件中打印调试信息

1,在Android.mk文件中加上 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE  :=ndkdemoLOCAL_SRC_FILES :=com_app_test_nativeMethod.cLOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -lloginclude $(BUILD_SHARED_LIBRAR

python中提取位图信息(AttributeError: module 'struct' has no attribute 'unstack')

前言 今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用.和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读. 0x00 首先,题目是:读取一个位图文件(xxx.bmp),然后读取前30个字节,从这前三十个字节中提取一些信息. 这里有一些知识要先知道:一个位图的前30位有什么? BMP格式采用小端方式存储数据,文件头的结构按顺序如下: 前两个字节:'BM'表示Windows位图,'BA'表示OS/2位图: 一个4字节整

Cortex M3 NVIC与中断控制

Cortex M3 NVIC与中断控制 宗旨:技术的学习是有限的,分享的精神的无限的. 一.NVIC概览 --嵌套中断向量表控制器 NVIC 的寄存器以存储器映射的方式来访问,除了包含控制寄存器和中断处理的控制逻辑之外, NVIC 还包含了 MPU. SysTick 定时器以及调试控制相关的寄存器. NVIC 共支持 1 至 240 个外部中断输入(通常外部中断写作 IRQs).具体的数值由芯片厂商在设计芯片时决定.此外, NVIC 还支持一个"永垂不朽"的不可屏蔽中断( NMI)输入

三个线程循环打印ABC10次的几种解决方法

题目:有三个线程分别打印A.B.C,请用多线程编程实现,在屏幕打印10次ABC 整体思路:该问题为三个线程的同步唤醒机制即ThreadA->ThreadB->ThreadC->ThreadA循环执行三个线程. public class MyThreadPrinter2 implements Runnable { private String name; private Object prev; private Object self; private Thread thread; pub

ARM cortex M3寄存器及指令集

1.cortex M3拥有通用寄存器R0-R15及一些特殊寄存器: R0‐R7 也被称为低组寄存器.所有指令都能访问它们.它们的字长全是 32 位,复位后的初始值是不可预料的. R8‐R12 也被称为高组寄存器.这是因为只有很少的 16 位 Thumb 指令能访问它们, 32位的指令则不受限制.它们也是 32 位字长,且复位后的初始值是不可预料的 . R13 是堆栈指针.在 CM3 处理器内核中共有两个堆栈指针,于是也就支持两个堆栈.当引用 R13(或写作 SP)时,你引用到的是当前正在使用的那

DIFramework.NET ━ Web中打印的各种方案参考-欢迎补充

RDIFramework.NET ━ Web中打印的各种方案参考-欢迎补充 做Web开发的同志应该都深有体会,在web程序中打印不再象应用程序中那样便于控制了,web程序天生的一些特性造成了这个缺点,如:打印机在本地,而文件确可能在服务器上:格式如何控制和定制等等,都给我们开发中带来了很多问题,当然有了问题就会有人来研究解决,这里我先对目前流行的几种方式做个简单介绍: 一.IE直接打印 这个不用多说,直接调用window.print或者webrower控件的ExecWB方法来打印.方便快捷,客户

RDIFramework.NET ━ Web中打印的各种方案参考-欢迎补充

RDIFramework.NET ━ Web中打印的各种方案参考-欢迎补充 做Web开发的同志应该都深有体会,在web程序中打印不再象应用程序中那样便于控制了,web程序天生的一些特性造成了这个缺点,如:打印机在本地,而文件确可能在服务器上:格式如何控制和定制等等,都给我们开发中带来了很多问题,当然有了问题就会有人来研究解决,这里我先对目前流行的几种方式做个简单介绍: 一.IE直接打印 这个不用多说,直接调用window.print或者webrower控件的ExecWB方法来打印.方便快捷,客户

CAD技巧—CAD中打印CAD图纸怎么设置成纵向?

在CAD行业中,大家都知道我们最常见的就是CAD图纸,但是CAD图纸都是dwg格式的,有的时候我们为了查看方便需要把CAD图纸文件进行打印出来,但是如果我们在进行打印的时候想要把CAD图纸打印设置成纵向,具体要怎么操作?CAD中打印CAD图纸怎么设置成纵向?下面小编就来教教大家在迅捷CAD编辑器标准版中打印CAD图纸怎么设置成纵向.想要了解的朋友就一起来看看吧! 第一步:在电脑中打开一个浏览器,在浏览器的搜索框中搜索迅捷CAD编辑器标准版,然后进入迅捷CAD官网,点击下载安装最新版本的迅捷CAD

如何在CAD中打印CAD图纸的一部分文件?

如何在CAD中打印CAD图纸的一部分文件?当CAD设计师们在编辑完CAD文件时,常常需要打印CAD文件,但是在有些时候我们必不需要将整张图纸进行打印,只需要打印CAD图纸文件其中的一部分,那如何在CAD中打印CAD图纸的一部分?具体应该要怎么进行操作呢?下面就来教教大家在迅捷CAD编辑器中如何打印CAD图纸的一部分文件?想要了解的朋友就一起来看看吧,希望能够帮助到你们. 第一步:在电脑桌面中任意的打开一个浏览器,在浏览器中搜索迅捷CAD编辑器,然后鼠标点击下载安装最新版本的CAD编辑器,下载完成