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 如何快速判断当前模式

调试时最怕的是:不知道当前通信双方配置的是哪种模式。我教你一个“三板斧”办法:

  1. 看空闲电平:用示波器挂上SCK,不通信时量它是高还是低。高则CPOL=1,低则CPOL=0。
  2. 看第一个边沿:触发片选拉低,看SCK的第一个跳变沿。记住这个沿的极性。
  3. 看数据变化点:然后看MOSI/MISO上的数据,是在第一个边沿变化,还是在第二个边沿变化?如果数据在第一个边沿被采样(即数据在第一个边沿之前就稳定了),那就是CPHA=0;如果数据在第一个边沿之后才变化,而第二个边沿采样,那就是CPHA=1。

你想想看,只要顺序走一遍,模式就水落石出了。我每次调试新硬件,都会先干这件事,能省下至少半天瞎猜的时间。

3.5 QSPI特有的相位考量

QSPI相比普通SPI,多了四线数据总线(IO0~IO3)。但这并没有改变极性和相位的本质,只是数据线和命令线的方向变了。比如在读取数据时,主设备先发送指令和地址,然后把IO线切换成输入方向,从设备开始驱动数据。此时,时钟的极性和相位必须保持连续,不能中途更换。否则数据窗口会错位。

我建议你在设计QSPI通信时,一旦选定了模式,就不要在同一个传输过程中改变。有些MCU的QSPI外设支持动态切换极性,但那只是针对不同指令段的,非常容易踩坑。标准做法是:固定用模式0或模式3,贯穿全程。

知识结构总览

下面这张图把极性与相位的核心逻辑串起来,方便你记:

QSPI极性相位知识结构 CPOL(时钟极性) CPHA(时钟相位) CPOL=0 空闲低电平 CPOL=1 空闲高电平 CPHA=0 第一个沿采样 CPHA=1 第二个沿采样 四种模式组合 模式0(0,0) | 模式1(0,1) | 模式2(1,0) | 模式3(1,1) 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,作为“握手检验”,过了再继续后面的操作。