嵌入式 · Flash · 擦写粒度
只改一个配置字节,Flash为什么还要先擦一整块?别把读写粒度混在一起
能读一个字节,不等于能独立擦掉并重写这个字节
Flash 的读取、编程和擦除通常具有不同粒度。修改一个配置字节,工程上常要先读出目标块,在 RAM 中修改,擦除原块,再重写并校验。若掉电发生在擦除与重写之间,还需要双副本、日志或版本标记保证可恢复。
固件只想更新一个配置值,代码却要申请一块 RAM、擦除一片区域、重新写回,过程比改变量复杂得多。更麻烦的是,设备在更新中途掉电,下一次启动可能发现整组配置都无效。
原因不是 Flash “不好用”,而是它的读取、编程和擦除不是同一种粒度。把存储器的物理操作边界看清,软件结构才不会把一次小更新变成数据风险。
一、读取、编程、擦除为什么要分开理解
很多非易失存储器可以细粒度读取,也能以页或更小单元编程,但擦除往往以更大的块为单位。具体粒度、方向限制和时序必须以目标器件数据手册为准。
图 1 读取、编程与擦除可能采用不同粒度(原理示意,非实测结果)
所以“CPU 能读到这个字节”不能推出“CPU 能独立把这个字节改成任意新值”。更新动作受到擦除块和编程规则约束。
二、为什么改一个字节常要重写整块
当目标位置无法在当前位状态上直接编程时,需要先擦除包含它的整个块。为了保留同块里的其他有效数据,固件通常先把整块读到 RAM,修改目标字段,再擦除并重写。
图 2 小字段更新常需要读改擦写与读回校验(原理示意,非实测结果)
这条链路中,地址边界、缓存大小、擦除完成判断、编程完成判断和读回校验都不可省略。写 API 返回成功,也应确认数据是否真正落在预期位置。
三、EEPROM和Flash的关键差异,不只是容量
传统 EEPROM 常强调更细粒度的随机修改能力;Flash 通过更大的擦除单元换取更高密度和不同成本结构。工程上两者都属于非易失存储,但软件更新策略往往不同。
选择存储器时,不只比较容量和接口,还要看更新频率、写入粒度、擦除块、耐久度、保持、坏块管理、启动要求和掉电行为。具体指标必须查目标型号数据手册,不使用同类器件的经验代替。
四、最危险的窗口在“旧数据已擦,新数据未完成”
如果先擦除原块,再开始写新数据,掉电可能发生在两者之间。此时旧版本已经不存在,新版本又不完整,设备重启后就没有可信配置。
图 3 双副本更新可避免先破坏唯一有效版本(原理示意,非实测结果)
常见思路是保留旧副本,先在新区写入新内容,完成校验后再原子化更新有效标记。也可使用日志、版本号、校验和或文件系统机制;具体方案取决于存储器特性和系统可接受的恢复复杂度。
五、配置存储至少做这6项检查
- 确认读取、编程和擦除粒度,以及地址对齐要求。
- 定义数据结构版本、长度和校验,避免固件升级后误读旧格式。
- 设计掉电窗口:任何时刻断电,重启后至少有一个可识别的有效版本。
- 对频繁更新字段做写入合并、轮换或磨损均衡,避免集中消耗同一区域。
- 每次写入后读回校验,并区分空白、有效、正在写和损坏状态。
- 在电压边界、复位、看门狗和真实掉电条件下做故障注入测试。
图 4 存储方案应按更新行为与恢复要求选择(原理示意,非实测结果)
六、3个常见误区
- 按字节读就能按字节改:读取能力不能代表擦除与编程粒度。
- 写函数返回成功就安全:还要读回校验,并验证掉电恢复。
- 数据很少就不用做版本:小配置同样会经历固件升级、结构变化和中途掉电。
工程判断:Flash 更新必须围绕擦除块设计,而不是围绕变量大小设计。读改擦写、校验和掉电恢复是一条完整链路;少任何一步,都可能让一个字节更新破坏整块数据。
写在最后
嵌入式里最危险的存储 bug,往往不是读不到,而是大多数时候都能写,只有一次异常掉电把唯一副本破坏。
先把器件的读、编程、擦除粒度画出来,再设计数据结构和更新状态机,配置保存才会从“能用”变成“可恢复”。