您的位置:首页 > 文旅 > 旅游 > 绿色国网app_网站设计是干什么的_中国50强企业管理培训机构_超级外链

绿色国网app_网站设计是干什么的_中国50强企业管理培训机构_超级外链

2024/12/23 9:58:36 来源:https://blog.csdn.net/m0_74676415/article/details/144341038  浏览:    关键词:绿色国网app_网站设计是干什么的_中国50强企业管理培训机构_超级外链
绿色国网app_网站设计是干什么的_中国50强企业管理培训机构_超级外链

目录

前言

一、程序跑飞原因

软件原因

1. 堆栈溢出

2. 指针操作错误

3. 中断优先级配置错误

4. 外设错误配置

5. 存储器管理问题

6. 代码逻辑错误

7. 时钟配置错误

8. Watchdog 超时

硬件原因

1. 电气问题

2. 硬件故障

软件复位与硬件问题结合

二、调试工具

如何进入仿真?

进入仿真

1.Registers工具

2.Memory工具

3.Disassembly工具

4.Call Stack工具

5.Watch窗口工具

三、找到程序跑飞位置(HardFault_Handler)

1.为什么会产生HardFault_Handler

2.出现HardFault_Handler怎么办

我遇到的HardFault_Handler

总结


前言

我们在使用STM32的时候,代码难免会出现疏忽,导致程序跑飞,不再正常运行,那么都是什么情况会导致STM32程序跑飞呢?或者我们调试的时候,发现代码进入了HardFault_Handler();导致了死循环,我们该如何去找到问题呢?

一、程序跑飞原因

软件原因

软件导致STM32跑飞(程序失控或死机)的原因有多种,主要包括以下几个方面:

STM32程序“跑飞”通常是指程序跳转到了意外的内存区域,导致异常行为或无法正常工作。这种现象可能由多种原因引起,以下是常见的原因及分析:

1. 堆栈溢出

  • 原因

    1.递归函数没有正确退出,导致栈空间耗尽。

    2.动态分配的内存过多,超出了系统栈的大小。

    3.中断嵌套过深,占用过多的栈空间。

  • 解决方法

    1.检查栈的使用情况,确保分配足够的栈空间(在启动文件 .ld.s 中设置)。

    2.使用调试器监控栈指针的位置,确认栈未超出分配范围。

    3.避免递归或深度嵌套的函数调用。

2. 指针操作错误

  • 原因

    1.空指针(NULL)被解引用。

    2.未初始化的指针或悬空指针(指向已释放的内存)。

    3.指针越界访问内存。

  • 解决方法

    1.确保所有指针都被正确初始化。

    2.检查数组和指针操作是否超出范围。

    3.使用静态分析工具(如 PC-Lint)查找潜在的指针错误。

3. 中断优先级配置错误

  • 原因

    1.中断优先级配置冲突(尤其是 FreeRTOS 等系统对优先级有要求)。

    2.高优先级中断执行时间过长,导致系统其他部分无法正常运行。

  • 解决方法

    1.确保中断优先级分配合理,并遵循 ARM Cortex-M 的优先级规则(例如,高优先级数值小)。

    2.在中断中执行时间敏感或关键任务,避免复杂逻辑。

4. 外设错误配置

  • 原因

    1.外设初始化错误(如定时器、DMA、USART 等)。

    2.外设中断未正确配置,导致程序跳转到错误的地址。

  • 解决方法

    1.检查所有外设的初始化代码,确保参数正确。

    2.确保外设的中断处理程序已正确设置。

5. 存储器管理问题

  • 原因

    1.动态内存分配失败(malloc 返回 NULL)。

    2.超出 SRAM 的限制,访问非法地址。

    3.堆栈和堆重叠。

  • 解决方法

    1.避免在嵌入式系统中频繁使用动态内存分配。

    2.检查链接脚本,确保堆和栈区域不重叠。

    3.使用静态分配代替动态分配。

6. 代码逻辑错误

  • 原因

    1.无意的无限循环。

    2.缺少对边界条件的处理。

    3.在某些异常路径中,没有返回合适的地址。

  • 解决方法

    1.使用单步调试工具检查代码逻辑。

    2.在代码中加入断点或日志输出,验证实际运行流程。

