1. 为什么STM32F103的Flash扇区会擦除失败最近在做一个嵌入式项目时遇到了一个让人头疼的问题STM32F103芯片的Flash扇区在第一次擦除后再次尝试擦除就会报FLASH_ERROR_PG错误。这个问题困扰了我好几天直到发现了一个关键点——标志位没有及时清除。Flash存储器就像一个小本子每次擦除就相当于把整页纸擦干净。但在STM32中这个擦除操作会留下一些痕迹也就是状态标志位。如果不及时清理这些标志位下次再想擦除时芯片就会认为上次操作还没完成或者出错了于是拒绝执行新的擦除命令。具体来说当出现FLASH_ERROR_PG错误时通常意味着编程操作失败PGERR标志位被置1写保护错误WRPRTERR标志位被置1操作结束标志EOP标志位被置1这些标志位就像是芯片内部的小旗子每次操作后都需要手动把它们放倒否则下次操作时芯片就会看到这些还竖着的旗子以为出了什么问题。2. FLASH_ERROR_PG错误的深层原因分析2.1 Flash控制器的内部机制STM32F103的Flash控制器其实是个相当精密的部件。它内部维护着一组状态寄存器FLASH_SR用来记录最近一次操作的状态。当我们执行擦除或写入操作时控制器会自动更新这些状态位。常见的几个重要标志位包括PGERR编程错误标志WRPRTERR写保护错误标志EOP操作结束标志这些标志位有个特点它们不会自动清除也就是说一旦被置位就会一直保持直到我们手动清除它们。这就好比你在厨房做饭用完的锅碗瓢盆如果不及时清洗下次想做饭时就会发现没干净的厨具可用了。2.2 重复擦除失败的具体场景在实际项目中这个问题通常出现在需要频繁更新Flash数据的场景比如实时参数保存运行日志记录配置信息存储以我的项目为例我们需要在系统运行时动态保存一些参数。最初的设计是每次上电时擦除一次Flash然后写入数据。这样运行没问题因为每次上电都是一次全新的开始。但当需求变成要在运行时多次保存数据时问题就出现了——第二次尝试擦除同一个扇区时必然会失败。3. 完整的解决方案与代码实现3.1 关键步骤清除标志位解决这个问题的关键就是在每次擦除操作前先清除所有相关的标志位。这就像每次做饭前先把厨房收拾干净一样重要。具体实现很简单只需要在擦除操作前加入这行代码FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);这行代码的作用是同时清除三个可能影响后续操作的标志位。在实际项目中我建议不管这些标志位当前是否被置位都统一清除一下这样可以确保万无一失。3.2 完整的Flash操作流程结合我的项目经验一个健壮的Flash操作流程应该如下__disable_irq(); // 第一步关闭总中断防止操作被打断 FLASH_Unlock(); // 第二步解锁Flash获得操作权限 // 第三步清除所有可能影响操作的标志位 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 第四步执行扇区擦除 FLASH_ErasePage(STM32_FLASH_BASE); // 第五步写入数据 for(i 0; i 12; i) { FLASH_ProgramHalfWord(STM_IDADDRi*2, pBuffer[i]); } // 写入结束标志 FLASH_ProgramHalfWord(STM_SETADDR, SET_FLAG); FLASH_Lock(); // 第六步重新锁定Flash __enable_irq(); // 第七步恢复中断这个流程中清除标志位的操作放在了擦除之前这是经过多次测试验证的最佳位置。如果放在擦除之后可能会错过某些在擦除过程中产生的标志位。4. 实际项目中的注意事项与优化建议4.1 操作时序的重要性在操作Flash时时序非常关键。根据STM32参考手册Flash控制器需要一定的时间来完成各种操作。如果在操作未完成时就尝试进行下一步可能会导致不可预料的错误。我的经验是在擦除或编程操作后最好加入一个简短的延时可以通过轮询FLASH_SR寄存器中的BSY位来判断操作是否完成对于关键数据建议实现校验机制比如CRC校验4.2 Flash寿命的考量虽然解决了重复擦除的问题但还需要注意Flash的寿命问题。STM32F103的Flash通常可以承受约1万次的擦写循环。在需要频繁更新的场景中建议实现磨损均衡算法将写操作分散到不同地址尽量减少不必要的擦写操作对于频繁变更的数据可以考虑先缓存在RAM中定期批量写入Flash4.3 错误处理的最佳实践在实际项目中完善的错误处理机制非常重要。我的做法是FLASH_Status status FLASH_ErasePage(STM32_FLASH_BASE); if(status ! FLASH_COMPLETE) { // 记录错误日志 // 执行恢复操作 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 可能需要重新尝试操作 }这种处理方式可以大大提高系统的鲁棒性特别是在工业控制等对可靠性要求高的场景中。