4. EEPROM与Flash的读写特性:擦写寿命、页写入与字节写入、磨损均衡策略
EEPROM和Flash,这两兄弟几乎撑起了单片机非易失存储的半边天。很多初学者觉得它们差不多——都能断电保存数据嘛。但实际用起来,脾气可差远了。我最早做产品时就在这上面栽过跟头,今天咱们把它们的读写特性掰开揉碎讲清楚。
4.1 擦写寿命:不是无限次随便写的
先说个扎心的事实:EEPROM和Flash都有寿命限制。不是写一次就坏,而是写到一定次数后,某个存储单元就“罢工”了。
| 存储器类型 | 典型擦写寿命 | 数据保持时间 |
|---|---|---|
| EEPROM | 10万 ~ 100万次 | 10 ~ 40年 |
| NOR Flash | 1万 ~ 10万次 | 10 ~ 20年 |
| NAND Flash | 3000 ~ 10万次 | 5 ~ 10年 |
你看,EEPROM的寿命明显比Flash长。但别高兴太早——EEPROM的容量通常只有几KB到几十KB,而Flash动不动就是几MB甚至上百MB。这就是鱼和熊掌的问题。
4.2 页写入与字节写入:速度与灵活性的博弈
EEPROM和Flash在写入方式上有个本质区别。EEPROM支持字节写入,而Flash只能页写入。什么意思呢?
- EEPROM字节写入:想改哪个字节就改哪个字节,不需要先擦除。写入时间大约3~10ms/字节。
- Flash页写入:必须先擦除整个扇区(通常256字节~4KB),然后再写入。擦除一次要几十到几百毫秒。
你想想看,如果我只想改一个字节的数据,用EEPROM直接写就行。但用Flash呢?得先把整个扇区读到RAM里,擦除扇区,修改那个字节,再把整个扇区写回去。这操作量完全不是一个级别。
4.3 磨损均衡策略:让存储单元“雨露均沾”
既然每个存储单元都有寿命限制,那怎么让它们“活”得更久?答案就是磨损均衡——把写操作均匀分布到所有存储单元上,避免某些单元过度使用。
我做过一个智能电表项目,需要每5秒记录一次电压数据。如果直接写固定地址,Flash撑不过一周。后来用了磨损均衡,愣是让设备跑了5年没出问题。
4.3.1 基本思路:地址映射 + 日志结构
磨损均衡的核心思想很简单:不直接写固定地址,而是通过一个映射表,把逻辑地址映射到物理地址。每次写入时,找下一个可用的物理地址写,同时更新映射表。
// 简单的磨损均衡示例(伪代码)
uint16_t logical_to_physical[LOGICAL_PAGES];
uint16_t next_free_page = 0;
void wear_levelling_write(uint16_t logical_addr, uint8_t *data) {
// 找到当前逻辑地址对应的物理地址
uint16_t old_phys = logical_to_physical[logical_addr];
// 标记旧页为无效
mark_page_invalid(old_phys);
// 找下一个空闲页
uint16_t new_phys = find_next_free_page();
// 写入数据
flash_page_write(new_phys, data);
// 更新映射表
logical_to_physical[logical_addr] = new_phys;
}
说白了,就是让数据在存储空间里“流动”起来,而不是死磕一个地方。
4.3.2 三种常见策略对比
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 日志结构 | 新数据追加写入,旧数据标记无效 | 实现简单,写入均匀 | 需要垃圾回收,读性能差 |
| 哈希映射 | 通过哈希函数将逻辑地址映射到物理地址 | 查找速度快 | 哈希冲突处理复杂 |
| 动态映射 | 运行时动态分配物理地址 | 灵活性高,磨损最均匀 | 需要RAM存储映射表 |
我个人比较推荐日志结构,尤其是在资源受限的单片机上。它不需要维护复杂的映射表,只需要一个写指针和一个无效标记位就行。
4.4 知识结构图:EEPROM与Flash读写特性全景
下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引,遇到问题时回来瞄一眼。
4.5 实际项目中的选择建议
说了这么多,到底怎么选?我总结了几条实战经验:
- 配置参数类数据(设备地址、校准系数等):用EEPROM。改得少,但每次改都要精确到字节。
- 日志记录类数据(运行状态、故障记录等):用Flash + 磨损均衡。数据量大,写入频繁,必须做均衡。
- 固件升级:用Flash。大块数据写入,页写入效率高。
- 掉电保护场景:优先EEPROM。字节写入原子性强,掉电不容易丢数据。