news 2026/4/4 13:44:31

STM32出现FLASH擦除失败异常现象分析及解决方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32出现FLASH擦除失败异常现象分析及解决方法

目录

前言:

先说结论:

一、问题分析

二、异常排查

三、发现原因!

四、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~


前言:

长颈鹿最近在使用STM32G070单片机进行项目开发,过程中遇到了一个很棘手的Bug,设备在调试中突然死机,仿真得知,原来是写FLASH操作失败,导致设备进入FLASH错误处理流程,陷入无限循环。好奇怪的现象,FLASH读写属于底层驱动,一般是不会有改动的,笔者也并没有动FLASH的相关代码,但莫名其妙出现这样的情况。决定刨根问底,探究一下深层次的原因。

先说结论:

出现该异常是因为FLASH擦除失败,从而导致FLASH写入操作失败,而FLASH擦除失败的原因在于类的构造函数位置问题,出于项目需要,长颈鹿把一个类定义在了文件作用域内,在这个类中一个类成员对象的构造函数中调用了HAL_TIM_PWM_Start() 函数,虽加上了static前缀,但其仍相当于一个全局变量,而系统中定义的全局变量会在main函数调用之前先被创建,对于类而言,被创建时会自动调用其构造函数,而我又在这个构造函数中调用了HAL_TIM_PWM_Start()函数,在所有外设初始化之前就操作了RCC寄存器(HAL_TIM_PWM_Start()会修改RCC寄存器,如RCC->APB1ENR使能TIM时钟),导致RCC寄存器的状态出现了异常,而FLASH操作要求系统时钟处于稳定且已知的状态,由此就产生了冲突,导致FLASH擦除失败,进而系统卡死。

所以如果你也遇到了类似的问题而又无从下手,可以检查一下代码中类的构造函数或者类中包含的成员类的构造函数,类定义的作用域有没有问题,构造函数中是不是有在外设初始化之前操作RCC寄存器的嫌疑!

这里的HAL_FLASH_Program()函数写入失败
导致程序进入Error_Handler()错误处理函数,陷入无限循环

一、问题分析

仿真发现,写FLASH失败是因为上一步擦除FLASH扇区出现了异常

擦除FLASH的操作调用了HAL库的HAL_FLASHEx_Erase(),在FLASH擦除函数中,调用了 FLASH_WaitForLastOperation() 函数来检测FLASH当前是否空闲,是否可对FLASH进行擦除和写入等操作,但是FLASH_WaitForLastOperation()函数在检测时,这个FLASH->SR中的FLASH_SR_CFGBSY位始终为1,导致函数超时退出。

FLASH->SR表示FLASH中的SR状态寄存器,FLASH_SR_CFGBSY 标志位是 SR 的状态位之一,该位表示FLASH当前是空闲,为 0 表示可执行擦除或写入操作。

FLASH->SR寄存器位

通过Watch窗口查看FLASH-SR的值,以及HAL库中定义的FLASH_SR_CFGBSY的值,两者都为0x00040000,对应上了,表示当前FLASH正处于忙状态。

二、异常排查

那么问题来了,这步的FLASH读写操作是程序对FLASH进行的首次操作,FLASH的状态怎么会是BUSY呢?况且STM32G070只有一个内核,程序只能顺序执行,对FLASH的操作一般都会等待FLASH状态回归正常才会结束,这个现象真的是非常奇怪。

是什么导致的呢?

长颈鹿一步一步的在项目中寻找,终于发现了端倪,造成FLASH擦除出错的原因竟然是一个通过定时器实现的呼吸灯!这个呼吸灯函数放在代码的开始部分,检查有无设备是否处于充电状态,如果处于充电状态,就开启呼吸灯。如果没有,设备正常开机。笔者将呼吸灯的源文件注释掉,代码编译后可以正常运行,将呼吸灯的源文件添加进项目中编译,即使不调用该函数,设备在进行FLASH擦除操作时也会卡死在这个地方。

但是定时器和FLASH,两个八竿子打不着的外设,为什么会互相影响?

而且只要参与编译,即使没有在程序中调用该函数,也会对FLASH造成影响。因此推测,影响很可能在编译阶段就已经发生,需要重点关注编译阶段的有关代码,宏定义,全局变量,以及全局变量类的构造函数!