7. 时钟配置错误

  • 原因

    1.系统时钟未正确配置,导致外设或 CPU 的行为异常。

    2.时钟频率超出芯片的允许范围。

  • 解决方法

    1.使用 STM32CubeMX 或数据手册验证时钟配置。

    2.确保主时钟频率(HCLK)、外设时钟(PCLK1/PCLK2)等都在芯片允许范围内。

8. Watchdog 超时

  • 原因

    1.程序未及时喂狗,导致复位或跳转。

  • 解决方法

    2.检查喂狗逻辑,确保在程序关键路径中正常运行。

硬件原因

1. 电气问题

  • 原因

    1.电源不稳定或电压过低,导致 CPU 或外设复位。

    2.电磁干扰(EMI)导致程序异常。

  • 解决方法

    1.使用示波器监测电源电压的稳定性。

    2.在 PCB 设计中加强抗干扰能力(如加电容、屏蔽、合理布线)。

2. 硬件故障

  • 原因

    1.芯片或外部存储器损坏。

    2.电气连接问题(如虚焊或断线)。

    3.外部晶振无法起振。

  • 解决方法

    1.检查硬件连接,确保引脚焊接可靠。

    2.在相同代码运行于其他硬件平台上验证是否正常。

    3.时钟采用芯片的内部时钟。

软件复位与硬件问题结合

  • 原因

    1.软件错误触发硬件复位。

    2.电压问题导致复位。

  • 解决方法

    在调试时检查复位原因寄存器(如 RCC_CSR),区分是看门狗复位、掉电复位,还是其他问题。

二、调试工具

我们要发现程序在哪跑飞了,肯定是要借助调试工具了,我们用的是keil5,相信大家已经对他非常非常熟悉了,我们接下来讲解如何使用keil5进行仿真调试,找出我们程序跑死的问题。

如何进入仿真?

我们插好我们的仿真器,常见的STM32仿真器有很多种,有STLINK或者DAP等等,我这里使用的是STLINK_V2这款仿真器,只能说便宜又好用了。插好仿真器,点击魔术棒->Debug->Setting,如果SW Device下面显示了芯片的ID,说明我们成功用仿真器连接到芯片。

进入仿真

我们成功进入了仿真界面,接下来我开始给大家介绍一下这里面的各个功能和工具。

1.Registers工具

我们可以在这里可以查看CPU寄存器的值。

(1)通用寄存器(R0~R12)

R0~R7:这些是低组寄存器,所有指令都可以访问。它们的大小为32位,复位后的初始值不定。 R8~R12:这些是高组寄存器,只有部分的16位Thumb指令可以访问,而32位Thumb-2指令则不受限制。它们的大小同样为32位,复位后的初始值也不定。 (2)特殊功能寄存器

堆栈指针(SP):也称为R13,在Cortex-M4内核中,有两个堆栈指针——主堆栈指针(MSP)进程堆栈指针(PSP)。MSP用于异常服务例程和需要特权访问的应用程序代码,而PSP则用于常规的应用代码。 连接寄存器(LR):即R14,主要作用是保存子程序的返回地址,以便在执行完子程序时恢复现场。 程序计数器(PC):即R15,用于指示当前执行的指令地址。在Cortex-M系列中,由于采用指令流水线技术,读取PC会返回当前指令地址+4(以兼容Thumb代码)。 (3)程序状态寄存器(xPSR)

xPSR是程序状态寄存器,它又被分为三个子状态寄存器:

应用程序状态寄存器(APSR) 中断状态寄存器(IPSR) PRIMASK:只有1个位,置1时关闭所有可屏蔽的异常。 FAULTMASK:只有1个位,置1时只有NMI(非屏蔽中断)可以响应。 BASEPRI:8位寄存器,定义了被屏蔽优先级的阈值。 (4)控制寄存器(Control)

控制寄存器用于控制FPU(浮点单元)的激活、堆栈指针的选择以及线程模式的特权级等。

2.Memory工具

Memory Window是Keil调试环境中的一个窗口,它提供了对程序内存的直接访问。通过这个窗口,你可以查看内存中的字节、字、双字等数据,并可以实时修改这些数据以测试程序的行为。这对于调试和验证程序中的内存访问、堆栈使用、变量存储等问题非常有帮助。

