嵌入式开发中断全解(2)Hard Fault的诊断

2023-05-16

承接上次的文章,讲几个大家应该都看过下面的几个中断,有的是在启动文件中或者是.c文件中。
在这里插入图片描述
注意:上述是ST公司的Stm32芯片

在这里插入图片描述
这里的GD32芯片是国产芯片,和stm32类似的操作,代码可以兼容。

1、void NMI_Handler(void)

不可屏蔽中断。主要是两方面触发,一个是外设触发,一个软件设置触发。首先要意识到,所有中断在某些情况下都是可屏蔽的。例如,如果中断控制器完全关闭,则不会向 CPU 传递任何中断。术语不可屏蔽中断实际上涵盖了一类中断,即使“正常”中断被屏蔽,仍可以将其传递给 CPU。NMI 仍然可以被屏蔽,但是通过标准内核代码难以访问的单独控制状态。在某些情况下屏蔽所有中断的能力也存在于其他具有悠久 NMI 历史的架构上。

操作系统软件需要仅在特定情况下被屏蔽的单独中断类别有两个主要原因。第一个原因很简单,中断可能会意外地被禁用,从而使系统处于无响应状态。乍一看,似乎可以轻松编写软件以在禁用中断后始终启用中断,但事实并非如此。现代操作系统内核通常跨越数百万行可以直接操作中断标志的代码。中断处理例程可能会导致同步异常,从而导致非线性代码路径,并可能导致禁用中断的死锁。还要考虑以完全内核权限运行并能够直接操纵 CPU 中断标志​​的第三方驱动程序。应该清楚的是,简单的代码检查或静态分析不足以防止中断被禁用。在一些场景中,能够快速传递中断至关重要,例如调试、跨 PE 同步和热补丁。原文克里斯托弗·达尔。
NMI属于内部中断,并且默认是使能的。NMI对外有一个引脚与之相关联,该引脚的默认功能就是NMI。我们可以知道,当我们使用该引脚用作其它功能的时候,如果把NMI引脚在电路上接地,程序在启动的时候就会触发NMI中断,从而进入到NMI_Handler函数中去,那程序一直卡在这了,后期再说说怎么屏蔽。(主要大家要看看芯片用户手册)。void NMI_Handler(void)函数原型如下。

/*!
    \brief      this function handles NMI exception 
    \param[in]  none
    \param[out] none
    \retval     none
*/
void NMI_Handler(void)
{
}

2、void SVC_Handler(void)
SVC(Supervisor Call)指令用于产生一个SVC异常。它是用户模式代码中的主进程,用于创造对特权操作系统代码的调用。SVC是用于呼叫操作系统所提供API的正道。用户程序只需知道传递给操作系统的参数,而不必知道各API函数的地址。

/*!
    \brief      this function handles SVC exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SVC_Handler(void)
{
}

3、void DebugMon_Handler(void)

调试监视器(断点, 数据观察点, 或外部调试请求),大部分debug的时候就是调试的时候遇到.

/*!
    \brief      this function handles DebugMon exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void DebugMon_Handler(void)
{
}

4、void PendSV_Handler(void)
PendSV是为系统级服务提供的中断驱动。在一个操作系统环境中,当没有其他异常正在执行时,可以使用PendSV来进行上下文的切换。
在进入PendSV处理函数时:
(1)xPSR、PC、LR、R12、R0~R3已经在处理栈中被保存。
(2)处理模式切换到线程模式。
(3)栈是主堆栈。
由于PendSV在系统中被设置为最低优先级,因此只有当没有其他异常或者中断在执行时才会被执行。

/*!
    \brief      this function handles PendSV exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void PendSV_Handler(void)
{
}

5、void SysTick_Handler(void)
看到这个大家肯定会想到滴答定时器,就是系统定时器 , 以后再分享个系统定时器的内容,在参考手册中, 有这样说法, 当systic为计数为0 则成1外部参考时钟源 21mhz bit[2]为0 内核时钟168MHZ bit[2] 为1 ,使能定时器中断为1,当计数产生为0产生中断,或者将bit[1]0使能位(0关闭,1打开, 系统定时器中断 一般用在操作系统的延时

/*!
    \brief      this function handles SysTick exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SysTick_Handler(void)
{
    delay_decrement();
}

以下四种常见的错误异常。
在这里插入图片描述
6、void HardFault_Handler(void)
以下异常处理被关闭,而又发生了异常,则触发
• 执行异常处理时,发生了异常,则触发
• 复位时默认使能
传说中的段错误。主要原因两方面:(1)中断问题,例如中断嵌套太多,中断优先级问题,中断标志位没清楚等;(2)堆栈不够或者溢出,例如:当你需要大数组去存取数据,数组就会设置的太大,就会跑飞进入这个中断,这时候你可以把栈堆分配多一点。如何分配堆栈空间?可以先去了解内存布局。

/*!
    \brief      this function handles HardFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/

void HardFault_Handler(void)
{
    /* if Hard Fault exception occurs, go to infinite loop */
    while(1){
    }
}

