当前位置: 首页 > news >正文

STM32中死机 Crash dump 打印出函数调用关系

在 STM32 中打印 crash dump 的函数调用关系需要结合硬件异常处理和调试技术。以下是详细方法:

1. 硬件异常处理基础

异常处理函数

// 在启动文件中重定义默认异常处理
void HardFault_Handler(void)
{__asm volatile("tst lr, #4 \n""ite eq \n""mrseq r0, msp \n""mrsne r0, psp \n""b print_call_stack \n");
}

2. 调用栈回溯实现

基于帧指针的回溯

#include <stdint.h>// 栈帧结构
typedef struct stack_frame {struct stack_frame* fp;  // 帧指针uint32_t lr;             // 返回地址
} stack_frame_t;// 打印调用栈
void print_call_stack(uint32_t* sp)
{printf("=== Call Stack ===\n");stack_frame_t* current_frame = (stack_frame_t*)sp;uint32_t depth = 0;// 检查栈帧是否在有效范围内uint32_t stack_base = (uint32_t)&__initial_sp;  // 从启动文件获取uint32_t stack_limit = stack_base - __STACK_SIZE;while (current_frame != NULL && (uint32_t)current_frame >= stack_limit && (uint32_t)current_frame < stack_base &&depth < 32) {uint32_t return_addr = current_frame->lr;// 调整返回地址(Thumb模式)if (return_addr & 1) {return_addr--;  // Thumb模式清除最低位}printf("#%d 0x%08lx\n", depth, return_addr);// 解析函数名(如果有符号表)const char* func_name = addr2func(return_addr);if (func_name) {printf("    %s\n", func_name);}current_frame = current_frame->fp;depth++;}printf("==================\n");
}

3. 完整的异常处理

增强的 HardFault 处理

void HardFault_Handler_Enhanced(void)
{// 保存寄存器状态uint32_t stacked_r0, stacked_r1, stacked_r2, stacked_r3;uint32_t stacked_r12, stacked_lr, stacked_pc, stacked_psr;__asm volatile("tst lr, #4 \n""ite eq \n""mrseq r0, msp \n""mrsne r0, psp \n""mov %0, r0 \n": "=r" (sp));// 从栈中提取寄存器值stacked_r0 = ((uint32_t*)sp)[0];stacked_r1 = ((uint32_t*)sp)[1];stacked_r2 = ((uint32_t*)sp)[2];stacked_r3 = ((uint32_t*)sp)[3];stacked_r12 = ((uint32_t*)sp)[4];stacked_lr = ((uint32_t*)sp)[5];stacked_pc = ((uint32_t*)sp)[6];stacked_psr = ((uint32_t*)sp)[7];// 打印异常信息printf("\n!!! HardFault !!!\n");printf("SP: 0x%08lX\n", sp);printf("PC: 0x%08lX\n", stacked_pc);printf("LR: 0x%08lX\n", stacked_lr);printf("PSR: 0x%08lX\n", stacked_psr);// 分析故障原因analyze_fault(stacked_psr);// 打印调用栈print_call_stack((uint32_t*)sp);// 停止执行while(1) {// 可以添加LED闪烁指示错误}
}

4. 符号表解析

简单的符号表实现

typedef struct {uint32_t address;const char* name;
} symbol_t;// 从map文件生成的符号表(需要预处理)
symbol_t symbol_table[] = {{0x08001234, "main"},{0x08001280, "function_a"},{0x08001300, "function_b"},{0, NULL}  // 结束标记
};const char* addr2func(uint32_t addr)
{for (int i = 0; symbol_table[i].name != NULL; i++) {if (addr >= symbol_table[i].address && addr < symbol_table[i+1].address) {return symbol_table[i].name;}}return "unknown";
}

5. 使用 GNU Binutils 解析

自动生成符号表脚本

#!/bin/bash
# generate_symbols.sh
arm-none-eabi-nm -n $1.elf | grep " T " | awk '{print "    {"$1", \""$3"\"},"}' > symbols.c

在 Makefile 中集成

symbols.c: $(TARGET).elf./generate_symbols.sh $(TARGET)$(TARGET).elf: symbols.c# 编译规则

6. 使用 SEGGER SystemView 或 RTT

通过 J-Link RTT 输出调用栈

