3. 极性与相位概念
咱们这一章,要把QSPI通信里最核心的两个参数讲透——时钟极性(CPOL)和时钟相位(CPHA)。很多新手调QSPI,上来就是“模式0、模式3”一通乱试,能通就行。说实话,我以前也这么干过,结果有一次在不同批次芯片间来回切换,死活连不上,后来才发现是极性相位搞反了。所以,这两个概念必须扎扎实实搞明白。
一句话总结:CPOL决定时钟空闲时的电平(高还是低),CPHA决定数据是在时钟的哪个边沿被采样(上升沿还是下降沿)。两者组合成4种模式,QSPI通常只用模式0(CPOL=0, CPHA=0)和模式3(CPOL=1, CPHA=1)。
3.1 时钟极性(CPOL)
CPOL,全称Clock Polarity。它定义的是:当没有数据传输时,SCK时钟线的电平状态。
- CPOL = 0:空闲时SCK为低电平(0V)。传输开始后,SCK从低跳变到高,再回来,反复震荡。
- CPOL = 1:空闲时SCK为高电平(VCC)。传输开始后,SCK从高跳变到低,再回来。
嗯,这里要注意:不是说CPOL=0就永远低电平,它只在空闲期是低的。一旦片选(CS)拉低开始通信,时钟就会开始翻转。我个人习惯在画时序图时,先把空闲电平画出来,一眼就能看出CPOL。
经验之谈:我遇到过一块Flash芯片,手册里写的时序图看着像CPOL=0,结果实际量出来空闲时SCK是高的。后来发现那是它内部上拉电阻惹的祸。所以别光信手册,示波器量一量最靠谱。
3.2 时钟相位(CPHA)
CPHA,Clock Phase。它决定的是:数据是在SCK的第一个边沿被采样,还是第二个边沿。
- CPHA = 0:在SCK的第一个边沿采样数据。注意,这里的“第一个”指的是从空闲状态跳变后的第一个有效沿。如果CPOL=0,第一个沿是上升沿;如果CPOL=1,第一个沿是下降沿。
- CPHA = 1:在SCK的第二个边沿采样数据。也就是空闲后第二个跳变沿。
说白了,CPHA决定了采样点是“跳变后马上捕捉”还是“等到周期过半再捕捉”。我曾经调试一个传感器时,把CPHA设反了,结果读回来的全是乱码,百思不得其解。后来用逻辑分析仪一看,数据采样点全落在数据跳变区,能不错吗?
3.3 四种模式组合
CPOL和CPHA两两组合,就产生了4种SPI/QSPI模式。QSPI主要用模式0和模式3,因为模式1和模式2在高速下容易出问题。下面这张表把四种模式的特征列清楚:
| 模式 | CPOL | CPHA | 空闲电平 | 采样边沿 | 典型应用 |
|---|---|---|---|---|---|
| 模式0 | 0 | 0 | 低 | 上升沿(第一个沿) | 绝大多数QSPI Flash、SD卡 |
| 模式1 | 0 | 1 | 低 | 下降沿(第二个沿) | 少部分外设 |
| 模式2 | 1 | 0 | 高 | 下降沿(第一个沿) | 极少数老器件 |
| 模式3 | 1 | 1 | 高 | 上升沿(第二个沿) | 很多QSPI Flash(如W25Q系列) |
避坑指南:我曾经遇到过一块FPGA和Flash芯片,手册都写模式0,结果怎么调都不通。最后用示波器看到:FPGA在片选拉低后SCK不是立刻翻转,而是先维持了半周期空闲电平。实际采样行为变成了模式1!所以,芯片内部逻辑和手册往往有差异,一定要以实测为准。
3.4 如何快速判断当前模式
调试时最怕的是:不知道当前通信双方配置的是哪种模式。我教你一个“三板斧”办法:
- 看空闲电平:用示波器挂上SCK,不通信时量它是高还是低。高则CPOL=1,低则CPOL=0。
- 看第一个边沿:触发片选拉低,看SCK的第一个跳变沿。记住这个沿的极性。
- 看数据变化点:然后看MOSI/MISO上的数据,是在第一个边沿变化,还是在第二个边沿变化?如果数据在第一个边沿被采样(即数据在第一个边沿之前就稳定了),那就是CPHA=0;如果数据在第一个边沿之后才变化,而第二个边沿采样,那就是CPHA=1。
你想想看,只要顺序走一遍,模式就水落石出了。我每次调试新硬件,都会先干这件事,能省下至少半天瞎猜的时间。
3.5 QSPI特有的相位考量
QSPI相比普通SPI,多了四线数据总线(IO0~IO3)。但这并没有改变极性和相位的本质,只是数据线和命令线的方向变了。比如在读取数据时,主设备先发送指令和地址,然后把IO线切换成输入方向,从设备开始驱动数据。此时,时钟的极性和相位必须保持连续,不能中途更换。否则数据窗口会错位。
我建议你在设计QSPI通信时,一旦选定了模式,就不要在同一个传输过程中改变。有些MCU的QSPI外设支持动态切换极性,但那只是针对不同指令段的,非常容易踩坑。标准做法是:固定用模式0或模式3,贯穿全程。
知识结构总览
下面这张图把极性与相位的核心逻辑串起来,方便你记:
3.6 代码示例:配置QSPI极性与相位
以STM32的QSPI外设为例,配置代码长这样。重点是设置 ClockPolarity 和 ClockPhase 两个字段:
// 配置QSPI句柄
QSPI_HandleTypeDef hqspi;
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 4; // 分频系数,得到实际时钟频率
hqspi.Init.FifoThreshold = 4; // FIFO阈值
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
hqspi.Init.FlashSize = POSITION_VAL(0x18); // 根据Flash容量设置
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
// 重点:极性和相位
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0; // 模式0:CPOL=0, CPHA=0
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
如果你要改成模式3,把 QSPI_CLOCK_MODE_0 换成 QSPI_CLOCK_MODE_3 即可。嗯,就是这么简单。但别因为简单就大意——我见过有人把极性配对了,相位配错,结果数据全对不上。所以每次改完配置,记得用示波器验证一下实际的采样窗口。
小技巧:调试时,抓一组已知数据(比如读Flash的ID,它通常是固定值)。如果ID读出来不对,先怀疑极性相位,别急着改其他寄存器。我一般会在初始化后先读一次ID,作为“握手检验”,过了再继续后面的操作。