硬错误异常是应对系统严重错误而设计的,其优先级为-1,仅次于NMI异常。触发硬错误异常的原因有:
• 调试出错
• 后面三个MemManage_Handler\BusFault_Handler\UsageFault_Handler常规错误异常不能及时响应
• 为响应中断,取中断向量时发生错误
• 仍可通过PRIMASK关闭
注意:

当总线错误异常、存储器管理错误异常、用法错误异常无法及时得到响应时或未被使能时,系统将产生硬错异常。当硬错误异常服务例程又引发新的硬错误异常时,系统将进入死锁状态,只能由复位使其退出.

7、void MemManage_Handler(void)
• 违反MPU设定的存储器访问规则
• 复位时默认未使能
在内核的MCU上写程序时,稍不留神,就可能出现内存溢出的情况。即,数组、指针变量溢出,导致MCU访问内部禁止访问的地址上。这样MCU就会跳转到 错误或者硬件错误中断上去,造成设备死机。也可以在这两个中断中加入强制CPU复位重启,但不能完全解决问题,其实发生存储管理错误中断(MemManage_Handler)后,都发现程序是跳到硬件错误中断(HardFault_Handler)里,说白了又是内存布局问题。

/*!
    \brief      this function handles MemManage exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void MemManage_Handler(void)
{
    /* if Memory Manage exception occurs, go to infinite loop */
    while(1){
    }
}

存储器管理错误异常是由于违规访问存储器空间或由某些非法访问引发的;通常包括以下类型:
• 访问存储区域时违反了MPU的设置规则
• 越权访问,访问了没有权限访问的地址;
• 访问了没有存储器的地址
• 试图从不可执行区域(XN)执行代码
• 对只读区域进行写操作

使能控制
• SCB->SHCSR. MEMFAULTENA @0xE000ED24

存储器管理错误异常,通常是与MPU相关联的。其诱因往往是违反了MPU设置的访问规则。某些非法访问,如在不可执行的存储器区域取指,也会会引发该异常,既使系统中没有MPU 或MPU被关掉了

发生Memory Management Fault时,可以查看存储器管理错误状态寄存器了解异常的类型
• SCB->CFSR. Memory Manage Fault(MFSR) @0xE000ED28

在这里插入图片描述

8、void BusFault_Handler(void)
• 总线错误(Bus Fault)
• 取指令、数据读写、堆栈操作
• 复位时默认未使能

/*!
    \brief      this function handles BusFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/

void BusFault_Handler(void)
{
    /* if Bus Fault exception occurs, go to infinite loop */
    while(1){
    }
}

当内核通过AHB接口传送数据时收到了一个错误应答信号,则认总线传输错误,进而发起总线错误异常请求;通常异常来自:
• 取指令时发生错误,通常称为“预取指流产”
• 数据读、写时发生错误,也叫“数据流产”
• 响应中断时,出、入栈发生错误
使能控制
• SCB->SHCSR. BUSFAULTENA @0xE000ED24
AHB总线收到”错误”应答,通常有以下诱因:

企图访问无效的存储器区域,常见于企图访问的地址没有存储器
设备未准备就绪,不能进行总线访问
企图发起的传输宽度不被目标设备所支持
在用户权限下程序企图访问特权模式下的设备

发生Bus Fault时,可以查看总线错误状态寄存器了解异常
的大致类型,供异常处理程序分析
• SCB->CFSR.Bus Fault(BFSR) @0xE000ED2

在这里插入图片描述

9、void UsageFault_Handler(void)
• 用法错误(Usage Fault)
• 执行未定义指令、非对齐操作、除零
• 复位时默认未使能

/*!
    \brief      this function handles UsageFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void UsageFault_Handler(void)
{
    /* if Usage Fault exception occurs, go to infinite loop */
    while(1){
    }
}

在这里插入图片描述

发生用法错误时,可通过查看寄存器了解出错的原因
• SCB->CFSR. Usage Fault(UFSR) @0xE000ED2A

在这里插入图片描述

解决方法:
通常一直循环进入某个中断,代码底层没错的前提就打断点,看看是在哪出错,检查内存使用,有没有访问越界,堆栈够不够。或者把可能出错的程序全部屏蔽。看是否再出现内存溢出,如果不再出现了,那就一点点打开屏蔽的代码。这样就可以轻松定位出错的位置上。然后再去分析为什会出现内存溢出。以下就是我这个小白,老是出错的问题:

第一,没有给指针变量赋初始值就开始使用。
第二,在给数组变量copy时,长度超限。如果这个数组是局部变量就会改变了栈中的其它变量和压栈的cpu寄存器,如果数组是,物理上相邻的变量就会被改变。
第三,中断与应用程序对同一指针变量的修改不同步。

(c语言基础一定要牢固啊!!!!以后再分享些c语言学习)

错误异常发生时:
• 上下文(Stack Frame)被入栈保存
• R0~R3, R12, LR,PC,xPSR
• 上下文保存在哪个堆栈中?由此时的LR决定
• LR = EXC_RETURN
• LR.2 = 1,保存在Main Stack,由MSP指向
• LR.2 = 0,保存在Process Stack,由PSP指向
在这里插入图片描述
记录发生异常时的现场数据 :
采集现场有助于分析出错原因可以通过串口来看,如下面这段代码.

HardFault_Handler: 
TST LR, #4 
ITE EQ 
MRSEQ R0, MSP 
MRSNE R0, PSP 
B hard_fault_handler_c
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[0]); 
stacked_r1 = ((unsigned long) hardfault_args[1]); 
stacked_r2 = ((unsigned long) hardfault_args[2]); 
stacked_r3 = ((unsigned long) hardfault_args[3]); 
stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_r12 = ((unsigned long) hardfault_args[4]); 
stacked_lr = ((unsigned long) hardfault_args[5]); 
stacked_pc = ((unsigned long) hardfault_args[6]); 
stacked_psr = ((unsigned long) hardfault_args[7]);
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 (1); 
}

俗话说:复习是进步他爹!!总结是进步他舅!!
单片机中断分为内部中断和外部中断两大类,外部中断由单片机外部设备产生,中断产生后通过单片机的外部管脚传递给单片机,传递这个中断信号最简单的方法就是 规定单片机的管脚在什么状态下有外部中断产生,这样单片机通常是有一个或多个IO口,当在输入状态时可以用来检测外部中断信号。嵌入式开发中断全解(1)
文章很长,能看到这也希望都能学到些东西,努力成为"栈溢出"工程师 ! ! ! 加油 !

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式开发中断全解(2)Hard Fault的诊断 的相关文章

