STM32 F4xx Fault 异常错误定位指南
STM32 F407 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为。Fault异常能够检测到以下几类非法行为:
- 总线 Fault: 在取址、数据读/写、取中断变量、进入/退出中断时寄存器堆栈操作(入栈/出栈)时检测到内存访问错误。
- 存储器管理 Fault: 检测到内存访问违反了内存保护单元(MPU, MemoryProtection Unit)定义的区域。
- 用法 Fault: 检测到未定义的指令异常,未对其的多重加载/存储内存访问。如果使能相应控制位,还可以检测出除数为零以及其他未对齐的内存访问。
- 硬 Fault: 如果上述的总线 Fault、存储器管理 Fault、用法 Fault 的处理程序不能被执行(例如禁能了总线 Fault、存储器管理Fault、用法Fault 的异常或者在这些异常处理程序中又出现了新的Fault)则触发硬Fault。
为了解释所述的 Fault 中断处理程序的原理,这里重述一下当系统产生异常时 MCU 的处理过程:
- 有一个压栈的过程,若产生异常时使用 PSP(进程栈指针),就压入到 PSP 中,若产生异常时使用MSP(主栈指针),就压入MSP 中。
- 会根据处理器的模式和使用的堆栈,设置 LR 的值(当然设置完的LR 的值再压栈)。
- 异常保存,硬件自动把 8 个寄存器的值压入堆栈(8 个寄存器依次为 xPSR、PC、LR、R12以及 R3~R0)。如果异常发生时,当前的代码正在使用PSP,则上面8 个寄存器压入PSP; 否则就压入MSP。
当系统产生异常时,我们需要两个关键寄存器值,一个是 PC ,一个是 LR (链接寄存器),通过 LR找到相应的堆栈,再通过堆栈找到触发异常的PC 值。将产生异常时压入栈的 PC 值取出,并与反汇编的代码对比就能得到哪条指令产生了异常。
这里解释一下关于 LR 寄存器的工作原理。如上所述,当 Cortex-M4 处理器接受了一个异常后,寄存器组中的一些寄存器值会被自动压入当前栈空间里,这其中就包括链接寄存器(LR )。这时的 LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN)。关于EXC_RETURN 的定义如下,其为 32 位数值,高 28 位置 1,第 0 位到第三位则提供了异常返回机制所需的信息,如下表所示。可见其中第 2 位标示着进入异常前使用的栈是 MSP还是PSP。在异常处理过程结束时,MCU 需要根据该值来分配 SP 的值。这也是本方法中用来判断所使用堆栈的原理,其实现方法可以从后面_init_hardfault_isr 中看到。
异常处理流程:
首先要定义异常处理函数,在M4和M3核中,这两个是一样的,可以直接在stm32_f4xx.s中定义:
.cpu cortex-m3
.thumb .global HardFault_Handler
.extern hard_fault_handler_c HardFault_Handler:
TST LR, #
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B hard_fault_handler_c
这里有几个命令要说明一下含义: TST 是Bit级别的与操作。ITE 是 MRSEQ和MRSNE都是两个命令的合体,分别可以拆开成:MRS,EQ和MRS,NE,分别的意思是如果两者相等,则把MSP的值赋值到R0,如果R0和PSP不等,则把PSP赋植到R0.ITE读为 if-then-else
关于HardFault_Handler 这个函数,一般在stm32_f4xx.s的中断向量表中,我的系统中的代码如下所示:
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
接下来就是整个流程的代码实现:
/ hard fault handler in C,
// with stack frame location as input parameter
void hard_fault_handler_c (unsigned int * hardfault_args)
{
unsigned int stacked_r0;
unsigned int stacked_r1;
unsigned int stacked_r2;
unsigned int stacked_r3;
unsigned int stacked_r12;
unsigned int stacked_lr;
unsigned int stacked_pc;
unsigned int stacked_psr; stacked_r0 = ((unsigned long) hardfault_args[]);
stacked_r1 = ((unsigned long) hardfault_args[]);
stacked_r2 = ((unsigned long) hardfault_args[]);
stacked_r3 = ((unsigned long) hardfault_args[]); stacked_r12 = ((unsigned long) hardfault_args[]);
stacked_lr = ((unsigned long) hardfault_args[]);
stacked_pc = ((unsigned long) hardfault_args[]);
stacked_psr = ((unsigned long) hardfault_args[]); printf ("\n\n[Hard fault handler - all numbers in hex]\n");
printf (“R0 = %x\n”, stacked_r0);
printf (“R1 = %x\n”, stacked_r1);
printf (“R2 = %x\n”, stacked_r2);
printf (“R3 = %x\n”, stacked_r3);
printf (“R12 = %x\n”, stacked_r12);
printf (“LR [R14] = %x subroutine call return address\n”, stacked_lr);
printf (“PC [R15] = %x program counter\n”, stacked_pc);
printf (“PSR = %x\n”, stacked_psr);
printf (“BFAR = %x\n”, (*((volatile unsigned long )(0xE000ED38))));
printf (“CFSR = %x\n”, (((volatile unsigned long )(0xE000ED28))));
printf (“HFSR = %x\n”, (((volatile unsigned long )(0xE000ED2C))));
printf (“DFSR = %x\n”, (((volatile unsigned long )(0xE000ED30))));
printf (“AFSR = %x\n”, (((volatile unsigned long *)(0xE000ED3C))));
printf (“SCB_SHCSR = %x\n”, SCB->SHCSR); while ();
} /* hard fault interrupt handler */
void _int_hardfault_isr( )
{
__asm(“TST LR, #”);
__asm(“ITE EQ”);
__asm(“MRSEQ R0,MSP”);
__asm(“MRSNE R0,PSP”);
__asm(“B hard_fault_handler_c”);
} void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
DEBUG_ERR(" hard fault handler ");
_int_hardfault_isr();
while ()
{
}
}
上面的这些代码,一般的工程师就可以看懂了,就不多做介绍了,假如你有啥这方面的问题,欢迎交流和沟通,反正是我的板子可以正常使用这些代码了。
参考文档:
1 https://community.arm.com/cn/b/blog/posts/3-thumb-2
STM32 F4xx Fault 异常错误定位指南的更多相关文章
- segment fault异常及常见定位手段
问题背景 最近boot中遇到个用户态程序的segment fault异常,除了一句"Segment fault"打印外无其他任何打印.该问题复现概率较低,定位起来比较棘手.我们的b ...
- Web安全开发指南--异常错误处理与日志审计
1.异常错误处理与日志审计 5.1.日志审计系统安全规则 1 日志系统能够记录特定事件的执行结果(比如 成功或失败). 确保日志系统包含如下重要日志信息: 1. 日志发生的时间: 2. 事件的严重 ...
- Cortex-M3和Cortex-M4 Fault异常应用之二 ----- Fault处理函数的实现
在项目处于调试期间,Fault处理程序可能只是一个断点指令,调试器遇到这个指令后停止程序的运行.默认情况下,由于非硬Fault被禁能,所有发生的非Fault都会上访成硬Fault,因此只要在硬Faul ...
- Cortex-M3和Cortex-M4 Fault异常应用之一 ----- 基础知识
1. 摘要 Cortex-M内核实现了一个高效异常处理模块,可以捕获非法内存访问和数个程序错误条件.本应用笔记从程序员角度描述Cortex-M Fault异常,并且讲述在软件开发周期中的Fault用法 ...
- postgresql异常快速定位
今天下午在使用.NET链接postgresql的时候报了“3D000”的错误,经过测试得知原来是web.config中的数据库配置问题. 在这里有个小情况需要注意,postgresql是不允许创建相同 ...
- 总结Selenium自动化测试方法(六)常见的异常错误处理
六.常见的异常错误处理 NoSuchElementException: Message: Unable to locate element: {"method":"xpa ...
- FindBugs错误修改指南 【转】
FindBugs错误修改指南 1. EC_UNRELATED_TYPES Bug: Call to equals() comparing different types Pattern id: EC_ ...
- c# 异常精准定位
在日常项目开发中,异常抛出和捕获是再平常不过的事情.通过try-catch我们可以方便的捕获异常,同时通过查看异常堆栈我们能发现抛出异常代码的位置. 例如下面这段代码: using System; u ...
- 出现java.lang.NoClassDefFoundError: com/google/common/base/Charsets异常错误
使用selenium,出现java.lang.NoClassDefFoundError: com/google/common/base/Charsets异常错误 原因:selenium-server- ...
随机推荐
- java之程序流程控制
顺序结构:代码由上至下依次执行: 分支结构: if () { } else{ } if () { } else if () { } else { } switch(常量){ case 常量: 语句; ...
- emojy表情的小问题
报错内容:在emojy表情入库或者更新库的时候,会报这个错:java.sql.SQLException:Incorrect string value: '\xF0\x9F\x99\x8F\..' fo ...
- Python中的常见特殊方法—— repr方法
在Python中有些方法名.属性名的前后都添加了双下划线,这种方法.属性通常都属于Python的特殊方法和特殊属性,开发者可以通过重写这些方法或者直接调用这些方法来实现特殊的功能.其实前面见过的构造方 ...
- pecl安装和卸载示例
比如安装MongoDB扩展 命令行下输入: [#] /usr/local/php/bin/pecl install mongodb ......嗒吧嗒吧一大堆输出后 Build process com ...
- SSH框架之Hibernate第三篇
1.1 多表关系分析和创建. 1.1.1 表关系分析和创建 表数据和表数据之间可以存在的关系? 一对多的关系 客户和联系人 建立关系原则: 在多的一方创建一个字段,这个字段作为外键指向一的一方的主键 ...
- springcloud 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
1.代码生成器: [正反双向](单表.主表.明细表.树形表,快速开发利器)freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本.处理类.service等完整模块2. ...
- IDEA激活码
MNQ043JMTU-eyJsaWNlbnNlSWQiOiJNTlEwNDNKTVRVIiwibGljZW5zZWVOYW1lIjoiR1VPIEJJTiIsImFzc2lnbmVlTmFtZSI6I ...
- HTTP中的Accept-Encoding、Content-Encoding、Transfer-Encoding、Content-Type
Accept-Encoding和Content-Encoding Accept-Encoding和Content-Encoding是HTTP中用来对采用何种压缩格式传输正文进行协定的一对header. ...
- Linux shell--基础指令
Linux shell--基础指令 浏览Linux文件系统 Linux中最基础也是最必要的一条指令 cd destination cd命令可接受单个参数destination,用以指定想切换到的目录名 ...
- angularjs用回车键动态添加数据,同时渲染到页面
<script src="../../angular-1.5.5/angular.min.js"></script> <script> var ...