QSPI 读写时序详解
1. 时钟相位:CPOL 与 CPHA
做 QSPI 驱动,第一步就是把时钟极性和相位搞明白。
我遇到过不少工程师,明明代码逻辑都对,数据就是错位,最后发现是时钟相位设反了。
QSPI 的时钟由两个参数决定:
- CPOL(Clock Polarity):空闲时时钟是高电平还是低电平。CPOL=0 表示空闲低,CPOL=1 表示空闲高。
- CPHA(Clock Phase):数据在时钟的哪个边沿采样。CPHA=0 表示第一个边沿采样,CPHA=1 表示第二个边沿采样。
组合起来有四种模式,但实际 QSPI 器件最常用的是 模式0(CPOL=0, CPHA=0) 和 模式3(CPOL=1, CPHA=1)。为什么?因为这两种模式的数据采样边沿都在时钟的上升沿,主控和从机容易对齐。
| 模式 | CPOL | CPHA | 空闲电平 | 采样边沿 |
|---|---|---|---|---|
| 0 | 0 | 0 | 低 | 上升沿 |
| 1 | 0 | 1 | 低 | 下降沿 |
| 2 | 1 | 0 | 高 | 下降沿 |
| 3 | 1 | 1 | 高 | 上升沿 |
2. 单线模式(Standard SPI)
单线模式就是传统 SPI。一条 MOSI 发命令,一条 MISO 回数据,一个时钟周期传 1 bit。
命令帧结构很简单:
| 8-bit 指令 | 24/32-bit 地址 | …… | 8-bit 数据(写) / 等待+读(读) |
写操作:主控在 MOSI 上依次发出指令、地址、数据。每个 bit 都在时钟的采样边沿被从机锁存。
读操作:主控发出指令和地址后,需要插入一个空周期让总线方向切换(从机从接收转为发送),然后从机在 MISO 上逐位输出数据。
3. 双线模式(Dual SPI)
双线模式很有意思。命令阶段仍然用单线,但数据阶段同时使用两根线(IO0 和 IO1)传输,每个时钟传 2 bit。
帧结构变成:
| 8-bit 指令(单线) | 24/32-bit 地址(单线) | 空周期 | 数据(双线,每时钟2bit) |
双线的地址依旧走单线,因为器件在开始阶段还不知道要切换成双线。指令里有一个 “Quad Enable” 位,告诉 Flash 后续阶段用双线还是四线。注意:地址阶段不能切换到双线,否则主控和从机对不齐。
| 特性 | 单线 | 双线 | 四线 |
|---|---|---|---|
| 数据带宽 | 1 bit/时钟 | 2 bit/时钟 | 4 bit/时钟 |
| 命令阶段线宽 | 1 线 | 1 线 | 1 线 / 4 线(取决于指令) |
| 地址阶段线宽 | 1 线 | 1 线 | 1 线 / 4 线 |
| 数据阶段线宽 | 1 线 | 2 线 | 4 线 |
| 典型读指令 | 0x03 | 0xBB (Fast Read Dual) | 0xEB (Fast Read Quad) |
4. 四线模式(Quad SPI)
四线才是真正的“QSPI”。数据阶段用四条线(IO0~IO3),一个时钟传 4 bit,速度直接翻倍。配合 DMA 时,读性能可以接近理论极限。
典型四线读指令(0xEB)的帧结构:
| 8-bit 指令 (单线) | 24-bit 地址 (单线) | 8-bit 空周期 | 数据 (四线,每时钟4bit) |
注意那个 空周期(Dummy Cycles)。为什么需要它?
- 给 Flash 内部地址译码和读操作留时间。
- 更重要的是:让总线从“输出方向”(主控发地址)切换到“输入方向”(从机发数据)。如果省略空周期,数据的前几个 bit 可能被主控自己的地址信号冲掉。
空周期的数量由 Flash 芯片决定,常见是 4~8 个时钟。你在 DMA 配置里要精确设置 dummy 个数,多一个或少一个都会导致整帧错位。
// QSPI 读命令配置示例(以 STM32 为例)
qspi.CommandInstruction = 0xEB; // Fast Read Quad
qspi.CommandMode = QSPI_CMD_ONE_LINE;
qspi.AddressSize = QSPI_ADDRESS_24_BIT;
qspi.AddressMode = QSPI_ADDR_ONE_LINE;
qspi.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi.DataMode = QSPI_DATA_FOUR_LINES;
qspi.DummyCycles = 6; // 与 Flash 手册一致
qspi.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
5. 时序全貌对比
下面这张图用 SVG 画出了单线、双线、四线模式下的帧结构对比,帮你一眼看清区别:
6. 避坑指南 · 经验总结
- 时钟相位一定要验证:我建议用 SPI Flash 的“读状态寄存器”命令(0x05)先发再读,看返回值是不是 0x00。如果读出来全是 0xFF 或 0x00,大概率是相位搞反了。
- 空周期别想当然:有些国产 Flash 的 dummy 周期会随工作电压变化。我曾经在 3.3V 下设置 6 个 dummy 正常,换到 2.8V 就必须改成 8 个。所以产品量产前要做电压拉偏测试。
- 四线模式先读 ID:每次初始化先发 0x9F 读 JEDEC ID,用单线模式读。如果 ID 正确,再使能 QE 位切换到四线。这个顺序能帮你区分是硬件连线问题还是协议问题。
- DMA 配置不要漏 dummy:DMA 的字节计数必须包含 dummy 周期占用的时钟数。例如用 6 个 dummy,QSPI 控制器会在数据阶段前自动插入 6 个时钟,你传给 DMA 的数据缓存起始地址要跳过 dummy 占用的时间窗口。具体怎么做?看控制器的参考手册的“Dummy Cycles”寄存器。
掌握了单线、双线、四线的时序差异和避坑要点,你再去配置 QSPI DMA 就会清晰很多。剩下的就是根据 Flash 手册塞对寄存器值了。