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