摘要系统在正常运行时数据存储正常但突然断电重启后发现 Flash 里的参数变成了 0xFF 或乱码不是 Flash 寿命到了而是掉电瞬间写操作未完成 或擦除-写入时序被破坏。本文解析 Flash 存储的“原子性”陷阱。一、问题描述现象**设备运行正常参数保存在 Flash 中模拟突然断电拔掉电源再次上电读取参数发现校验失败数据全变成 0xFF 或 0x00。**很多工程师的排查方向是Flash 擦写次数到了写入函数没执行完校验算法错了二、原理分析1. 物理模型Flash 的写入不是瞬间的而是一个高压隧穿的过程。擦除 (Erase) - 写入 (Program) - 校验 (Verify)2. 核心参数Tprog编程时间写一个字Word所需的时间通常 10~100µs。Vpp编程电压Flash 内部泵升的电压依赖 VDD。Write BufferFlash 接口的写缓冲区。3. 反直觉真相“写入函数返回 Success并不代表数据已经落地。”Flash 控制器通常有缓存。如果在HAL_FLASH_Program()返回后、但缓存还没刷入物理单元时断电数据丢失。如果掉电发生在擦除完成后、写入开始前扇区是空的0xFF数据丢失。三、工程级解决方案方案 1双备份 标志位黄金法则永远不要在原地覆盖数据。存储结构Sector A: [Valid Flag] [Data] Sector B: [Valid Flag] [Data]流程写 Sector B新数据。校验 Sector B。擦除 Sector A。设置 Sector B 的 Valid Flag。结果 任何时候断电至少有一份数据是完整的。方案 2掉电检测BOR中断利用 MCU 的掉电中断BOR IT 或PVD可编程电压检测器。void PVD_IRQHandler(void) { // 1. 立刻关闭不必要的外设减少电流消耗 // 2. 停止 Flash 写操作 // 3. 点亮 LED 报警 // 4. 等待电容放电维持 10ms 以上 }关键点 必须在 VDD 跌落到 Flash 最低工作电压如 2.7V之前停止所有写操作。方案 3大电容蓄能在电源输入端增加大容量储能电容。Vbat - [LDO] - [100uF 电解] - [10uF MLCC] - MCU计算假设 MCU 运行电流 50mA最低工作电压 3.0V。需要在 3.3V - 3.0V 跌落期间维持 50ms。CΔVI×Δt0.30.05×0.05≈8330μF。四、选型避坑建议不要在中断里写 Flash写 Flash 时间长会阻塞系统。写操作期间不要关中断Flash 控制器可能需要中断来同步状态。ECC纠错码高端 MCU 支持 Flash ECC能纠正 1bit 错误但无法防止掉电。五、总结 Checklist[ ] 是否采用了“双备份 标志位”的存储结构[ ] 是否利用了 BOR/PVD 中断在掉电前停止写操作[ ] 电源输入端是否有足够的储能电容 1000µF[ ] 写入数据后是否进行了回读校验六、写在最后关注我少走弯路我是 gqqsherry一个拒绝调包、专注底层逻辑的嵌入式工程师。数据的持久化是“最后的一公里”如果这一步没走好前面的功能再完美也是零。关注我的专栏《嵌入式底层避坑指南》《复位与启动》系列到此正式完结。专栏总索引预告《嵌入式底层避坑指南 · 全系列导航》原创文章转载请注明出处。
掉电瞬间数据会丢?别只怪 Flash,看看写操作与时序
摘要系统在正常运行时数据存储正常但突然断电重启后发现 Flash 里的参数变成了 0xFF 或乱码不是 Flash 寿命到了而是掉电瞬间写操作未完成 或擦除-写入时序被破坏。本文解析 Flash 存储的“原子性”陷阱。一、问题描述现象**设备运行正常参数保存在 Flash 中模拟突然断电拔掉电源再次上电读取参数发现校验失败数据全变成 0xFF 或 0x00。**很多工程师的排查方向是Flash 擦写次数到了写入函数没执行完校验算法错了二、原理分析1. 物理模型Flash 的写入不是瞬间的而是一个高压隧穿的过程。擦除 (Erase) - 写入 (Program) - 校验 (Verify)2. 核心参数Tprog编程时间写一个字Word所需的时间通常 10~100µs。Vpp编程电压Flash 内部泵升的电压依赖 VDD。Write BufferFlash 接口的写缓冲区。3. 反直觉真相“写入函数返回 Success并不代表数据已经落地。”Flash 控制器通常有缓存。如果在HAL_FLASH_Program()返回后、但缓存还没刷入物理单元时断电数据丢失。如果掉电发生在擦除完成后、写入开始前扇区是空的0xFF数据丢失。三、工程级解决方案方案 1双备份 标志位黄金法则永远不要在原地覆盖数据。存储结构Sector A: [Valid Flag] [Data] Sector B: [Valid Flag] [Data]流程写 Sector B新数据。校验 Sector B。擦除 Sector A。设置 Sector B 的 Valid Flag。结果 任何时候断电至少有一份数据是完整的。方案 2掉电检测BOR中断利用 MCU 的掉电中断BOR IT 或PVD可编程电压检测器。void PVD_IRQHandler(void) { // 1. 立刻关闭不必要的外设减少电流消耗 // 2. 停止 Flash 写操作 // 3. 点亮 LED 报警 // 4. 等待电容放电维持 10ms 以上 }关键点 必须在 VDD 跌落到 Flash 最低工作电压如 2.7V之前停止所有写操作。方案 3大电容蓄能在电源输入端增加大容量储能电容。Vbat - [LDO] - [100uF 电解] - [10uF MLCC] - MCU计算假设 MCU 运行电流 50mA最低工作电压 3.0V。需要在 3.3V - 3.0V 跌落期间维持 50ms。CΔVI×Δt0.30.05×0.05≈8330μF。四、选型避坑建议不要在中断里写 Flash写 Flash 时间长会阻塞系统。写操作期间不要关中断Flash 控制器可能需要中断来同步状态。ECC纠错码高端 MCU 支持 Flash ECC能纠正 1bit 错误但无法防止掉电。五、总结 Checklist[ ] 是否采用了“双备份 标志位”的存储结构[ ] 是否利用了 BOR/PVD 中断在掉电前停止写操作[ ] 电源输入端是否有足够的储能电容 1000µF[ ] 写入数据后是否进行了回读校验六、写在最后关注我少走弯路我是 gqqsherry一个拒绝调包、专注底层逻辑的嵌入式工程师。数据的持久化是“最后的一公里”如果这一步没走好前面的功能再完美也是零。关注我的专栏《嵌入式底层避坑指南》《复位与启动》系列到此正式完结。专栏总索引预告《嵌入式底层避坑指南 · 全系列导航》原创文章转载请注明出处。