果不其然,在呼吸灯的构造函数中发现了异常点。

CBreathingLight::CBreathingLight() { bDirction = DOWN; u32CCRVal = LEDS_CCR_VAL; u8ChargeState = CHARGE_OFF; /* 充电状态 */ vON(); /* 启动PWM输出 */ }

构造函数中的vON();函数会启动PWM输出,有操作定时器和调用系统时钟的行为,这个函数放在构造函数中,单独看没有问题,但是CBreathingLight类的对象定义在系统运行类CSystem中,而CSystem类的对象定义在文件作用域内!

#include "CSystem.h" static CSystem m_App; #if defined(__cplusplus) extern "C" { #endif void vInit(void) { m_App.bInit(); } void vRun(void* parameter) { m_App.vRun(0); } #if defined(__cplusplus) } #endif

三、发现原因!

CSystem类是一个全局变量,编译器会在编译时创建全局变量的对象,而针对类的全局变量,会自动调用其构造函数初始化类内成员,也就是说,CSystem类的构造函数会在main函数之前调用,而CBreathingLight类作为CSystem类的类内成员,其构造函数会在CSystem类的构造函数之前调用。

最终,在还没有进入main函数进行外设(GPIO、TIM等)初始化操作时,系统就通过构造函数执行了CBreathingLight类内的vON();函数,启动PWM输出,操作了定时器和系统时钟,虽然语法上没有问题,但这种行为是非法的,推测对后续的系统时钟造成了影响,继而导致FLASH擦除出现错误。

果然,去掉构造函数内调用的vON函数之后,系统恢复正常。

STM32的所有外设(包括TIM和FLASH)都依赖系统时钟配置(RCC寄存器),HAL_TIM_PWM_Start()会修改RCC寄存器(如RCC->APB1ENR使能TIM3时钟),FLASH操作要求系统时钟处于稳定且已知的状态,(如FLASH->CR中的LOCK位需清空),vON()修改了TIM3时钟配置(RCC),导致FLASH控制器误判为"正在配置中"所以出现了FLASH擦除失败的现象

四、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/29 9:08:55

【故障排查】WPS/Word粘贴数学公式变图片?竟是格式在“搞鬼”

【故障排查】WPS/Word粘贴数学公式变图片?竟是格式在“搞鬼”踩坑了!复制WPS/Word里的数学公式,粘贴完居然变成图片,根本没法编辑。折腾半天发现,罪魁祸首是文档格式——把doc改成docx就搞定了!问题很简单就…

作者头像 李华
网站建设 2026/4/3 2:59:46

AI应用架构实战:上下文工程的数据预处理

AI应用架构实战:上下文工程的数据预处理——让模型“听懂”你的每一句话 关键词 上下文工程 | 数据预处理 | AI应用架构 | 对话系统 | 向量数据库 | 上下文窗口 | 语义分割 摘要 在AI应用(如对话系统、知识库问答、个性化推荐)中&#xf…

作者头像 李华
网站建设 2026/3/25 18:33:18

OCCT-右手坐标系

一、右手坐标系的定义(数学与工程一致)右手坐标系(Right-Handed Coordinate System)规则:右手 食指 → X 正方向右手 中指 → Y 正方向右手 拇指 → Z 正方向三者满足:X Y Z也就是: X 轴叉乘 …

作者头像 李华
网站建设 2026/3/22 23:54:54

jvm性能调优

JVM性能调优方法调整堆内存大小 -Xms和-Xmx参数分别设置JVM堆的初始大小和最大大小。建议将两者设为相同值以避免运行时动态调整带来的性能开销。例如: -Xms4g -Xmx4g 根据应用需求调整大小,避免频繁Full GC。选择合适的垃圾回收器 根据应用特点选择垃圾…

作者头像 李华
网站建设 2026/4/4 4:53:05

超绝好用清理软件工具,(附下载方式)超多好用功能

电脑用久了,难免会堆积闲置软件、捆绑程序,自带卸载工具往往 “卸不干净”,残留的文件碎片和注册表垃圾不仅占用磁盘空间,还可能拖慢系统运行。今天给大家实测推荐三款 Windows 平台口碑爆棚的卸载工具 ——HiBit Uninstaller、Ge…

作者头像 李华