3.Disassembly工具

在Disassembly窗口中,你可以看到程序的反汇编代码。这些代码是程序在CPU上实际执行的指令的文本表示。你可以滚动窗口来查看不同的部分,或者使用窗口中的搜索功能来定位特定的代码或地址。

4.Call Stack工具

Call Stack(调用堆栈)界面是一个关键的调试工具,它允许开发者查看程序执行过程中函数调用的顺序和当前的位置。Call Stack窗口将显示当前函数调用的堆栈。这包括每个函数的调用顺序、每个函数的名称(如果可用)以及调用该函数的地址。你可以看到程序是如何从main函数开始,逐步调用其他函数,直到达到当前执行点的。

5.Watch窗口工具

在keil5调试中,Watch窗口是一个非常重要的工具,用于实时监控程序中的变量、寄存器或表达式的值。

三、找到程序跑飞位置(HardFault_Handler)

1.为什么会产生HardFault_Handler

1.由调试事件触发 2.由总线错误,存储器管理错误或使用错误而产生 这个错误的产生是由于HardFault寄存器状态发生改变所导致,这两个原因看上去说的很玄乎,实际上这个问题大家应该都不陌生,最常出现这个问题的原因一般是数据越界,堆栈溢出,等等。

2.出现HardFault_Handler怎么办

遇到之后没有必要手忙脚乱,这个问题其实并不难解决,因为HardFault_Handler的存在意义并不是为了让你的程序卡死,而是为了帮助你解决程序的问题,可以按照以下步骤进行:

1.找到Registers界面

2.然后查看LR寄存器的值,该寄存器只有六种值是正常情况,具体可参考M4权威指南193页。这里给出一个表格:

需要注意的是,进入HardFault_Handler错误处理的原因并不是LR寄存器导致的,而是HardFault寄存器导致的,LR只是在发生错误前进行现场保存而已。LR寄存器只是我们查询问题所在的“指路器”。 当问题是在中断处理导致的时候,LR寄存器的值为0xFFFFFFF9或者0xFFFFFFE9时(也就是上述表格的前两种情况),问题是在由中断事件到中断处理的过程中产生的,而在值为0xFFFFFFF1或者0xFFFFFFE1 的时候,问题是在中断处理被另一中断打断时才发生的,所以才会认为LR的值为0xFFFFFFF1或者0xFFFFFFE1 的时候一定是发生了中断嵌套。我们可以查看一下Cortex M3与M4权威指南。  

3.根据LR的寄存器的值判断是主栈还是线程栈导致的问题,,如果是主栈就继续查看MSP寄存器,如果是进程栈,那么就查看PSP寄存器。

4.根据MSP寄存器或者PSP寄存器的记录,将其值在Memory中查看其具体地址,一般是0x80开头的。  

5.打开Disassembly,在里面可以找到具体是哪一行代码导致的该问题。

们可以成功找到我们跑飞前代码地方了。

我遇到的HardFault_Handler

我这里的问题很简单,同时也是我故意设置的一个堆栈溢出的问题,这里我带大家分析一下:

首先,我们是走到一条叫menuPoint[selectItem+scrollBar].func1()来去执行我们想执行的函数,我们通过Watch窗口查看一下:

我本意里面的func1是应该指向一个叫amplitudeSub的函数指针的,这里指向不正确,于是我就非常怀疑这个结构体赋值给这个成员有一个环节出了问题,于是我就一步步的单步指向,知道我执行到了一个地方:  

在这之前,结构体里面的值都是我理想的值,当我执行完下一条指令的时候,错误出现了。

这里的func1的值居然给我离奇的改变了,因为他的改变,导致我后续程序的跑飞。我们仔细看看执行的那条代码,发现是数组溢出了。

 

结构体里面的数组大小是15,我这里已经超过了15,所以问题找到了,我们进行修正,继续执行,发现程序没有出现跑死了。  

总结

调试我只能说非常的考验自己,入门刚刚好一年STM32,每次打开调试这个界面的时候,都感觉有点力不从心,但是随着一步步的不断调试,我逐渐对调试这个界面越来越熟悉,见到的问题也越来越多,只有不断的去遇到问题,然后逐步的解决问题,才能从菜鸟变成大神吧。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com