QSPI Flash 概述

QSPI 协议简介

说白了,QSPI 就是 SPI 的升级版。标准 SPI 用 4 根线——SCK、CS、MOSI、MISO,一次只能传 1 位数据。QSPI 把 MOSI 和 MISO 扩展成了 4 根数据线(IO0~IO3),同样时钟下吞吐量翻 4 倍。我在一个高速数据采集项目里试过,用 80MHz 时钟跑 QSPI,读速度能到 40MB/s 左右,SPI 只能到 10MB/s。差距非常明显。

但要注意一个坑:QSPI 不是所有 Flash 都支持。芯片手册里会写“Quad SPI”或“QSPI”字样。我遇到过有人买了个普通 SPI Flash 强行配成 QSPI 模式,结果指令不识别,板子直接黑屏。

特性 SPI QSPI
数据线数量 2 (MOSI, MISO) 4 (IO0~IO3)
单次传输位数 1 bit 4 bits
典型读速度 (80MHz) ~10 MB/s ~40 MB/s
引脚数 4 (含片选) 6~8 (含片选/时钟)
兼容 SPI 模式 – 通常向下兼容
我的小习惯:项目选型时,如果目标主频超过 100MHz,我一般直接上 QSPI Flash。成本多三两毛钱,但启动时间能快 30% 以上。

Flash 芯片工作原理

Flash 存储单元实际上就是浮栅晶体管。写数据靠“隧穿效应”把电荷注入浮栅,擦除靠高电压把电荷拉走。你想想看,每擦写一次,绝缘层就会磨损一点。QSPI Flash 的擦写寿命通常在 10 万次左右。我做 bootloader 时经常反复擦写同一片区域,后来发现 Flash 坏块越来越多,才改成随机地址写入。

指令集是统一的。常用的有:

  • Read (0x03) – 普通读,速度慢但兼容性好
  • Fast Read (0x0B) – 带 dummy cycle,跑更高时钟
  • Quad Read (0x6B / 0xEB) – 四线模式读,启动初始化常用
  • Write Enable (0x06) – 每次写操作前必须先发这个
  • Page Program (0x02) – 写入数据到页缓存
  • Sector Erase (0x20 / 0xD8) – 擦除 4KB 或 64KB 块

我个人习惯把初始化过程定义为:“读 ID → 配置模式 → 读数据”三步走。每次上电后先读 JEDEC ID(指令 0x9F),确认 Flash 型号、容量,然后根据芯片手册切换到 QSPI 模式。这一步做错,后面全白费。

// QSPI 读 ID 示例(伪代码)
void qspi_read_id(uint8_t *id_buf) {
    qspi_cs_low();
    qspi_send_byte(0x9F);    // 发送读 ID 指令
    for (int i = 0; i < 3; i++) {
        id_buf[i] = qspi_read_byte();
    }
    qspi_cs_high();
}
警告:换用不同厂家的 Flash 后,必须重新配 Mode Bits(如 Macronix 用 0x38 切 QPI,Winbond 用 0x01 配状态寄存器)。我曾经因为忽略这个,新批次 Flash 上电后一直读 0xFF。

启动流程概览

系统上电后,CPU 会从默认地址取第一条指令。如果这个地址映射到 QSPI Flash,那么 Flash 控制器必须在 CPU 取指完成前完成初始化。这就是所谓的“零等待启动”——CPU 还没醒来,Flash 已经准备好了。

典型的流程分为 4 个阶段:

  1. 硬件复位与时钟起振 – 电源稳定,PLL 开始输出 QSPI 时钟
  2. QSPI 控制器初始化 – 配置 IO 复用、时序参数、中断
  3. Flash 初始化 – 读 ID、切换 Quad 模式、设置读速度(dummy cycle 数)
  4. CPU 取指启动 – 从 Flash 首地址读出 bootloader 并执行

这里有个关键点:第二步和第三步谁先谁后?我记得第一次做 SoC 方案,手册说先配控制器再操作 Flash,结果死活读不到 ID。后来发现控制器复位后默认时钟频率太低,Flash 不响应。嗯,后来我在控制器初始化里先设一个慢时钟(比如 1MHz),等 Flash 识别完再升频。

QSPI Flash 启动流程概览 上电复位 控制器初始化 Flash 初始化 读取启动代码 CPU 跳转执行 🚩 关键时序 控制器必须在 CPU 取指前 完成 Flash 搜索与配置

看了这张图应该就能明白,整个流程是串行的。任何一个节点卡住,系统就起不来。我调试过一个项目,现象是偶尔能启动偶尔不能,查了几天发现是 Flash 初始化时的 dummy cycle 数配少了,高速读命令丢字节。后来我把 dummy cycle 从 4 改成 6,再也没出过问题。

核心结论:QSPI Flash 启动初始化的本质就是“在 CPU 睁眼之前,把 Flash 的嘴巴调到最快模式,然后让 CPU 一口吞下启动代码”。控制好时钟过渡、模式切换、时序配比,就能稳定跑起来。