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 信号波形。图中清晰标出了数据采样点和数据切换点。
2.4 如何选择正确的 SPI 模式
选择 SPI 模式的核心依据是从设备的数据手册。以下是驱动开发中的标准排查流程:
- 查阅 Datasheet:找到从设备的 SPI 接口章节,通常会明确标注支持的 SPI 模式(如 "SPI Mode 0 and Mode 3")。
- 观察时序图:手册中的时序图会标明数据是在 SCLK 的上升沿还是下降沿被采样,以及空闲时钟电平。
- 初始化配置:在 MCU 的 SPI 外设初始化代码中,设置 CPOL 和 CPHA 寄存器位。
- 验证通信:发送一个已知的读 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 模式,提高代码的可移植性。