• 正文
  • 相关推荐
申请入驻 产业图谱

STM32G0B1 Flash 编程 PGSERR 错误排查:DMA 中断残留引发的非法写故障

03/31 14:38
501
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在 STM32G0 系列 Flash 擦写编程中,不少工程师会遇到一个违背常识的故障:还没执行任何 Flash 擦除 / 编程操作,Flash 状态寄存器 SR 却已置起PGSERR(编程序列错误),不清除该标志就无法继续操作 Flash。这一现象极易让人怀疑芯片本身存在缺陷,而 ST 官方 LAT1210 应用笔记通过真实案例,揭开了这个诡异问题的真相 ——并非芯片问题,而是 DMA 中断残留导致的非法 Flash 写操作。

本文基于该笔记,完整还原问题现象、根因定位、编译器差异解析及根治方案,帮你快速避开这类隐蔽的底层故障。

资料获取:【应用笔记】LAT1210 一个Flash编程错误标志的探析

1. 诡异的故障现象:未操作 Flash 却报编程错误

客户基于 STM32G0B1 开发项目,在执行 Flash 擦写流程前,读取 Flash SR 寄存器发现PGSERR 位已置 1,出现两个典型问题:

  1. 未执行解锁、擦除、写入等任何 Flash 操作,却提前触发编程序列错误;
  2. 必须手动清除该错误标志,后续 Flash 操作才能继续,且开发者担心清除操作影响产品稳定性;
  3. 相同代码在IAR 编译报错,Keil 编译正常,表现完全不一致。

从 Flash 操作规范来看,PGSERR 是违反 Flash 写入时序产生的硬件错误,正常情况下未操作 Flash 不可能触发,这也是客户怀疑芯片缺陷的核心原因。

2. 故障根因定位:共用 DMA 中断 + 无效句柄引发非法写 Flash

通过精简工程、剥离无关模块,最终在 NUCLEO-G0B1 开发板上复现问题,定位到串口 DMA 中断配置错误这一核心根源。

2.1 问题根源:未使用的串口 DMA 中断未移除

客户工程中使用 USART2 和 USART3,均配置为 DMA 模式;后期整合代码时,USART2 被弃用,初始化代码被删除,但中断处理函数未清理。

STM32G0B1 的 DMA 中断资源存在共用特性:DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQHandler

是 DMA1 通道 4-7、DMA2 通道 1-5 的共用中断入口,USART2 与 USART3 的 DMA 中断均挂载在此中断线上。

2.2 无效 DMA 句柄导致非法地址访问

中断处理函数中,仍保留 USART2 的 DMA 处理代码:

HAL_DMA_IRQHandler(&hdma_usart2_rx);
HAL_DMA_IRQHandler(&hdma_usart2_tx);

由于 USART2 初始化已删除,hdma_usart2_rxhdma_usart2_tx这两个 DMA 句柄未初始化、成员变量为随机值 / 0,调用HAL_DMA_IRQHandler时出现致命错误:

  1. 无效句柄的DmaBaseAddress被赋值为0x00000000;
  2. 函数内部执行清除标志操作:
hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << (hdma->ChannelIndex & 0x1CU));
  1. 地址 0x00000000 在 STM32G0B1 中映射到内部 Flash 首地址 0x08000000,该操作相当于直接向 Flash 地址执行写操作;
  2. Flash 未解锁、未擦除就被直接写入,硬件立即触发PGSERR 编程序列错误。

这就是 “未操作 Flash 却报错” 的真实原因 ——DMA 中断后台执行了非法 Flash 写操作。

3. 关键疑问:相同代码 IAR 报错、Keil 正常的原因

客户工程出现编译器差异化表现,根源在于栈顶地址分配不同:

  1. flag_it取值自hdma->DmaBaseAddress->ISR,而DmaBaseAddress=0,实际读取的是地址 0 映射的栈顶地址;
  2. IAR 与 Keil 对栈地址的默认分配规则不同,导致栈顶初始值不同;
  3. 栈顶值差异导致HAL_DMA_IRQHandler内的if 条件判断结果不同:
    • IAR:条件成立,进入错误分支,执行非法写 Flash → 触发 PGSERR;
    • Keil:条件不成立,跳过错误分支 → 无异常。

本质是无效指针访问 + 栈地址随机分配,让相同代码在不同编译器下表现迥异,这类问题极具隐蔽性。

4. 根治方案:清理无效中断 + 规范 DMA 配置

定位根因后,解决方案简单直接,且能彻底杜绝问题复发:

4.1 立即修复:移除无效 DMA 中断处理代码

stm32g0xx_it.c的共用 DMA 中断函数中,删除未使用的 USART2 DMA 处理代码:

// 删除这两行无效代码
// HAL_DMA_IRQHandler(&hdma_usart2_rx);
// HAL_DMA_IRQHandler(&hdma_usart2_tx);
// 仅保留有效串口USART3的DMA处理
HAL_DMA_IRQHandler(&hdma_usart3_rx);
HAL_DMA_IRQHandler(&hdma_usart3_tx);

4.2 规范配置:外设弃用时需完整清理

后续开发中,若停用某外设,必须执行完整清理:

  1. 删除初始化代码;
  2. 关闭外设时钟、DMA 通道;
  3. 注销中断、清理中断处理函数;
  4. 不保留无效句柄调用代码。

4.3 防御性编程:DMA 中断处理前校验句柄有效性

在共用中断中添加句柄状态校验,避免调用未初始化的 DMA 句柄:

// 示例:校验DMA句柄状态后再处理
if(hdma_usart2_rx.State != HAL_DMA_STATE_RESET)
{
    HAL_DMA_IRQHandler(&hdma_usart2_rx);
}

5. 开发避坑总结

  1. Flash 错误并非都是芯片问题:STM32 所有已知硬件缺陷均会公示在勘误手册中,99% 的 Flash 异常均为应用层代码错误,切勿先入为主怀疑芯片品质。
  2. 警惕 STM32 共用中断资源:G0、G4、C0 等入门级系列存在大量外设 / DMA 共用中断,修改外设配置时,必须检查中断入口是否残留无效代码。
  3. 无效指针是底层故障的重灾区:未初始化的句柄、指针,会导致非法地址访问,轻则触发 HardFault,重则破坏 Flash / 寄存器,引发无规律故障。
  4. 编译器差异会掩盖问题:IAR 与 Keil 的栈分配、变量初始化规则不同,会让同一故障表现迥异,必须以报错环境为基准排查。

本次 STM32G0B1 Flash PGSERR 错误,是典型的 **“隐蔽后台操作引发的硬件异常”**,看似诡异的现象,本质是代码整合时的疏忽所致。

这个案例也给所有嵌入式开发者提了醒:底层硬件异常,优先排查中断、DMA、指针访问等后台操作,而非质疑芯片本身。遵循 “外设弃用→完整清理” 的规范流程,就能从根源上杜绝这类隐蔽故障。

相关推荐