随机推荐

  • 基于F4/F7/H7飞控硬件和px4飞控固件的廉价自主无人机系统(1)-飞控

    前言 穿越机F4 F7 H7飞控是一系列采用stm32系列F4xx和F7xx处理器的飞控的统称 xff0c 是目前穿越机爱好者非常喜欢使用的飞控硬件 xff0c 其价格也非常便宜180 xff5e 410 而px4则是一款常见的开源飞控固件
  • 基于F4/F7/H7飞控硬件和px4飞控固件的廉价自主无人机系统(2)- 外设和固件修改

    前言 为了实现自主飞行 xff0c 无人机需要许多的感知设备 xff0c 而F4 F7 H7所搭载的传感器都很有限 xff0c 因此需要额外的外设 而外设也分为可以直接与飞控连接的以及无法与飞控连接的 本文主要解决如何在F4 pro V2飞
  • 卡尔曼滤波的推导过程详解

    在学习卡尔曼滤波的时候看了很多博客讲这方面的知识 xff0c 感觉都讲得表面的东西 xff0c 无法了解它五个公式真正代表的过程 xff0c 这篇博客我想以我的理解讲讲卡尔曼滤波 首先我先写出卡尔曼滤波的具体过程 xff0c 首先针对如下状
  • 时变系统多传感器信息融合kalman滤波器

    前面的博客中讲得都是针对一种传感器的测量值有状态空间模型对系统状态值进行估计 xff0c 本次博客讨论多传感器下对系统状态值估计的方法 xff0c 即多传感器的数据融合 多传感器的状态融合又分成两种 xff1a 1 集中式kalman滤波
  • px4Flow--替换超声波模块

    几个必看的官方资料 xff1a px4开发者指南 xff1a http pixhawk org dev px4flow px4flow介绍 xff1a http pixhawk org start id 61 modules px4flow
  • 485是什么以及特点、协议、使用方法

    1 485是什么 xff1f 485 xff0c 通信接口的远程称重数据采集方法 xff0c 在要求通信距离为几十米到上千米时 xff0c 广泛采用RS 485串行总线标准 RS 485采用平衡发送和差分接收 xff0c 因此具有抑制共模干
  • 路径规划(一) —— 环境描述(Grid Map & Feature Map) & 全局路径规划(最优路径规划(Dijkstra&A*star) & 概率路径规划(PRM&RRT))

    路径规划问题就是把机器人的工作环境量化的描述出来 xff0c 让机器人知道哪里可以走 xff0c 哪里不可以走 xff0c 从而规划出一条可行的轨迹 xff0c 并且对于轨迹本身进行优化 环境的描述 对于环境的描述 xff0c 我们一般使用
  • 模型参数量(Params)/模型大小 & Pytorch统计模型参数量

    模型参数量大小可以从保存的checkpoint文件直观看出来 total params 61 sum p numel for p in model parameters total params 43 61 sum p numel for
  • C语言-函数实现

    函数的定义 1 逻辑上 xff1a 能够完成特定功能的独立的代码块 2 物理上 xff1a 能够接收数据 也可以不接收数据 xff0c 能够对接收的数据进行处理 也可以不对数据进行处理 xff0c 能够将数据处理的结果返 也可以没有返回值
  • 微策略MicroStrategy校园招聘2013.10.21面试题

    全程英语口语回答 xff1a 一面 计算机基础 1 判断一个字符串是否回文 2 如何快速找出一个有序数组中a i 61 i的那个元素 分析 xff1a 对于任意的j和i xff0c 如果j gt i则A j gt 61 A i 假设所求的解
  • C语言的几个标准定义(__FILE__,__LINE__,__DATE__)

    stm32库的断言 assert 官方提供了包含 FILE LINE 的代码 标准外设库位于stm32fxxx conf h文件 xff1a define USE FULL ASSERT 1U define assert param exp
  • Linux/C/C++ 不可错过的好书

    来源 xff1a 公众号 编程珠玑 作者 xff1a 守望先生 ID xff1a shouwangxiansheng 访问 xff1a https www yanbinghu com 查看更多好文 前言 经常有读者让我推荐书籍 xff0c
  • 这些算法可视化网站助你轻松学算法

    来源 xff1a 公众号 编程珠玑 作者 xff1a 守望先生 前言 无疑 xff0c 数据结构与算法学习最大的难点之一就是如何在脑中形象化其抽象的逻辑步骤 而图像在很多时候能够大大帮助我们理解其对应的抽象化的东西 xff0c 而如果这个图
  • 关于学习生活记录的感想总结

    今年从4月开始决定写博客记录自己学习过程 xff0c 但是直到今天才不过写了1篇而已 xff0c 中间没有坚持 xff0c 这一年感觉过得很糟糕 xff0c 从很久开始我就明白自己有很大缺陷 xff0c 平时难以克服懒惰心理 xff0c 做
  • ESP32踩坑记1——未定义(undefined reference to)

    昨日心血来潮 xff0c 又开始玩起了我的小板板 xff0c 本想给他加个屏幕 xff0c 显示点什么东西 xff0c 用VScode编译的时候居然提示我没有找到文件 这怎么会呢 xff0c 我明明添加了c文件和h文件 xff0c 后来折腾
  • Docker容器进入的4种方式

    Docker容器进入的4种方式 在使用Docker创建了容器之后 xff0c 大家比较关心的就是如何进入该容器了 xff0c 其实进入Docker容器有好几多种方式 xff0c 这里我们就讲一下常用的几种进入Docker容器的方法 进入Do
  • 清晰解读C语言中的比特序、字节序、位域、大小端

    一 比特序 位序 比特序表示一个字节中8个比特位 xff08 bit xff09 之间的顺序问题 分为LSB 0 位序和MSB 0 位序 LSB 最低有效位 MSB 最高有效位 LSB least significant bit 0位序 x
  • 【转载】四种常见系统架构介绍

    转自于 四种常见系统架构介绍 宇大 大 博客园 软件架构 xff08 software architecture xff09 就是软件的基本结构 合适的架构是软件成功的最重要因素之一 大型软件公司通常有专门的架构师职位 xff08 arch
  • 在Ubuntu 20.04上通过Docker使用ROS Kinetic (含RViz, Gazebo等图形界面及加速方法)

    背景 传统上 xff0c ROS 1的每一代系统都基于某一个版本的Ubuntu系统进行开发 xff0c 而其依赖库规模庞大 xff0c 再加上Python 2 3的转换问题 xff0c 导致在不同版本的Linux系统上原生编译安装非对应版本
  • 嵌入式开发中断全解(2)Hard Fault的诊断

    承接上次的文章 xff0c 讲几个大家应该都看过下面的几个中断 xff0c 有的是在启动文件中或者是 c文件中 注意 xff1a 上述是ST公司的Stm32芯片 这里的GD32芯片是国产芯片 xff0c 和stm32类似的操作 xff0c