2. SPI 工作模式:CPOL 与 CPHA 详解

SPI 通信的灵活性体现在其四种工作模式(Mode 0 ~ Mode 3)上。这些模式由时钟极性(CPOL)和时钟相位(CPHA)两个参数决定。理解这两个参数是正确配置 SPI 外设、确保主从设备正常通信的前提。

2.1 CPOL 与 CPHA 核心概念

  • CPOL(Clock Polarity,时钟极性):定义 SPI 总线在空闲状态(无数据传输)时,SCK 时钟信号的电平。
    • CPOL = 0:空闲时 SCK 为低电平(0)。
    • CPOL = 1:空闲时 SCK 为高电平(1)。
  • CPHA(Clock Phase,时钟相位):定义数据采样(读取)和切换(输出)相对于 SCK 时钟边沿的时刻。
    • CPHA = 0:在 SCK 的第一个边沿(上升沿或下降沿,取决于 CPOL)采样数据,在第二个边沿切换数据。
    • CPHA = 1:在 SCK 的第二个边沿采样数据,在第一个边沿切换数据。
记忆技巧:CPOL 决定“空闲时”的时钟电平;CPHA 决定“第一个边沿”是采样还是切换。通常,CPHA = 0 意味着数据在第一个时钟边沿被锁存,而 CPHA = 1 意味着数据在第二个时钟边沿被锁存。

2.2 四种工作模式(Mode 0 ~ Mode 3)

通过组合 CPOL 和 CPHA 的 0/1 状态,可以得到四种标准 SPI 模式。下表总结了各模式的配置参数和典型应用场景。

模式 CPOL CPHA 空闲时钟电平 数据采样边沿 典型应用
Mode 0 0 0 低电平 上升沿(第一个边沿) 大多数 SPI Flash、SD 卡、传感器
Mode 1 0 1 低电平 下降沿(第二个边沿) 部分 ADC、DAC 器件
Mode 2 1 0 高电平 下降沿(第一个边沿) 某些 TI 系列 DSP 外设
Mode 3 1 1 高电平 上升沿(第二个边沿) 部分 EEPROM、LCD 控制器
开发提示:在嵌入式驱动开发中,Mode 0 是最常用的模式,约 80% 的 SPI 从设备默认工作在 Mode 0。在初始化 SPI 外设前,务必查阅目标芯片的数据手册(Datasheet)确认其支持的 SPI 模式。

2.3 四种模式的时序图详解

下面的 SVG 时序图展示了在发送一个字节(0x52,二进制 0101 0010)时,四种模式下的 SCLK、MOSI 和 MISO 信号波形。图中清晰标出了数据采样点和数据切换点。

SPI 四种模式时序对比图(发送 0x52) Mode 0 (CPOL=0, CPHA=0) SCLK MOSI 采样点 Mode 1 (CPOL=0, CPHA=1) SCLK MOSI 采样点 Mode 2 (CPOL=1, CPHA=0) SCLK MOSI 采样点 Mode 3 (CPOL=1, CPHA=1) SCLK MOSI 采样点 SCLK MOSI/MISO 采样点

2.4 如何选择正确的 SPI 模式

选择 SPI 模式的核心依据是从设备的数据手册。以下是驱动开发中的标准排查流程:

  1. 查阅 Datasheet:找到从设备的 SPI 接口章节,通常会明确标注支持的 SPI 模式(如 "SPI Mode 0 and Mode 3")。
  2. 观察时序图:手册中的时序图会标明数据是在 SCLK 的上升沿还是下降沿被采样,以及空闲时钟电平。
  3. 初始化配置:在 MCU 的 SPI 外设初始化代码中,设置 CPOL 和 CPHA 寄存器位。
  4. 验证通信:发送一个已知的读 ID 命令,检查返回的数据是否正确。
常见错误:主从设备 SPI 模式不匹配是导致通信失败的常见原因。如果出现数据全为 0xFF 或 0x00,或者数据错位(例如发送 0x55 收到 0xAA),请首先检查 CPOL 和 CPHA 的配置是否与从设备一致。

2.5 模式配置代码示例(基于 STM32 HAL 库)

以下代码展示了如何在 STM32 平台上初始化 SPI1 为 Mode 0 和 Mode 3。实际开发中,请根据芯片型号调整寄存器操作。

// SPI 初始化结构体
SPI_HandleTypeDef hspi1;

// 配置为 SPI Mode 0 (CPOL=0, CPHA=0)
void SPI_Mode0_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      // CPOL = 0
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          // CPHA = 0
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    HAL_SPI_Init(&hspi1);
}

// 配置为 SPI Mode 3 (CPOL=1, CPHA=1)
void SPI_Mode3_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;     // CPOL = 1
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;          // CPHA = 1
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    HAL_SPI_Init(&hspi1);
}

2.6 四种模式适用场景总结

下表从数据采样稳定性和硬件兼容性两个维度,总结了四种模式的适用场景。

模式 数据采样稳定性 硬件兼容性 推荐场景
Mode 0 高(上升沿采样,数据在时钟低电平期间稳定) 最广泛,几乎所有 SPI 设备都支持 通用 SPI Flash、SD 卡、温湿度传感器
Mode 1 中(下降沿采样,数据在时钟高电平期间稳定) 较少见,部分 ADC 使用 高速 ADC 转换器
Mode 2 高(下降沿采样,数据在时钟高电平期间稳定) 较少见,部分 DSP 外设使用 TI DSP 外设接口
Mode 3 中(上升沿采样,数据在时钟低电平期间稳定) 较常见,部分 EEPROM 和 LCD 使用 EEPROM、LCD 控制器、部分射频模块
驱动开发最佳实践:在编写 SPI 驱动时,建议将 SPI 模式配置为可宏定义或可枚举的参数,而不是硬编码。这样在更换不同型号的 Flash 或传感器时,只需修改一个宏定义即可适配新的 SPI 模式,提高代码的可移植性。