3、数据手册精读(上):器件概述与特性、引脚配置与功能描述、存储阵列架构(Sector/Block/Page)、指令集概览(Read/Write/Erase)
拿到一颗新的NOR Flash芯片,第一件事是什么?
别急着上电,别急着写代码。先把数据手册打印出来,泡杯茶,从头到尾翻一遍。我个人的习惯是,先看器件概述和特性,快速建立整体认知。这一步走对了,后面能省下大把调试时间。
3.1 器件概述与特性:先看“身份证”
数据手册的开头几页,其实就是这颗芯片的“身份证”。它会告诉你:
- 容量:比如 16Mb、32Mb、64Mb。注意,这里的 Mb 是兆比特,不是兆字节。16Mb 换算下来是 2MB。
- 电压范围:常见的有 2.7V~3.6V,也有 1.8V 的低压版本。选型时一定要和你的系统电压匹配。
- 接口类型:SPI、QSPI、Parallel 等。现在主流是 SPI 和 QSPI。
- 工作温度:工业级一般是 -40°C 到 +85°C,车规级会更宽。
我记得有一次项目,选了一颗 3.3V 的 Flash,结果板子上的电源实际输出只有 3.0V。手册上写的最小工作电压是 2.7V,按理说没问题。但实际跑起来,高温下偶尔会读写失败。后来查了半天,发现是电源纹波太大,导致瞬间电压掉到了 2.6V 以下。嗯,这里要注意:选型时一定要留足余量,别卡着边界值选。
核心要点:器件概述部分,重点看容量、电压、接口、温度范围。这四个参数决定了这颗芯片能不能在你的系统里稳定工作。
3.2 引脚配置与功能描述:别接错线
引脚配置这部分,说白了就是告诉你每个引脚是干什么的。对于 SPI NOR Flash,引脚数量不多,但每个引脚都很关键。
以常见的 8-pin SOP 封装为例:
| 引脚编号 | 引脚名称 | 功能描述 |
|---|---|---|
| 1 | CS# | 片选信号,低电平有效。拉低时选中芯片,拉高时释放。 |
| 2 | DO (IO1) | 数据输出,在 QSPI 模式下作为 IO1。 |
| 3 | WP# (IO2) | 写保护,低电平时禁止写入。在 QSPI 模式下作为 IO2。 |
| 4 | GND | 地。 |
| 5 | DI (IO0) | 数据输入,在 QSPI 模式下作为 IO0。 |
| 6 | CLK | 时钟输入。 |
| 7 | HOLD# (IO3) | 保持信号,低电平时暂停传输。在 QSPI 模式下作为 IO3。 |
| 8 | VCC | 电源正极。 |
你想想看,如果 CS# 接错了,或者 WP# 没处理好,芯片可能根本没法正常工作。我曾经遇到过一个问题:板子上的 Flash 死活写不进去数据。查了半天,发现 WP# 引脚被拉低了,写保护生效了。所以,硬件设计时,WP# 和 HOLD# 这两个引脚,如果不使用,一定要上拉到 VCC,否则会有奇奇怪怪的问题。
个人经验:我习惯在原理图阶段,就把每个引脚的功能和注意事项标注清楚。比如 WP# 旁边加个注释:“若不使用,请上拉至 VCC”。这样 PCB Layout 的同事也不会踩坑。
3.3 存储阵列架构:Sector / Block / Page
这部分是 NOR Flash 的核心。不理解存储阵列架构,你写出来的驱动大概率是错的。
NOR Flash 的存储阵列是分层的:
- Page(页):最小的写入单位。通常是 256 字节或 512 字节。你写数据时,必须按页对齐。
- Sector(扇区):最小的擦除单位。通常是 4KB 或 64KB。擦除操作只能以 Sector 为单位进行。
- Block(块):由多个 Sector 组成。有些芯片支持 Block 擦除,擦除粒度更大。
举个例子,一颗 16Mb 的 NOR Flash,它的架构可能是这样的:
- 总容量:16Mb = 2MB
- Page 大小:256 字节
- Sector 大小:4KB(即 16 个 Page)
- Block 大小:64KB(即 16 个 Sector)
- 总 Sector 数:2MB / 4KB = 512 个
为什么会这样设计?说白了,是为了平衡擦除速度和灵活性。Sector 越小,擦除越灵活,但擦除次数多了,芯片的寿命会受影响。Block 越大,擦除速度越快,但粒度太粗,不适合小数据更新。
避坑指南:我曾经在一个项目中,需要频繁更新一小块配置数据。我直接按 Page 写入,结果发现写入失败。查了手册才知道,NOR Flash 在写入之前,必须先擦除。而擦除的最小单位是 Sector。所以,写数据前,一定要先擦除整个 Sector,然后再把整个 Sector 的数据(包括修改的部分)写回去。这就是所谓的“读-改-写”流程。
下面这张图,是我自己画的 NOR Flash 存储阵列架构图,帮你直观理解这三者的关系:
3.4 指令集概览:Read / Write / Erase
指令集是驱动开发的“操作手册”。NOR Flash 的指令集其实很简洁,核心就三类:读、写、擦除。但每个指令都有固定的时序和格式,错一个 bit 都不行。
3.4.1 读指令 (Read)
读操作是最常用的。SPI NOR Flash 的读指令有好几种:
- 0x03 (Read Data):标准读,速度较慢,适合低频场景。
- 0x0B (Fast Read):快速读,带 dummy cycle,适合高频场景。
- 0xEB (Quad Output Fast Read):四线快速读,QSPI 模式下使用,速度最快。
读操作的时序很简单:先发指令码,再发 3 字节地址,然后读取数据。对于 Fast Read,中间需要插入一个 dummy byte。
// 标准读指令示例 (0x03)
// 发送: 0x03 + 地址(3字节)
// 接收: 数据(连续读取)
uint8_t nor_read_byte(uint32_t addr) {
uint8_t cmd = 0x03;
uint8_t data;
CS_LOW();
spi_transfer(&cmd, 1); // 发送指令
spi_transfer_addr(addr); // 发送3字节地址
spi_transfer(&data, 1); // 读取1字节数据
CS_HIGH();
return data;
}
小技巧:读操作不需要先擦除,可以直接读。这也是 NOR Flash 相比 NAND Flash 的一大优势——支持 XIP(片上执行)。
3.4.2 写指令 (Write / Program)
写操作比读复杂一些。写之前,必须先执行写使能指令 (0x06),然后才能写数据。写操作以 Page 为单位。
常用的写指令:
- 0x02 (Page Program):页编程,一次最多写一个 Page。
- 0xAD (Quad Page Program):四线页编程,QSPI 模式下使用。
写操作的流程:
- 发送写使能指令 (0x06)
- 发送页编程指令 (0x02) + 地址 + 数据
- 等待芯片忙状态结束(通过读状态寄存器判断)
// 页编程示例 (0x02)
// 注意:写入前必须先擦除
uint8_t nor_page_program(uint32_t addr, uint8_t *data, uint16_t len) {
uint8_t cmd;
// 1. 写使能
cmd = 0x06;
CS_LOW();
spi_transfer(&cmd, 1);
CS_HIGH();
// 2. 页编程
cmd = 0x02;
CS_LOW();
spi_transfer(&cmd, 1); // 发送指令
spi_transfer_addr(addr); // 发送地址
spi_transfer(data, len); // 发送数据
CS_HIGH();
// 3. 等待完成
while (nor_read_status() & 0x01); // 等待 BUSY 位清零
return 0;
}
避坑指南:我曾经犯过一个低级错误——写数据前忘了发写使能指令。结果芯片毫无反应,数据写不进去。查了半天手册才发现,NOR Flash 的写操作必须由写使能指令“解锁”。所以,每次写操作前,一定要先发 0x06,这是铁律。
3.4.3 擦除指令 (Erase)
擦除操作是 NOR Flash 里最耗时的操作。擦除前,同样需要先发写使能指令。
常用的擦除指令:
- 0x20 (Sector Erase):扇区擦除,擦除一个 Sector(通常 4KB)。
- 0xD8 (Block Erase):块擦除,擦除一个 Block(通常 64KB)。
- 0xC7 (Chip Erase):整片擦除,擦除整个芯片。
擦除操作的流程:
- 发送写使能指令 (0x06)
- 发送擦除指令 + 地址(对于 Sector/Block Erase)
- 等待芯片忙状态结束
// 扇区擦除示例 (0x20)
// 擦除一个 4KB 的 Sector
uint8_t nor_sector_erase(uint32_t addr) {
uint8_t cmd;
// 1. 写使能
cmd = 0x06;
CS_LOW();
spi_transfer(&cmd, 1);
CS_HIGH();
// 2. 扇区擦除
cmd = 0x20;
CS_LOW();
spi_transfer(&cmd, 1); // 发送指令
spi_transfer_addr(addr); // 发送地址(只需 Sector 对齐的地址)
CS_HIGH();
// 3. 等待完成(擦除耗时较长,可能几十毫秒)
while (nor_read_status() & 0x01);
return 0;
}
擦除时间是个关键指标。Sector Erase 一般需要 30ms~100ms,Chip Erase 可能需要几秒钟。如果你的系统对实时性有要求,擦除操作可能会阻塞任务。我一般会在擦除期间,让 MCU 去干点别的事,或者用 DMA + 中断的方式异步等待。
总结一下:读、写、擦除是 NOR Flash 的三大核心操作。读最快,写次之,擦除最慢。写和擦除前都必须先发写使能指令。记住这些,你的驱动框架就搭起来了。
好了,这一章的内容就到这里。数据手册的精读才刚刚开始,下一章我们会深入指令集的细节,包括状态寄存器、ID 读取、以及各种时序参数。别急,慢慢来,扎实最重要。
公众号:蓝海资料掘金营,微信deep3321