3、NAND Flash工作原理:存储单元结构、组织方式与操作时序
好,咱们今天聊聊NAND Flash。这东西在弹载存储系统里太常见了,说白了就是咱们的“黑匣子”核心。我刚开始接触弹载存储时,总觉得Flash就是个能存数据的芯片,后来被坑过几次才明白——不了解它的脾气,你根本玩不转。
3.1 存储单元结构:SLC、MLC、TLC
先说说最基本的存储单元。NAND Flash的存储单元本质上是一个浮栅晶体管。你可以把它想象成一个带“小仓库”的开关。电子存进“小仓库”里,阈值电压就变了,我们通过检测这个电压来判断存的是“0”还是“1”。
SLC(Single-Level Cell):一个单元存1个bit。只有两种状态:有电子(0)或没电子(1)。
- 优点:速度快、寿命长(10万次擦写)、可靠性高
- 缺点:容量小、成本高
- 我个人的习惯:弹载关键数据存储,首选SLC。为什么?因为战场上数据丢了可不是闹着玩的。
MLC(Multi-Level Cell):一个单元存2个bit。有四种电压状态:11、10、01、00。
- 优点:容量翻倍、成本降低
- 缺点:速度慢、寿命短(约1万次擦写)
- 我在项目中遇到过:某次用MLC做数据记录,结果擦写次数到了,整片Flash直接报废。嗯,教训深刻。
TLC(Triple-Level Cell):一个单元存3个bit。八种电压状态。
- 优点:容量最大、成本最低
- 缺点:速度最慢、寿命最短(约500-1000次擦写)
- 说白了,TLC就是拿寿命和速度换容量。消费级产品用用还行,弹载环境我建议慎用。
关键对比表:
| 类型 | 每单元bit数 | 电压状态数 | 典型擦写次数 | 读取速度 | 适用场景 |
|---|---|---|---|---|---|
| SLC | 1 | 2 | 10万次 | 最快 | 军工、工业控制 |
| MLC | 2 | 4 | 1万次 | 中等 | 消费级SSD |
| TLC | 3 | 8 | 500-1000次 | 最慢 | U盘、存储卡 |
⚠️ 注意:我曾经在选型时犯过一个错误——只看容量不看寿命。结果系统跑了不到一年,坏块率飙升到30%。记住:弹载系统里,可靠性永远排在容量前面。
3.2 页、块、平面的组织方式
NAND Flash的存储结构是分层的。你想想看,就像一本书:页是单页纸,块是一个章节,平面是一本书的上下册。
页(Page):读写操作的最小单位。通常大小为4KB、8KB或16KB。每个页还带有一小块额外空间(OOB,Out-Of-Band),用来存ECC校验码、坏块标记等信息。
- 读操作:以页为单位
- 写操作:也是以页为单位
- 注意:写之前必须先擦除,不能直接覆盖写
块(Block):擦除操作的最小单位。一个块包含64页、128页或256页。大小通常是256KB到几MB。
- 擦除操作:以块为单位
- 坏块管理:也是以块为单位标记
- 我建议:在驱动层做好坏块表管理,否则写到坏块上数据就丢了
平面(Plane):多个块组成一个平面。每个平面有自己的页缓存寄存器,可以独立操作。
- 多平面操作:可以同时读写多个平面,提升吞吐量
- 交叉操作:一个平面在擦除时,另一个平面可以读写
- 说白了,这就是NAND Flash的“并行加速”技术
💡 实战技巧:我在做弹载存储系统时,喜欢把数据分散到不同平面。这样即使一个平面出问题,其他平面的数据还能抢救回来。这叫“平面级冗余”。
3.3 读写擦除操作时序
这部分是硬核内容。你如果只看datasheet上的时序图,可能会一头雾水。我用自己的话给你捋一遍。
读操作时序:
- 发送读命令(00h)
- 发送5个地址周期(列地址+行地址)
- 发送确认命令(30h)
- 等待tR时间(页读取时间,通常25-50μs)
- 读取数据(从页缓存中读出)
// 伪代码示例:NAND Flash页读取
void nand_read_page(uint32_t page_addr) {
nand_send_cmd(0x00); // 读命令
nand_send_addr(page_addr); // 发送5字节地址
nand_send_cmd(0x30); // 确认命令
wait_tR(); // 等待页读取完成
nand_read_data(buffer, PAGE_SIZE); // 读取数据
}
写操作时序:
- 发送写命令(80h)
- 发送5个地址周期
- 写入数据(一页大小)
- 发送确认命令(10h)
- 等待tPROG时间(页编程时间,通常200-700μs)
- 检查状态寄存器(判断是否成功)
⚠️ 关键点:写操作前一定要检查该页所在的块是否已被擦除。我曾经在调试时忘了这步,结果数据写不进去,查了半天才发现是块没擦除。浪费时间啊。
擦除操作时序:
- 发送擦除命令(60h)
- 发送3个行地址(块地址)
- 发送确认命令(D0h)
- 等待tBERS时间(块擦除时间,通常1.5-10ms)
- 检查状态寄存器
// 伪代码示例:NAND Flash块擦除
uint8_t nand_erase_block(uint32_t block_addr) {
nand_send_cmd(0x60); // 擦除命令
nand_send_addr(block_addr); // 发送3字节块地址
nand_send_cmd(0xD0); // 确认命令
wait_tBERS(); // 等待擦除完成
return nand_check_status(); // 返回擦除结果
}
🚨 避坑指南:我曾经遇到过擦除操作超时的情况。原因是某块物理损坏,擦除命令发出去后芯片一直忙。后来我在驱动里加了超时处理——如果等待超过2倍tBERS时间,就标记为坏块。这个经验分享给你。
3.4 总结与个人经验
好了,NAND Flash的工作原理就聊到这儿。你想想看,其实核心就三点:
- 存储单元:SLC/MLC/TLC,选型时看寿命和可靠性
- 组织方式:页读写、块擦除、平面并行
- 操作时序:命令-地址-数据-等待-检查,环环相扣
我个人习惯是:在项目初期就把Flash的坏块管理、ECC校验、磨损均衡这些机制设计好。别等到板子打回来再补,那时候就晚了。
下一章咱们聊聊NAND Flash的接口协议——从并行到ONFI,再到Toggle模式。这些东西在弹载系统里怎么选、怎么用,我会结合实战案例来讲。