3. Flash 编程与擦除:标准流程、页擦除、批量擦除与编程模式
Flash 操作,说白了就是两件事:擦除和编程。擦除是把 1 写进去,编程是把 0 写进去。这个顺序不能乱,必须先擦后写。我刚开始做项目时,就因为没注意这个顺序,折腾了一整天。
3.1 标准编程流程
不管是哪家厂商的 MCU,Flash 编程流程都大同小异。我以 STM32 为例,给你拆解一下标准步骤:
- 解锁 Flash —— 写入解锁序列(KEY1 + KEY2),防止误操作。
- 检查忙标志 —— 确认 Flash 控制器空闲(BSY = 0)。
- 配置编程宽度 —— 选择半字、字或双字模式。
- 写入目标地址 —— 地址必须对齐(半字对齐、字对齐等)。
- 写入数据 —— 硬件自动启动编程。
- 等待完成 —— 轮询 BSY 位或等待 EOP 中断。
- 校验数据 —— 读回写入值,确认无误。
- 锁定 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 编程的流程串起来了。你可以把它贴在工位上,写代码时对照着看。
3.6 实战建议
- 先擦后写,顺序不能乱。 这是 Flash 操作的第一铁律。
- 尽量用页擦除,少用批量擦除。 除非你确定要全片更新。
- 编程宽度选大的。 双字编程效率最高,但要注意地址对齐。
- 功耗敏感场景,减少擦除次数。 用磨损均衡或增量写入。
- 校验不能省。 我遇到过 Flash 编程偶发失败的情况,读回校验能帮你提前发现问题。
好了,Flash 编程与擦除的核心内容就这些。记住:擦除是写 1,编程是写 0,先擦后写,地址对齐。这十六个字,够你应付大部分场景了。