3. Flash 编程与擦除:标准流程、页擦除、批量擦除与编程模式

Flash 操作,说白了就是两件事:擦除和编程。擦除是把 1 写进去,编程是把 0 写进去。这个顺序不能乱,必须先擦后写。我刚开始做项目时,就因为没注意这个顺序,折腾了一整天。

3.1 标准编程流程

不管是哪家厂商的 MCU,Flash 编程流程都大同小异。我以 STM32 为例,给你拆解一下标准步骤:

  1. 解锁 Flash —— 写入解锁序列(KEY1 + KEY2),防止误操作。
  2. 检查忙标志 —— 确认 Flash 控制器空闲(BSY = 0)。
  3. 配置编程宽度 —— 选择半字、字或双字模式。
  4. 写入目标地址 —— 地址必须对齐(半字对齐、字对齐等)。
  5. 写入数据 —— 硬件自动启动编程。
  6. 等待完成 —— 轮询 BSY 位或等待 EOP 中断。
  7. 校验数据 —— 读回写入值,确认无误。
  8. 锁定 Flash —— 重新上锁,防止意外改写。

核心原则:每次编程前,目标地址所在的页必须处于擦除状态(全 0xFF)。如果页内有有效数据,必须先擦除再编程。

3.2 页擦除与批量擦除

擦除操作分为两种:页擦除和批量擦除(也叫整片擦除)。

特性 页擦除 批量擦除
擦除粒度 1KB / 2KB(视型号而定) 整个 Flash 阵列
典型耗时 10~40 ms 100~500 ms
适用场景 更新参数、日志、配置块 固件升级、出厂初始化
风险 低(只影响单页) 高(全片数据丢失)

我个人习惯是:能用页擦除,绝不用批量擦除。为什么?因为批量擦除一旦触发,中途断电或复位,整个芯片就变砖了。我在一个 OTA 项目中就吃过这个亏——批量擦除到一半,用户拔了电源,结果 MCU 再也起不来了。

避坑指南:批量擦除前,务必确保电源稳定。如果条件允许,加一个掉电检测电路,检测到电压跌落时立即停止擦除操作。

3.3 半字 / 字 / 双字编程

Flash 编程不是按字节写的,而是按半字(16 位)、字(32 位)或双字(64 位)写入。具体用哪种,取决于你的 MCU 和 Flash 控制器设计。

编程模式 数据宽度 地址对齐要求 典型耗时(每单位)
半字编程 16 位 半字对齐(地址 bit0 = 0) ~50 μs
字编程 32 位 字对齐(地址 bit1:0 = 00) ~100 μs
双字编程 64 位 双字对齐(地址 bit2:0 = 000) ~200 μs

你可能会问:为什么不用字节编程?嗯,这是因为 Flash 的物理结构决定的。Flash 存储单元是浮栅晶体管,编程时一次注入的电荷量是固定的,按字节写反而效率更低。所以厂商干脆不支持字节编程。

我在一个数据采集项目中,需要频繁存储传感器数据。一开始我用半字编程,每次写 2 字节,速度还行。后来数据量大了,我改成字编程,速度翻倍。再后来,我干脆用双字编程,配合 DMA,效率直接拉满。

小技巧:如果数据量较大,优先使用双字编程。虽然单次耗时更长,但单位字节的编程时间更短。配合双缓冲或乒乓缓存,可以做到“零等待”写入。

3.4 编程时间与功耗分析

Flash 编程不是免费的午餐。每次编程都会消耗时间和电流。我整理了一份典型数据,供你参考:

操作 典型时间 典型电流 能量消耗
页擦除(1KB) 20 ms 5 mA 100 μJ
半字编程 50 μs 8 mA 0.4 μJ
字编程 100 μs 8 mA 0.8 μJ
双字编程 200 μs 8 mA 1.6 μJ
批量擦除(512KB) 200 ms 5 mA 1 mJ

从表中可以看出:擦除比编程耗电得多。页擦除一次 20 ms,耗电 100 μJ;而写一个双字才 1.6 μJ。所以,如果你在做电池供电的设备,尽量减少擦除次数。我见过一个同事,每次更新参数都先擦除整页,再写一个字节。结果电池寿命直接砍半。

优化建议:对于频繁更新的小数据(如计数值、配置参数),可以考虑使用“磨损均衡”算法。每次只写新位置,不擦除旧页。等整页写满了,再一次性擦除。这样能显著降低平均功耗。

3.5 Flash 编程流程示意图

下面这张图,把整个 Flash 编程的流程串起来了。你可以把它贴在工位上,写代码时对照着看。

Flash 编程与擦除流程 Flash 编程与擦除流程 开始 解锁 Flash(写入 KEY1 + KEY2) 擦除 or 编程? 擦除 编程 选择擦除模式 执行擦除(页/批量) 等待 BSY 清除 选择编程宽度 写入地址 + 数据 等待 EOP 标志 读回校验(可选) 结束 ⚠ 注意: 编程前目标页必须已擦除 地址必须对齐

3.6 实战建议

  • 先擦后写,顺序不能乱。 这是 Flash 操作的第一铁律。
  • 尽量用页擦除,少用批量擦除。 除非你确定要全片更新。
  • 编程宽度选大的。 双字编程效率最高,但要注意地址对齐。
  • 功耗敏感场景,减少擦除次数。 用磨损均衡或增量写入。
  • 校验不能省。 我遇到过 Flash 编程偶发失败的情况,读回校验能帮你提前发现问题。

好了,Flash 编程与擦除的核心内容就这些。记住:擦除是写 1,编程是写 0,先擦后写,地址对齐。这十六个字,够你应付大部分场景了。