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造成影响。因此推测,影响很可能在编译阶段就已经发生,需要重点关注编译阶段的有关代码,宏定义,全局变量,以及全局变量类的构造函数!

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

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

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

cpp 复制代码
#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擦除失败的现象

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

相关推荐
三佛科技-1341638421213 分钟前
FT32F072xx、FT32F072xB、FT32F072x6/x8基于ARM Cortex-M0内核32位单片机分析
arm开发·单片机·嵌入式硬件·智能家居·pcb工艺
清风66666614 分钟前
基于单片机的火焰与温度联动检测及声光灭火控制系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
chen_mangoo28 分钟前
Rockchip debian预置安装deb包
linux·驱动开发·嵌入式硬件
梁山1号31 分钟前
【关于CAN】
c语言·stm32·单片机
尼喃33 分钟前
汽车摩托车LED车灯驱动芯片IC,精准调光抗干扰,为车灯性能提升赋能
单片机·汽车·51单片机·芯片
金线银线还是铜线?1 小时前
BLE设备 MF9006 PMIC 能量协同设计 低功耗物联网供电方案
嵌入式硬件·物联网·射频工程·iot
yuanmenghao1 小时前
CAN系列 — (3) Radar Object List 在 MCU 内部是如何被拼装、校验并最终被消费的?
单片机·嵌入式硬件·自动驾驶·信息与通信
yuanmenghao1 小时前
CAN系列 — (4) Radar Header 报文:为什么它是 MCU 感知周期的“锚点”
网络·单片机·自动驾驶·信息与通信
飞睿科技1 小时前
乐鑫ESP32-S3-BOX-3,面向AIoT与边缘智能的新一代开发套件
人工智能·嵌入式硬件·esp32·智能家居·乐鑫科技
Y1rong1 小时前
STM32之SPI
stm32·单片机·嵌入式硬件