STM32程序发生异常崩溃时,怎样从串口输出当时的程序调用栈等信息

embedded/2025/1/18 17:46:42/

当STM32程序发生异常崩溃时,为了从串口输出当时的程序调用栈信息,并使用Keil等工具确定具体的函数信息,你可以按照以下步骤操作:

  1. 启用调试信息输出

    • 在STM32程序中,你需要先确保启用了调试信息的输出。这通常涉及到在编译器设置中开启调试信息(如DWARF格式),以便在程序崩溃时能够输出有用的调试数据。
  2. 捕获异常并输出调用栈

    • 在程序中加入异常处理机制,例如使用C++的异常处理或者嵌入式系统中的硬件异常处理(如使用setjmplongjmp,或者定义硬件异常处理函数)。
    • 当异常发生时,捕获异常并在异常处理函数中获取当前的调用栈信息。你可以使用backtrace库或者自定义的函数来获取调用栈。
    • 将调用栈信息通过串口输出。这可能需要你将调用栈地址转换为具体的函数名或行号,这通常需要在编译时包含调试信息,并在程序运行时解析这些信息。
  3. 串口输出

    • 配置STM32的UART(通用异步收发传输器)以输出调试信息。确保串口初始化正确,波特率等参数与接收设备匹配。
    • 在异常处理函数中,将捕获的调用栈信息通过串口发送出去。
  4. 使用Keil等工具分析

    • 在Keil等IDE中,你可以使用调试器来加载崩溃时的程序镜像。
    • 通过查看串口输出的调用栈信息,你可以在Keil中找到对应的函数地址。
    • 利用Keil的符号表(Symbol Table)或者地图文件(Map File),将地址解析为具体的函数名和行号。
    • 通过分析调用栈,你可以确定是哪个函数调用导致了崩溃,并进一步调试以找到问题的根源。
  5. 注意事项

    • 确保在编译时开启了调试信息的生成,这样你才能将地址映射到具体的函数和行号。
    • 串口输出的调用栈信息可能需要进行后处理才能方便查看,例如转换为可读的函数名和行号。
    • 如果程序崩溃时无法直接输出完整的调用栈,可以考虑在关键位置插入日志输出,以便在崩溃前获取尽可能多的信息。

综上所述,通过捕获异常、输出调用栈信息,并结合Keil等工具的调试功能,你可以有效地定位和解决STM32程序中的崩溃问题。

在STM32微控制器上,捕获异常并输出调用栈信息是一个相对复杂的任务,因为这涉及到操作系统的异常处理机制和调试信息的使用。由于STM32通常运行在裸机环境中,没有操作系统的支持,因此需要手动实现异常处理和调用栈跟踪。

以下是一个简单的示例,展示如何在STM32上捕获异常并尝试输出调用栈信息。请注意,这个示例假设你使用的是STM32F4系列,并且使用HAL库进行开发。这个示例可能需要根据具体的硬件和软件环境进行调整。

1. 配置异常向量表

首先,你需要配置异常向量表,将有一个专门的函数来处理未定义指令或系统错误等异常情况。

在你的启动代码或者向量表文件中,确保有定义一个默认的异常处理函数。

例如,在startup_stm32f4xx.s文件中,找到默认的异常向量,如Default_Handler,并确保它被定义。

2. 实现异常处理函数

接下来,实现一个异常处理函数,当发生异常时,该函数会被调用。

void Default_Handler(void) {// 进入异常处理模式__disable_irq();// 输出错误信息printf("Exception occurred!\r\n");// 尝试获取调用栈信息uint32_t* stack_pointer = (uint32_t*)__get_PSP();for (int i = 0; i < 10; i++) {printf("Stack %d: 0x%08X\r\n", i, stack_pointer[i]);}// 无限循环或重启while (1);
}

在这个函数中,我们禁用了中断,输出错误信息,并尝试读取当前的程序堆栈指针(PSP),然后输出堆栈中的内容。这里假设堆栈是满递减堆栈,并且每个栈帧是32位。

3. 配置串口输出

