第3章:NAND Flash操作原理:读、写、擦的基本流程与时序
好,咱们今天聊聊NAND Flash最核心的三个操作——读、写(编程)、擦除。这三个操作,说白了就是NAND Flash的命根子。你搞懂了它们,后面坏块管理、ECC纠错什么的,理解起来就顺了。
我记得刚入行那会儿,第一次看NAND Flash的datasheet,满眼都是时序图,看得我头皮发麻。后来做项目踩了几次坑,才真正明白这些时序参数背后意味着什么。今天我就把这些经验掰开揉碎了讲给你听。
3.1 读操作:从存储单元中取数据
读操作,就是把数据从存储单元里取出来。听起来简单,但内部流程其实挺讲究的。
3.1.1 读操作的基本流程
读操作大致分三步走:
- 发送命令和地址:先告诉芯片“我要读数据”,再告诉它“从哪个地址读”。
- 等待数据就绪:芯片内部开始干活,把数据从存储阵列搬到页寄存器里。这段时间叫tR(Read Latency)。
- 读取数据:数据准备好了,你从页寄存器里把数据取出来。
我习惯把读操作比作去图书馆借书。你告诉管理员书名(命令),再告诉他书架位置(地址),管理员去书架上找(tR时间),找到后把书递给你(数据输出)。
3.1.2 读操作的命令序列
以常见的NAND Flash为例,读一页数据的命令序列是这样的:
// 读一页数据(典型命令序列)
// 假设页大小是2KB,列地址用2个字节,行地址用3个字节
CLE = 1, CE = 0, WE = 0 // 进入命令模式
发送命令 0x00 // 读操作起始命令
CLE = 0, ALE = 1 // 进入地址模式
发送列地址低字节 (CA0-CA7)
发送列地址高字节 (CA8-CA15)
发送行地址低字节 (RA0-RA7)
发送行地址中字节 (RA8-RA15)
发送行地址高字节 (RA16-RA23)
CLE = 1, ALE = 0 // 再次进入命令模式
发送命令 0x30 // 读操作确认命令
// 等待 tR 时间(通常 25-50μs)
// 检查 R/B# 引脚,等待其变高
// 数据输出阶段
CLE = 0, ALE = 0, RE = 0 // 进入数据读取模式
循环读取数据,每次RE下降沿输出一个字节
嗯,这里要注意:不同厂商、不同型号的NAND Flash,命令码可能不一样。比如有些芯片用0x00/0x30,有些用0x05/0xE0。做项目时一定要先看datasheet,别想当然。
3.1.3 读操作的时序参数
读操作有几个关键时序参数,我列个表给你看:
| 参数 | 说明 | 典型值 | 我的经验 |
|---|---|---|---|
| tR | 读操作延迟,从命令发出到数据就绪 | 25-50μs | 这个时间跟工艺有关,越新的工艺tR越短 |
| tRC | 读周期时间,两次连续读操作的最小间隔 | 20-30ns | 别卡太紧,留点余量,我吃过亏 |
| tREA | RE#下降沿到数据输出的延迟 | 15-20ns | 这个参数决定了你的读速度上限 |
| tRP | RE#脉冲宽度 | 10-15ns | 太短了数据不稳定,太长了影响速度 |
3.2 写操作(编程):把数据写入存储单元
写操作,也叫编程操作。跟读操作不一样,写操作只能把1变成0,不能把0变成1。想恢复成1?那得靠擦除操作。
3.2.1 写操作的基本流程
写操作分四步:
- 发送命令和地址:告诉芯片“我要写数据”和“写到哪里”。
- 写入数据:把要写的数据一字节一字节地送进页寄存器。
- 发送编程确认命令:告诉芯片“数据准备好了,开始写吧”。
- 等待编程完成:芯片内部开始编程,这段时间叫tPROG(Program Latency)。
你想想看,写操作比读操作多了一步——你得先把数据送进去,再让芯片去写。这就像你写作业,得先想好答案(数据),再写到本子上(存储单元)。
3.2.2 写操作的命令序列
// 写一页数据(典型命令序列)
CLE = 1, CE = 0, WE = 0 // 进入命令模式
发送命令 0x80 // 写操作起始命令
CLE = 0, ALE = 1 // 进入地址模式
发送列地址低字节
发送列地址高字节
发送行地址低字节
发送行地址中字节
发送行地址高字节
CLE = 0, ALE = 0 // 进入数据输入模式
循环写入数据,每次WE上升沿锁存一个字节
CLE = 1, ALE = 0 // 再次进入命令模式
发送命令 0x10 // 写操作确认命令
// 等待 tPROG 时间(通常 200-800μs)
// 检查 R/B# 引脚,等待其变高
// 检查状态寄存器,确认编程是否成功
发送命令 0x70
读取状态寄存器
检查 bit6(编程成功标志位)
3.2.3 写操作的时序参数
| 参数 | 说明 | 典型值 | 我的经验 |
|---|---|---|---|
| tPROG | 编程时间,从确认命令到编程完成 | 200-800μs | 这个时间跟页大小和工艺有关,MLC比SLC慢很多 |
| tWC | 写周期时间,两次连续写操作的最小间隔 | 20-30ns | 跟tRC类似,别卡太紧 |
| tWP | WE#脉冲宽度 | 10-15ns | 太窄了数据可能锁存不上 |
| tADL | 地址到数据输入的延迟 | 70-100ns | 地址发完后,别急着发数据,等一会儿 |
3.3 擦除操作:把存储单元恢复成1
擦除操作,就是把存储单元里的数据全部变成1。注意,擦除是按块(Block)进行的,不是按页。一个块通常包含64页或128页。
3.3.1 擦除操作的基本流程
擦除操作分三步:
- 发送擦除命令和块地址:告诉芯片“我要擦除哪个块”。
- 发送擦除确认命令:确认你要擦除。
- 等待擦除完成:芯片内部开始擦除,这段时间叫tBERS(Block Erase Latency)。
说白了,擦除操作就是给整个块“格式化”。我经常跟团队说,擦除操作是NAND Flash里最耗时的操作,能少擦就少擦。
3.3.2 擦除操作的命令序列
// 擦除一个块(典型命令序列)
CLE = 1, CE = 0, WE = 0 // 进入命令模式
发送命令 0x60 // 擦除操作起始命令
CLE = 0, ALE = 1 // 进入地址模式
发送行地址低字节 (RA0-RA7)
发送行地址中字节 (RA8-RA15)
发送行地址高字节 (RA16-RA23)
// 注意:擦除只需要行地址,不需要列地址
CLE = 1, ALE = 0 // 再次进入命令模式
发送命令 0xD0 // 擦除操作确认命令
// 等待 tBERS 时间(通常 1-3ms)
// 检查 R/B# 引脚,等待其变高
// 检查状态寄存器,确认擦除是否成功
发送命令 0x70
读取状态寄存器
检查 bit6(擦除成功标志位)
3.3.3 擦除操作的时序参数
| 参数 | 说明 | 典型值 | 我的经验 |
|---|---|---|---|
| tBERS | 块擦除时间 | 1-3ms | 这是三个操作里最慢的,所以尽量少擦 |
| tWB | 写缓冲时间,命令发出到R/B#变低 | 50-100ns | 命令发完后,别急着查状态,等一会儿 |
3.4 三个操作的对比与避坑指南
我把三个操作放在一起对比一下,方便你理解:
| 操作 | 最小单位 | 典型耗时 | 数据变化 | 注意事项 |
|---|---|---|---|---|
| 读 | 页 | 25-50μs | 不变 | 注意tR时间,别提前读数据 |
| 写(编程) | 页 | 200-800μs | 1→0 | 必须检查状态寄存器 |
| 擦除 | 块 | 1-3ms | 全部变1 | 只能按块擦除,不能按页 |
另外,我建议你在做驱动时,把这三个操作封装成独立的函数。比如:
// 伪代码示例
int nand_read_page(uint32_t page_addr, uint8_t *buffer);
int nand_write_page(uint32_t page_addr, uint8_t *buffer);
int nand_erase_block(uint32_t block_addr);
这样上层代码调用起来就清爽多了。而且每个函数里都加上超时处理,防止芯片卡死。嗯,这个习惯我保持了很多年,帮我省了不少调试时间。
好了,这一章的内容就到这里。读、写、擦这三个操作,是NAND Flash的基石。你把这章吃透了,后面讲坏块管理、磨损均衡什么的,就轻松多了。