2. CPOL与CPHA核心概念:时钟极性(CPOL)定义、时钟相位(CPHA)定义、数据采样与切换的时序关系
SPI(Serial Peripheral Interface)通信的灵活性很大程度上来源于其可配置的时钟极性(CPOL)和时钟相位(CPHA)。这两个参数共同决定了主从设备之间数据交换的时序基准。理解CPOL与CPHA是进行SPI驱动适配和调试的基础,也是避免通信失败的关键。
2.1 时钟极性(CPOL)定义
时钟极性(Clock Polarity, CPOL)定义了SPI总线在空闲状态(即没有数据传输时)下,时钟信号(SCLK)的电平状态。
- CPOL = 0:空闲状态时,SCLK为低电平。有效数据传输时,时钟信号从低电平跳变到高电平。
- CPOL = 1:空闲状态时,SCLK为高电平。有效数据传输时,时钟信号从高电平跳变到低电平。
关键理解: CPOL只决定时钟的空闲电平,不决定数据是在时钟的上升沿还是下降沿被采样。这个由CPHA决定。
硬件接口视角: 在连接从设备前,务必查阅其数据手册中关于“Clock Polarity”或“CPOL”的说明。错误的CPOL配置会导致从设备无法识别有效的时钟边沿,从而产生乱码或通信超时。
2.2 时钟相位(CPHA)定义
时钟相位(Clock Phase, CPHA)定义了数据采样(Sampling)和切换(Switching)相对于时钟边沿的时序关系。它决定了数据是在时钟的第一个边沿被采样,还是在第二个边沿被采样。
- CPHA = 0:数据在时钟的第一个边沿被采样。数据在第一个边沿之前的一个时钟周期内准备好。
- CPHA = 1:数据在时钟的第二个边沿被采样。数据在第一个边沿之后的一个时钟周期内准备好。
常见误区: 初学者常将CPHA与数据采样边沿直接等同。实际上,CPHA定义的是“第几个边沿”,而具体的边沿类型(上升沿或下降沿)由CPOL决定。例如,CPOL=0且CPHA=0时,采样发生在第一个边沿(上升沿);CPOL=0且CPHA=1时,采样发生在第二个边沿(下降沿)。
2.3 四种SPI模式(Mode)
CPOL和CPHA的组合构成了SPI的四种标准工作模式。这是驱动开发中最常遇到的配置项。
| SPI模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据切换边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平 | 上升沿(第一个边沿) | 下降沿 |
| Mode 1 | 0 | 1 | 低电平 | 下降沿(第二个边沿) | 上升沿 |
| Mode 2 | 1 | 0 | 高电平 | 下降沿(第一个边沿) | 上升沿 |
| Mode 3 | 1 | 1 | 高电平 | 上升沿(第二个边沿) | 下降沿 |
驱动适配要点: 在Linux内核中,SPI设备树节点通常通过
spi-cpol 和 spi-cpha 属性来指定模式。例如,配置Mode 0时,两个属性都不需要设置(默认值)。配置Mode 3时,需要同时设置 spi-cpol 和 spi-cpha。
2.4 数据采样与切换的时序关系
理解采样(Sampling)和切换(Switching)的时序关系是调试SPI通信的关键。主设备在时钟边沿切换数据线(MOSI/MISO),从设备在另一个边沿采样数据。
- 数据切换(Data Switching):发送方在时钟边沿改变数据线上的电平。这个边沿称为“切换边沿”。
- 数据采样(Data Sampling):接收方在时钟边沿读取数据线上的电平。这个边沿称为“采样边沿”。
采样边沿和切换边沿总是交替出现,确保数据在稳定的时间段内被读取。CPHA决定了采样边沿是第一个还是第二个时钟边沿。
2.5 时序关系总结
下表总结了CPOL和CPHA组合下,采样边沿与切换边沿的对应关系,以及数据有效窗口的位置。
| SPI模式 | CPOL | CPHA | 采样边沿 | 切换边沿 | 数据有效窗口 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 上升沿 | 下降沿 | 上升沿后到下降沿前 |
| Mode 1 | 0 | 1 | 下降沿 | 上升沿 | 下降沿后到上升沿前 |
| Mode 2 | 1 | 0 | 下降沿 | 上升沿 | 下降沿后到上升沿前 |
| Mode 3 | 1 | 1 | 上升沿 | 下降沿 | 上升沿后到下降沿前 |
调试技巧: 当SPI通信出现数据错位或第一位数据丢失时,通常是因为CPHA配置错误。尝试切换CPHA值(例如从Mode 0切换到Mode 1)往往能解决问题。使用逻辑分析仪观察SCLK和MOSI/MISO的时序关系,可以直观地验证采样边沿是否正确。
2.6 驱动开发中的CPOL/CPHA配置示例
在Linux内核中,SPI控制器驱动通过 spi_device 结构体的 mode 字段来配置CPOL和CPHA。以下是一个典型的配置代码片段:
/* Linux SPI设备配置示例 */
struct spi_device *spi;
struct spi_board_info board_info = {
.modalias = "my_spi_device",
.max_speed_hz = 1000000,
.bus_num = 0,
.chip_select = 0,
.mode = SPI_MODE_0, /* CPOL=0, CPHA=0 */
};
/* 或者动态配置 */
spi->mode = SPI_MODE_3; /* CPOL=1, CPHA=1 */
spi_setup(spi);
RTOS驱动适配: 在FreeRTOS或裸机环境中,通常通过操作SPI控制器的寄存器来配置CPOL和CPHA。例如,在STM32的HAL库中,通过
SPI_InitTypeDef 结构体的 CLKPolarity 和 CLKPhase 成员进行配置:
/* STM32 HAL库 SPI配置示例 */
SPI_HandleTypeDef hspi1;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; /* CPOL=0 */
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; /* CPHA=0 */
HAL_SPI_Init(&hspi1);