#include "SEGGER_RTT.h"void print_call_stack_rtt(uint32_t* sp)
{SEGGER_RTT_printf(0, "=== Call Stack ===\n");stack_frame_t* current_frame = (stack_frame_t*)sp;uint32_t depth = 0;while (current_frame != NULL && depth < 16) {uint32_t return_addr = current_frame->lr & ~1UL;  // Thumb模式SEGGER_RTT_printf(0, "#%d 0x%08lX", depth, return_addr);const char* func_name = addr2func(return_addr);if (func_name) {SEGGER_RTT_printf(0, " %s\n", func_name);} else {SEGGER_RTT_printf(0, "\n");}current_frame = current_frame->fp;depth++;}
}

7. 故障原因分析

void analyze_fault(uint32_t psr)
{uint32_t cfsr = SCB->CFSR;  // Configurable Fault Status Registeruint32_t hfsr = SCB->HFSR;  // Hard Fault Status Registeruint32_t mmfar = SCB->MMFAR; // MemManage Fault Addressuint32_t bfar = SCB->BFAR;   // Bus Fault Addressprintf("CFSR: 0x%08lX\n", cfsr);if (cfsr & (1 << 0)) printf("  - IACCVIOL: Instruction access violation\n");if (cfsr & (1 << 1)) printf("  - DACCVIOL: Data access violation\n");if (cfsr & (1 << 3)) printf("  - MUNSTKERR: MemManage on exception entry\n");if (cfsr & (1 << 4)) printf("  - MSTKERR: MemManage on exception return\n");// ... 更多故障标志
}

8. 调试配置

在 IDE 中设置

// 在 main() 开始时启用
void enable_fault_handlers(void)
{// 启用所有故障异常SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |SCB_SHCSR_BUSFAULTENA_Msk |SCB_SHCSR_USGFAULTENA_Msk;
}

9. 实际应用示例

int main(void)
{// 初始化调试输出debug_uart_init();// 启用故障处理enable_fault_handlers();printf("System started\n");// 你的应用代码while(1) {application_code();}
}

注意事项

  1. 编译器优化: 确保帧指针不被优化掉(-fno-omit-frame-pointer

  2. 栈保护: 检查栈指针有效性,避免二次崩溃

  3. 内存布局: 了解你的内存映射,避免访问非法地址

  4. 实时性: 异常处理应尽量简洁,避免影响实时性要求高的应用

这种方法可以在 STM32 发生崩溃时提供详细的调用栈信息,大大简化调试过程。

http://www.dtcms.com/a/544997.html

相关文章:

  • STM32的GPIOx_IDR 与 GPIOx_ODR
  • Rust 借用检查器(Borrow Checker)的工作原理:编译期内存安全的守护者
  • 仓颉语言核心技术深度解析:面向全场景智能时代的现代编程语言
  • 漳州住房和城乡建设部网站简单的页面
  • 架构论文《论负载均衡的设计与应用》
  • Linux frameworks 音视频架构音频部分
  • 【AI论文】PICABench:我们在实现物理逼真图像编辑的道路上究竟走了多远?
  • 设计模式之抽象工厂模式:最复杂的工厂模式变种
  • 设计模式>原型模式大白话讲解:就像复印机,拿个原件一复印,就得到一模一样的新东西
  • 网站数据库大小石家庄发布最新消息
  • 本地运行Tomcat项目
  • 大模型如何变身金融风控专家
  • 台州网站建设维护网页设计与制作教程杨选辉
  • 动力网站移动端模板网站建设价格
  • Windows 10终止服务支持:企业IT安全迎来重大考验
  • Mac os安装Easyconnect卡在正在验证软件包
  • 手机网站免费模板下载门户网站 销售
  • 学习和掌握RabbitMQ及其与springboot的整合实践(篇二)
  • Flink、Storm、Spark 区别
  • 当 AI Agent 遇上工作流编排:微软 Agent Framework 的 Workflow 深度解析
  • 5步构建多模式内容策略:统一品牌信息,最大化内容影响力
  • STP 转换为 3DXML 的技术指南及迪威模型网在线转换推荐
  • 如何建设视频网站好的网站设计题目
  • 深入理解 Vite 开发服务器的 Local 与 Network 地址
  • 免费建立网站的网站吗免费软件视频
  • 和利时 PLC 配网
  • 时间序列数据预测:14种机器学习与深度学习模型
  • 手机网站编程语言finecms
  • 第六部分:VTK进阶(第178章 网格质量评估vtkMeshQuality)
  • 多模态+CLIP | 视觉语言交互的终极形态?CLIP融合AIGC与持续学习,重塑多模态AI边界