确保串口已经正确配置并初始化,以便能够输出调试信息。

#include "stm32f4xx_hal.h"UART_HandleTypeDef huart2;void MX_USART2_UART_Init(void) {huart2.Instance = USART2;huart2.Init.BaudRate = 115200;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart2.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart2) != HAL_OK) {// 初始化错误处理Error_Handler();}
}void Error_Handler(void) {while(1);
}int __io_putchar(int ch) {HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 0xFFFF);return ch;
}

4. 在主函数中调用串口初始化

确保在主函数中调用了串口初始化函数。

int main(void) {HAL_Init();MX_USART2_UART_Init();// 主程序代码while (1) {// 主循环}
}

5. 生成调试信息

在Keil中,确保项目设置中启用了调试信息,例如DWARF格式。这样,你可以将地址映射到函数名和源代码行号。

6. 分析调用栈信息

当程序崩溃并输出调用栈信息时,你可以记录下这些地址,然后在Keil中使用“Address to line”功能,将这些地址转换为具体的函数和行号。

例如,在Keil的命令行中,输入:

addr2line -e <your_project.axf> 0xAddress

这将输出对应的文件名和行号。

注意事项

  • 这个方法仅能输出堆栈中的地址,需要手动映射到函数和行号。
  • 堆栈帧的解析取决于编译器的设置和调用约定,可能需要根据实际情况调整。
  • 在裸机环境中,没有标准的调用栈跟踪机制,因此这只是一个基本的实现,可能不适用于所有情况。

通过以上步骤,你可以在STM32程序崩溃时,通过串口输出调用栈信息,并结合Keil工具进行分析,从而定位问题所在。

在Keil中,当你已经获得了调用栈上各个函数的地址后,可以通过以下步骤利用**符号表(Symbol Table)地图文件(Map File)**来将地址解析为具体的函数名和行号。以下是详细的操作步骤:


1. 生成地图文件(Map File)

在Keil中生成地图文件是第一步。地图文件包含了程序中所有符号(函数、变量、地址等)的详细信息,包括它们的地址、大小、所属模块等。

步骤:
  1. 打开Keil项目。
  2. 点击菜单栏的 Project -> Options for Target
  3. 在弹出的窗口中,选择 Linker 标签页。
  4. 勾选 Create Map File,并选择生成地图文件的格式(通常选择 Plain 或 Extended)。
  5. 点击 OK 保存设置。
  6. 重新编译项目(Build)。

编译完成后,地图文件会生成在项目的输出目录下,通常命名为 项目名.map


2. 使用地图文件解析地址

步骤:
  1. 打开生成的 .map 文件。

  2. 在地图文件中,查找 Image Symbol Table 部分。这里列出了程序中所有符号的地址和名称。

    • 例如:
      Image Symbol Table
      Address        Name
      0x08000340     main
      0x08000500     HAL_Init
      0x08000600     SystemClock_Config
      
  3. 根据你从调用栈中获取的地址,在 Image Symbol Table 中查找对应的函数名。

    • 例如,调用栈地址是 0x08000340,在地图文件中找到对应的名称为 main
  4. 如果需要进一步定位到具体的源代码行号,可以结合调试信息。


3. 使用调试信息解析行号

如果你在编译时启用了调试信息(如DWARF格式),Keil可以直接解析地址到具体的函数和行号。

步骤:
  1. 在Keil中打开调试模式(Debug)。

  2. 在调试窗口中,点击 View -> Command Window

  3. 在命令窗口中输入以下命令,将地址解析为函数名和行号:

    ADR2LINE <地址>
    
    • 例如,如果地址是 0x08000340,输入命令:
      ADR2LINE 0x08000340
      
    • Keil会返回类似以下的结果:
      main (main.c: 10)
      
      这表示地址 0x08000340 对应的是 main 函数,位于 main.c 文件的第 10 行。
  4. 如果你没有在调试模式下,可以使用 addr2line 工具。


4. 使用 addr2line 工具解析地址

如果你在命令行环境下,可以使用 addr2line 工具来解析地址。

