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上的时序图,可能会一头雾水。我用自己的话给你捋一遍。

读操作时序:

  1. 发送读命令(00h)
  2. 发送5个地址周期(列地址+行地址)
  3. 发送确认命令(30h)
  4. 等待tR时间(页读取时间,通常25-50μs)
  5. 读取数据(从页缓存中读出)
// 伪代码示例: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); // 读取数据
}

写操作时序:

  1. 发送写命令(80h)
  2. 发送5个地址周期
  3. 写入数据(一页大小)
  4. 发送确认命令(10h)
  5. 等待tPROG时间(页编程时间,通常200-700μs)
  6. 检查状态寄存器(判断是否成功)

⚠️ 关键点:写操作前一定要检查该页所在的块是否已被擦除。我曾经在调试时忘了这步,结果数据写不进去,查了半天才发现是块没擦除。浪费时间啊。

擦除操作时序:

  1. 发送擦除命令(60h)
  2. 发送3个行地址(块地址)
  3. 发送确认命令(D0h)
  4. 等待tBERS时间(块擦除时间,通常1.5-10ms)
  5. 检查状态寄存器
// 伪代码示例: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模式。这些东西在弹载系统里怎么选、怎么用,我会结合实战案例来讲。