3. SPI Flash指令集

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 读三种典型场景。

SPI Flash 指令集交互流程 SPI Flash 指令集交互流程 系统启动 / 驱动初始化 发送 0x9F 读取 JEDEC ID 校验 Manufacturer / Type / Capacity 匹配? 错误处理 / 重试 选择指令模式 标准读 0x03 / 0x0B QSPI 读 0xEB / 0x6B MISO 串行输出数据 IO0-IO3 并行输出数据 说明 QSPI 模式需硬件支持四线功能 标准读兼容所有 SPI Flash

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 指令。