步骤:
  1. 确保你有编译生成的 .axf 文件(包含调试信息)。
  2. 在命令行中输入以下命令:
    addr2line -e <你的.axf文件> <地址>
    
    • 例如:
      addr2line -e project.axf 0x08000340
      
    • 输出结果类似:
      main.c:10
      
      这表示地址 0x08000340 对应的是 main.c 文件的第 10 行。

5. 结合符号表(Symbol Table)解析地址

符号表是地图文件的一部分,通常在 .map 文件的 Image Symbol Table 部分。它列出了程序中所有符号的地址、名称和大小。

步骤:
  1. 在 .map 文件中,查找 Image Symbol Table 部分。
  2. 根据调用栈中的地址,在符号表中查找对应的函数名。
  3. 如果需要进一步定位行号,可以结合调试信息和 ADR2LINE 命令或 addr2line 工具。

总结

  • 获取地图文件:确保生成 .map 文件,用于查找符号地址和名称。
  • 解析函数名:通过地图文件的 Image Symbol Table 部分,查找调用栈地址对应的函数名。
  • 解析行号
    • 在Keil调试模式下,使用 ADR2LINE 命令。
    • 在命令行环境下,使用 addr2line 工具。
  • 符号表:符号表是 .map 文件的一部分,用于快速定位函数名。

通过以上方法,你可以将调用栈中的地址解析为具体的函数名和行号,从而快速定位程序崩溃的原因。


http://www.ppmy.cn/embedded/155008.html

相关文章

【网络】DNS解析流程

DNS全称叫做域名系统。 DNS域名主要是通过 . 来进行分割层级的&#xff0c;越往后层级级别越大&#xff08;符合外国人起的名称&#xff09; 我们访问的url如&#xff1a;www.baidu.com其实在最后还有一个 . ->www.baidu.com. 最后一个点代表根域名 . 根域 在最顶层&…

html中鼠标位置信息

pageX&#xff1a;鼠标距离页面的最左边的距离&#xff0c;包括滚动条的长度。clientX&#xff1a;鼠标距离浏览器视口的左距离&#xff0c;不包括滚动条。offsetX&#xff1a;鼠标到事件源左边的距离。movementX&#xff1a;鼠标这次触发的事件的位置相对于上一次触发事件的位…

Qt——QTableWidget 限制单元格输入范围的方法(正则表达式输入校验法、自定义代理类MyItemDelegrate)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》

专业130+总分410+西安交通大学815/869原909信号与系统考研电子信息与通信工程。真题,大纲,参考书。

read-normal-img 考研成功上岸西安交通大学&#xff0c;总分410&#xff0c;专业课815/909-现在的869信号与系统&#xff08;含DSP&#xff09;130&#xff0c;总结一下自己的复习经历&#xff0c;希望给大家有些帮助。 专业课&#xff1a;815/869原909信号与系统和dsp 教材&…

设计模式(4)行为模式

行为模式 1. Chain of Responsibility Pattern&#xff08;责任链模式&#xff09;2.Command Pattern&#xff08;命令模式&#xff09;3.Interpreter Pattern&#xff08;解释器模式&#xff09;▲4.Iterator&#xff08;迭代器模式&#xff09;5.Mediator&#xff08;中介者模…

MySQL系列之数据授权(安全)

导览 前言Q&#xff1a;如何对MySQL数据库进行授权管理一、MySQL的“特权” 1. 权限级别2. 权限清单 二、授权操作 1. 查看权限2. 分配权限3. 回收权限 结语精彩回放 前言 看过博主上一篇的盆友&#xff0c;可以Get到一个知识点&#xff1a;数据授权&#xff08;eg&#xff…

Unity新版InputSystem短按与长按,改键的实现

目录 前言&#xff1a; 一、InputSystem简介 1.安装InputSystem包 2.创建配置文件 3.创建自定义的Actions 二、自定义输入类 三、改键 四、全代码 前言&#xff1a; 新版inputsystem是Unity推出的一种新的输入方式&#xff0c;它将设备与行为进行分离&#xff0c;通过…