3. SPI Flash 指令集:标准指令集(READ/WRITE/ERASE)、四线 QSPI 指令、JEDEC ID 读取指令
SPI Flash 的指令集是驱动与硬件交互的“语言”。本章深入解析三类核心指令:标准 SPI 指令(单线)、四线 QSPI 指令(高性能)以及JEDEC ID 读取指令(设备识别)。理解这些指令的时序、格式和适用场景,是进行读写调试与性能调优的基础。
💡 核心概念: 所有 SPI Flash 操作均以“指令码 + 地址 + 数据/虚周期”的序列进行。指令码决定了操作类型,地址决定了操作位置,数据是读写的内容。QSPI 通过增加数据线数量,在相同时钟频率下将吞吐量提升至 4 倍。
3.1 标准 SPI 指令集(Single SPI)
标准 SPI 使用 MOSI(主出从入) 和 MISO(主入从出) 两条数据线进行半双工或全双工通信。这是所有 SPI Flash 芯片都支持的基础指令集,兼容性最好,但速度最慢。
3.1.1 核心指令速查表
| 指令名称 | 指令码(1 Byte) | 地址长度 | 虚周期(Dummy) | 数据方向 | 说明 |
|---|---|---|---|---|---|
| READ (读数据) | 0x03 |
3 Byte | 0 | MISO → Host | 标准读,速度较慢,通常 ≤ 50 MHz |
| Fast Read (快速读) | 0x0B |
3 Byte | 1 Byte (8 个时钟) | MISO → Host | 带虚周期,允许更高时钟频率 |
| Page Program (页编程) | 0x02 |
3 Byte | 0 | MOSI → Flash | 按页写入,通常 256 Byte/页 |
| Sector Erase (扇区擦除) | 0x20 |
3 Byte | 0 | 无数据 | 擦除 4 KB 扇区 |
| Block Erase (块擦除) | 0xD8 |
3 Byte | 0 | 无数据 | 擦除 64 KB 块 |
| Chip Erase (全片擦除) | 0xC7 / 0x60 |
0 | 0 | 无数据 | 擦除整个芯片,耗时较长 |
| Write Enable (写使能) | 0x06 |
0 | 0 | 无数据 | 执行写/擦除前必须发送 |
| Read Status Register (读状态寄存器) | 0x05 |
0 | 0 | MISO → Host | 查询 BUSY 位,判断操作是否完成 |
3.1.2 标准读指令时序(READ 0x03)
标准读指令是最简单的读操作。主机发送指令码后,紧跟 3 字节地址,Flash 在地址发送完成后立即从 MISO 线输出数据。由于没有虚周期,时钟频率受限于 Flash 内部读取速度。
// 标准读操作伪代码
void spi_flash_read(uint32_t addr, uint8_t *buf, uint32_t len) {
spi_cs_low(); // 片选拉低
spi_tx_byte(0x03); // 发送 READ 指令
spi_tx_byte((addr >> 16) & 0xFF); // 发送地址高字节
spi_tx_byte((addr >> 8) & 0xFF); // 发送地址中字节
spi_tx_byte(addr & 0xFF); // 发送地址低字节
for (uint32_t i = 0; i < len; i++) {
buf[i] = spi_rx_byte(); // 连续读取数据
}
spi_cs_high(); // 片选拉高
}
⚠️ 调试要点: 标准读指令
0x03 在高速时钟下(如 > 33 MHz)容易因信号反射或建立时间不足导致数据错位。如果遇到读回数据全为 0xFF 或 0x00,首先检查时钟频率是否过高,或尝试使用 Fast Read 指令。
3.2 四线 QSPI 指令集(Quad SPI)
QSPI 在标准 SPI 的基础上,将 MOSI、MISO 以及另外两根普通 GPIO 复用为 IO0、IO1、IO2、IO3 四根双向数据线。在数据传输阶段,每个时钟周期可以传输 4 位数据,理论吞吐量是标准 SPI 的 4 倍。
3.2.1 QSPI 指令模式
QSPI 指令通常分为三种模式,区别在于指令码、地址和数据的传输线宽:
- 模式 1:指令单线,地址/数据四线 —— 指令码通过 IO0 发送,地址和数据通过 IO0-IO3 四线传输。兼容性较好。
- 模式 2:指令/地址单线,数据四线 —— 指令和地址通过 IO0 发送,数据阶段切换到四线。常用于快速读。
- 模式 3:全四线模式 —— 指令、地址、数据全部通过四线传输。性能最高,但需要 Flash 和控制器都支持。
3.2.2 常用 QSPI 指令速查表
| 指令名称 | 指令码 | 地址模式 | 虚周期 | 数据模式 | 典型性能 |
|---|---|---|---|---|---|
| Fast Read Quad I/O | 0xEB |
四线 | 2 或 4 个时钟 | 四线 | 极高,常用于 XIP |
| Fast Read Quad Output | 0x6B |
单线 | 8 个时钟 | 四线 | 高,地址阶段兼容性好 |
| Quad Page Program | 0x32 |
单线 | 0 | 四线 | 提升写入吞吐量 |
3.2.3 QSPI 读时序(Fast Read Quad I/O 0xEB)
以 0xEB 指令为例,它采用全四线模式,是 XIP(就地执行)场景下的首选指令。时序中包含 2 个或 4 个虚周期,用于 Flash 内部准备数据。
// QSPI 读操作伪代码(假设硬件支持 QSPI 控制器)
void qspi_flash_read(uint32_t addr, uint8_t *buf, uint32_t len) {
qspi_cs_low();
qspi_send_instruction(0xEB, QSPI_LINE_1); // 指令通过单线发送
qspi_send_address(addr, QSPI_LINE_4); // 地址通过四线发送
qspi_send_dummy_cycles(2); // 发送 2 个虚周期
qspi_receive_data(buf, len, QSPI_LINE_4); // 数据通过四线接收
qspi_cs_high();
}
⚠️ 硬件调试警告: QSPI 的四根数据线 IO0-IO3 在指令和地址阶段可能切换方向。如果 PCB 走线不等长或上拉电阻配置不当,容易导致信号串扰。调试时建议先用逻辑分析仪抓取 IO0-IO3 的波形,确认数据线在“输出地址”和“输入数据”之间切换时没有毛刺。
3.3 JEDEC ID 读取指令
JEDEC ID 是 SPI Flash 芯片的“身份证”,包含制造商 ID、设备类型和容量信息。驱动初始化时,通过读取 JEDEC ID 来识别芯片型号,并加载对应的参数配置表。
3.3.1 指令格式
- 指令码:
0x9F - 地址: 无
- 返回数据: 至少 3 字节(标准 JEDEC ID),部分芯片返回更多字节(含扩展 ID)。
| 字节序号 | 名称 | 示例(Winbond W25Q64) | 说明 |
|---|---|---|---|
| Byte 0 | Manufacturer ID | 0xEF |
Winbond 的制造商代码 |
| Byte 1 | Memory Type | 0x40 |
表示 SPI Flash 系列 |
| Byte 2 | Capacity | 0x17 |
表示 64 Mbit(8 MB) |
3.3.2 读取与校验代码
// 读取 JEDEC ID 并校验
uint8_t jedec_id[3];
spi_cs_low();
spi_tx_byte(0x9F); // 发送 JEDEC ID 指令
jedec_id[0] = spi_rx_byte(); // 读取 Manufacturer ID
jedec_id[1] = spi_rx_byte(); // 读取 Memory Type
jedec_id[2] = spi_rx_byte(); // 读取 Capacity
spi_cs_high();
// 校验:以 W25Q64 为例
if (jedec_id[0] == 0xEF && jedec_id[1] == 0x40 && jedec_id[2] == 0x17) {
// 芯片识别成功,加载 W25Q64 参数
} else {
// 芯片不匹配或通信异常,进入错误处理
}
💡 调试技巧: 如果读取的 JEDEC ID 全为
0xFF,通常表示片选或时钟极性/相位(CPOL/CPHA)配置错误。如果全为 0x00,则可能是 MISO 线短路到 GND 或 Flash 未上电。建议先用示波器测量 CS 和 CLK 引脚,确认时序是否正常。
3.4 指令集对比与选择策略
在实际项目中,需要根据性能需求和硬件支持情况选择合适的指令集。以下对比表可以帮助决策:
| 对比维度 | 标准 SPI 指令 | QSPI 指令 |
|---|---|---|
| 数据线数量 | 2 根(MOSI, MISO) | 4 根(IO0-IO3) |
| 理论吞吐量(@ 100 MHz) | 12.5 MB/s | 50 MB/s |
| 兼容性 | 所有芯片都支持 | 需要芯片和控制器支持 |
| PCB 布线复杂度 | 低 | 中等(需等长匹配) |
| 典型应用场景 | 启动加载、配置存储 | XIP 执行、大文件读写 |
3.5 指令集交互流程(SVG 图)
下图展示了从系统启动到最终数据读写的完整指令交互流程,涵盖 JEDEC ID 识别、标准读和 QSPI 读三种典型场景。
3.6 调试与性能调优要点
- 指令兼容性: 不同厂商的 SPI Flash 对 QSPI 指令的支持可能略有差异。例如,某些芯片的
0xEB指令需要 4 个虚周期而非 2 个。务必查阅对应芯片的数据手册。 - 虚周期配置: 虚周期数量直接影响读性能。在保证数据稳定的前提下,尽量使用数据手册中推荐的最小虚周期值。
- 状态寄存器轮询: 在执行写或擦除指令后,必须通过
0x05指令轮询 BUSY 位,直到操作完成。不要使用固定延时,否则会浪费大量时间或导致操作未完成就进行下一步。 - 写使能前置: 每次写操作(Page Program、Erase)之前,必须发送
0x06写使能指令。如果漏掉,Flash 会忽略后续的写指令。
🔧 实战案例: 某项目在 80 MHz 时钟下使用
0x03 标准读指令,发现读取数据偶尔出现字节错位。将指令切换为 0x0B(Fast Read)并增加 1 个虚周期后,问题解决。原因是标准读没有虚周期,高速时钟下 Flash 内部数据准备时间不足。此案例说明:在高速场景下,优先使用带虚周期的 Fast Read 或 QSPI 指令。