QSPI 概述
做嵌入式这么多年,总有人问我:“QSPI 不就是 SPI 多了几根线吗?”
嗯,这么说也不算错,但远不止这么简单。
我最早接触 QSPI 是在一个需要高速读取外部 Flash 的项目上。
当时 SPI 跑 40 MHz 已经到极限了,可带宽还是不够。
换成 QSPI 后,同样 80 MHz 时钟,吞吐量直接翻了三倍多。
QSPI 到底是什么?
QSPI,全称 Queued Serial Peripheral Interface 或 Quad SPI。
说白了,就是把标准 SPI 的单根数据输出(MOSI)和数据输入(MISO),扩展成四根双向数据线(IO0~IO3)。
时钟和片选还是老样子。这意味着每个时钟周期可以传输 4 个比特,而不是 1 个。
核心差异:QSPI 保留了 SPI 的主从架构和全双工能力,但数据线从 2 根变成 4 根(双向),吞吐量提升 4 倍(同一时钟频率下)。
| 特性 | 标准 SPI | QSPI |
|---|---|---|
| 数据线数量 | 2(MOSI + MISO) | 4(IO0~IO3) |
| 传输方向 | 单向专用 | 双向复用 |
| 典型带宽(80 MHz) | 80 Mbps | 320 Mbps |
| 协议复杂度 | 低 | 中等(需配置指令+地址+空周期) |
| 常见应用 | 传感器、ADC/DAC | Flash、FPGA配置、高速缓存 |
为什么需要 QSPI?
你想想看,现在的 MCU 主频动不动几百 MHz,外设也越来越多。
系统启动时要加载代码、运行时需要存储日志,Flash 带宽很容易成为瓶颈。
SPI 跑到极限也就 50~60 Mbps,QSPI 轻松突破 300 Mbps。
我在一个无线固件升级的项目里,就因为 SPI 太慢导致升级要 8 秒,换成 QSPI 后缩到 1.5 秒,用户体验大为改观。
- 高带宽:同等时钟频率,吞吐量是 SPI 的 4 倍
- 引脚复用:只需额外 2 根线(相对 SPI 增加 2 根),性能飞跃
- 支持双倍数据率(DDR):部分控制器可在时钟上升沿和下降沿同时采样,再翻倍
- 指令包高效:一条指令即可完成地址+数据读取,减少片选开销
QSPI 的典型应用场景
最常见的是挂载 NOR Flash 或 NAND Flash,用于存储代码、配置字、字库等。
此外,FPGA 的主动配置接口也大量采用 QSPI,因为配置比特流动辄几十 MB,传输速度直接决定加载时间。
还有一些高速传感器(如摄像头模组)会利用 QSPI 将图像数据快速传给 MCU。
我曾经踩过坑:在一个双芯片方案中,我把 Flash 的 QSPI 走线布得太长而且不等长,结果 80 MHz 下读数据总是 CRC 错误。后来强制降到 40 MHz 才稳定。教训就是:高速下 PCB 等长和阻抗匹配绝不能偷懒。
QSPI 的极性相位(CPOL / CPHA)
虽然本章只是概述,但我必须提前提一嘴:QSPI 的时钟极性和相位设置完全沿袭 SPI。
CPOL 决定时钟空闲时的电平(0 或 1),CPHA 决定数据采样边沿(第一个边沿还是第二个边沿)。
组合起来就是 4 种模式。如果你不搞清楚,抓出来的数据永远是错位的。
后面我们会专门花一整个章节来细说这块,这里先记住:绝大多数 QSPI Flash 默认使用 Mode 0(CPOL=0, CPHA=0)或 Mode 3(CPOL=1, CPHA=1)。
调试小技巧:拿到一个 QSPI 器件后,先读它的状态寄存器(0x05 指令),如果读到 0xFF 或 0x00 异常值,多半是极性相位没配对。我曾经用逻辑分析仪看了半天波形才发现 CPOL 设反了,白白浪费一下午。
实例:QSPI 初始化配置片段(STM32 HAL)
/* 假设使用 Quad SPI 与 Winbond W25Q64 */
QSPI_HandleTypeDef hqspi;
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 2; // 分频系数,HCLK=160MHz 时得到 80MHz
hqspi.Init.FifoThreshold = 1;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
hqspi.Init.FlashSize = 23; // 2^23 = 8 MB
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0; // CPOL=0, CPHA=0
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
小结
QSPI 不是全新的协议,它是在 SPI 基础上做了合理的演化。
用最少的引脚换来最大的吞吐量,尤其适合嵌入式系统中大数据量的搬运。
我个人建议,只要硬件支持,尽量优先选择 QSPI 来连接 Flash 或高速外设。
当然,代价是驱动编写和调试难度略有上升——没关系,后面我们会一步步拆解。