3. CPOL=0 与 CPOL=1 的区别:空闲时钟电平的定义、波形对比图、对从设备逻辑电平的影响

在 SPI 协议中,时钟极性(CPOL,Clock Polarity)决定了 SCLK 线在空闲状态(即无数据传输时)的逻辑电平。CPOL 只有两个取值:0 和 1。理解 CPOL 的本质是区分 SPI 模式的第一步,也是正确配置主从设备通信的基础。

3.1 空闲时钟电平的定义

空闲时钟电平是指 SPI 总线在 CS(片选)信号无效 或 两次数据传输之间,SCLK 线保持的固定逻辑电平。CPOL 直接定义了这个电平:

CPOL 值 空闲时钟电平 有效时钟边沿极性 典型应用场景
0 低电平(0V / GND) 上升沿为有效边沿(采样/移位) 多数低速传感器、EEPROM
1 高电平(VCC / 3.3V) 下降沿为有效边沿(采样/移位) 部分高速 ADC、射频模块
记忆口诀:“CPOL=0,空闲躺地上(低电平);CPOL=1,空闲挂天上(高电平)”。这个简单的物理比喻可以帮助你在调试时快速判断当前配置的极性。

3.2 波形对比图:CPOL=0 vs CPOL=1

下面两张 SVG 波形图分别展示了 CPOL=0 和 CPOL=1 在传输一个字节(0xA5,即二进制 1010 0101)时的 SCLK 与 MOSI 信号时序。注意观察空闲状态下的电平差异。

图 3-1:CPOL=0 波形(空闲低电平)

CPOL=0 波形图(空闲低电平) t0 t SCLK (CPOL=0) MOSI (0xA5) 1 0 1 0 0 1 0 1 空闲低 ↑采样

图 3-2:CPOL=1 波形(空闲高电平)

CPOL=1 波形图(空闲高电平) t0 t SCLK (CPOL=1) MOSI (0xA5) 1 0 1 0 0 1 0 1 空闲高 ↓采样
波形对比要点:
  • 空闲电平差异:CPOL=0 时 SCLK 在空闲时保持低电平;CPOL=1 时保持高电平。这是两者最直观的区别。
  • 有效边沿差异:CPOL=0 时,第一个时钟边沿是上升沿;CPOL=1 时,第一个时钟边沿是下降沿。注意:有效边沿的具体作用(采样还是移位)由 CPHA 决定,但边沿的极性由 CPOL 决定。
  • 数据线不受影响:MOSI/MISO 的数据电平由发送数据决定,与 CPOL 无关。CPOL 只改变 SCLK 的极性。

3.3 对从设备逻辑电平的影响

CPOL 的选择直接影响从设备内部逻辑的输入阈值和时序窗口。错误的 CPOL 配置会导致从设备无法正确识别时钟边沿,从而造成通信失败。

3.3.1 从设备输入阈值匹配

每个 SPI 从设备的数据手册都会明确标注其支持的 CPOL 模式。从设备内部的输入缓冲器通常设计为在特定边沿触发采样:

  • CPOL=0 设备:内部逻辑期望在 SCLK 的上升沿采样数据。如果主设备配置为 CPOL=1,从设备会在下降沿采样,导致采样到错误的数据位。
  • CPOL=1 设备:内部逻辑期望在 SCLK 的下降沿采样数据。如果主设备配置为 CPOL=0,从设备会在上升沿采样,同样导致数据错位。
常见调试陷阱:很多工程师在调试时只关注数据线(MOSI/MISO)的波形,而忽略了 SCLK 的极性。如果从设备输出数据看起来“移位”或“乱码”,首先检查 CPOL 是否与从设备数据手册一致。使用示波器同时观察 SCLK 和 MOSI,确认采样边沿是否对齐。

3.3.2 对从设备功耗的影响

CPOL 的选择还会影响从设备的静态功耗:

CPOL 配置 空闲时 SCLK 电平 对从设备输入引脚的影响 功耗考量
CPOL=0 低电平 从设备 SCLK 引脚输入为低,CMOS 输入无静态电流 低功耗场景更优
CPOL=1 高电平 从设备 SCLK 引脚输入为高,CMOS 输入无静态电流 与 CPOL=0 相当

实际上,对于 CMOS 输入,只要电平稳定在 VCC 或 GND,静态电流几乎为零。因此 CPOL 对功耗的影响微乎其微,主要考虑的还是时序兼容性。

3.3.3 对从设备复位状态的影响

某些 SPI 从设备在复位后,其内部状态机依赖于 SCLK 的初始电平来识别起始条件:

  • CPOL=0 设备:复位后 SCLK 为低,从设备等待第一个上升沿作为起始标志。
  • CPOL=1 设备:复位后 SCLK 为高,从设备等待第一个下降沿作为起始标志。

如果主设备在初始化时先输出错误的 CPOL 电平,从设备可能会误判起始条件,导致后续通信完全错乱。这也是为什么在 SPI 驱动初始化时,必须先配置 CPOL,再使能 SPI 控制器。

3.4 CPOL 配置的代码示例

以下是在 Linux 内核 SPI 控制器驱动中配置 CPOL 的典型代码片段:

/* 在 SPI 控制器配置函数中设置 CPOL */
static void spi_master_set_cpol(struct spi_master *master, u8 cpol)
{
    u32 regval;

    /* 读取当前控制寄存器 */
    regval = readl(master->base + SPI_CR);

    /* 清除 CPOL 位(假设 CPOL 位在 bit1) */
    regval &= ~SPI_CR_CPOL_MASK;

    /* 设置 CPOL 值 */
    if (cpol)
        regval |= SPI_CR_CPOL_HIGH;  /* CPOL=1: 空闲高电平 */
    else
        regval |= SPI_CR_CPOL_LOW;   /* CPOL=0: 空闲低电平 */

    /* 写入控制寄存器 */
    writel(regval, master->base + SPI_CR);

    /* 等待配置生效(某些控制器需要同步) */
    while (readl(master->base + SPI_SR) & SPI_SR_BUSY)
        cpu_relax();
}
调试建议:在驱动初始化阶段,使用示波器探头测量 SCLK 引脚,确认空闲电平是否符合预期。如果空闲电平与配置不符,检查寄存器位定义是否正确,或者控制器是否存在硬件 bug(某些早期 SPI 控制器需要额外的时钟周期才能稳定输出)。

3.5 本章小结

CPOL 定义了 SPI 时钟在空闲状态下的逻辑电平:

  • CPOL=0:空闲低电平,有效边沿为上升沿(配合 CPHA 决定采样/移位)。
  • CPOL=1:空闲高电平,有效边沿为下降沿(配合 CPHA 决定采样/移位)。