2. Flash存储器基础:NOR Flash与NAND Flash的区别、Flash的读写特性、坏块管理策略
做嵌入式固件开发,尤其是搞Bootloader的人,天天都得跟Flash打交道。我见过不少新手,上来就调Flash驱动,结果连自己用的是NOR还是NAND都没搞清楚,最后踩坑踩到怀疑人生。这一节,咱们就把Flash的老底翻一翻。
2.1 NOR Flash与NAND Flash:两个性格迥异的兄弟
先说NOR Flash。它的名字来源于“或非”门,内部结构是并联的。你想想看,每个存储单元都直接连到数据线上,所以读数据特别快,几乎可以像访问内存一样随机读取。我在早期做BIOS的时候,代码就直接在NOR Flash里跑,不需要拷贝到RAM里,省事得很。
NAND Flash就不一样了。它的单元是串联的,像一串珠子。读数据得先把整页数据读到缓冲区,再慢慢取。写数据也是按页来,擦除却要按块来。说白了,NAND的随机读取能力很弱,但它的密度高、成本低,适合做大容量存储。
我整理了一张对比表,你一看就明白:
| 特性 | NOR Flash | NAND Flash |
|---|---|---|
| 读取方式 | 随机读取,类似RAM | 页读取,需先加载到缓冲区 |
| 写入方式 | 按字节/字写入 | 按页写入 |
| 擦除方式 | 按扇区擦除(通常64KB~128KB) | 按块擦除(通常128KB~1MB) |
| 容量密度 | 低(通常≤512MB) | 高(可达数TB) |
| 坏块管理 | 基本不需要,出厂坏块极少 | 必须管理,出厂就有坏块 |
| 典型应用 | Bootloader、代码存储 | 文件系统、大容量数据 |
| 成本 | 高 | 低 |
核心结论:Bootloader通常放在NOR Flash里,因为要随机执行代码。NAND Flash更适合存固件镜像、文件系统这些大块数据。
2.2 Flash的读写特性:别拿它当内存用
Flash有个很坑爹的特性:写之前必须先擦除。你想想看,内存可以随便覆盖,但Flash不行。它只能把1变成0,不能把0变成1。要把0变回1,就得擦除整个扇区或块。
我记得有一次,我在项目里直接往Flash里写数据,没先擦除,结果读出来全是乱码。排查了半天才发现,原来那个地址之前写过数据,位已经变成0了,再写也改不回来。嗯,从那以后,我每次写Flash前都会先检查一下是否需要擦除。
具体来说,Flash的读写操作分三步:
- 擦除(Erase):把整个扇区或块的所有位变成1。NOR按扇区擦,NAND按块擦。
- 写入(Program):把需要的位从1变成0。NOR可以按字节写,NAND必须按页写。
- 读取(Read):直接读数据。NOR可以随机读,NAND需要先读整页到缓冲区。
这里有个关键点:擦除次数是有限的。NOR Flash通常能擦10万次,NAND Flash只有几千到几万次。所以,别没事就擦着玩,否则Flash很快就报废了。
我的习惯:在Bootloader里,我会把频繁更新的数据(比如配置参数)放在一个独立的扇区里,并且做磨损均衡。说白了,就是轮流使用不同的扇区,别逮着一个扇区死磕。
2.3 坏块管理策略:NAND Flash的必修课
NOR Flash基本没有坏块问题,出厂时坏块极少,而且不会在使用中突然冒出来。但NAND Flash就不一样了。它出厂时就可能有坏块,而且随着擦写次数增加,坏块会越来越多。
我曾经在一个项目里,用了某款NAND Flash,结果写到一半突然报错。查了手册才知道,那个块已经坏了。从那以后,我再也不敢忽略坏块管理了。
坏块管理通常有两种策略:
- 坏块跳过(Bad Block Skipping):在写入时,如果发现某个块是坏的,就跳过它,写到下一个好块。这种方法简单,但会浪费空间。
- 坏块替换(Bad Block Replacement):在Flash的保留区域里预留一些好块,当发现坏块时,用保留块替换它。这种方法更高效,但需要维护一张映射表。
我建议在Bootloader里至少实现坏块跳过。因为Bootloader的代码量不大,坏块跳过的实现成本最低,而且足够可靠。
注意:NAND Flash的坏块标记通常放在每个块的第1页或最后1页的特定位置(比如第0字节)。在擦除或写入前,一定要先检查坏块标记。我曾经见过有人没检查,结果把数据写到了坏块上,导致系统启动失败。
2.4 知识体系总览
下面这张图,把这一节的核心逻辑串起来了。你可以把它当作一个快速参考:
2.5 实战建议:选型与初始化
在实际项目中,我一般这样选型:
- Bootloader代码:放在NOR Flash里,容量小(通常1MB以内),但要求随机执行。
- 固件镜像:放在NAND Flash里,容量大(几百MB到几GB),支持OTA升级。
- 配置参数:放在NOR Flash的独立扇区里,方便频繁更新。
初始化Flash时,我建议按以下步骤来:
- 读取Flash的ID,确认型号和容量。
- 检查坏块标记,建立坏块表。
- 初始化读写缓冲区(尤其是NAND Flash)。
- 验证第一个扇区/块是否可读可写。
一个小技巧:在Bootloader里,我会把坏块表放在RAM里,每次启动时重新扫描。这样既省了Flash空间,又避免了坏块表本身被写坏的风险。
好了,这一节的内容就到这里。Flash的基础打牢了,后面讲分区部署的时候,你就能理解为什么Bootloader要放在特定位置,为什么分区表要预留坏块替换区。这些东西,都是实践中一点点踩坑踩出来的经验。