2. 姿态的数学描述
SPI 这东西,说白了就是四根线在跳舞。SCK、MOSI、MISO、CS,跳对了,数据就通了。我见过不少工程师,代码写得飞起,示波器一抓波形却傻了眼——数据对不上,时序全乱套。这一章,咱们就把 SPI 的时序掰开揉碎,从波形图到配置实战,一步步讲清楚。
2.1 SPI数据传输时序图:读懂波形里的“暗号”
先看一张最基础的 SPI 时序图。别怕,这东西看着复杂,其实就几个关键点。
这张图展示的是最常用的模式——CPOL=0, CPHA=0。你注意看几个地方:
- SCK(时钟线):空闲时是低电平。每个上升沿,主设备从 MOSI 上发送一位,从设备从 MISO 上回复一位。
- MOSI(主出从入):数据在时钟上升沿之前就准备好了。主设备先发 MSB(最高位),最后发 LSB(最低位)。
- MISO(主入从出):从设备同步在上升沿送出数据。主设备在同一个边沿采样。
- CS(片选):低电平有效。传输开始前拉低,结束后拉高。
2.2 主从设备通信建立过程:握手不是握一次
SPI 的通信建立,其实就三步。但每一步都有坑。
- 主设备拉低 CS:告诉从设备“我要跟你说话”。从设备收到这个下降沿,开始准备接收数据。
- 主设备输出 SCK 时钟:时钟开始跳变。从设备根据时钟边沿,在 MOSI 上读取命令或数据,同时在 MISO 上返回数据。
- 主设备拉高 CS:传输结束。从设备检测到 CS 上升沿,停止响应,回到空闲状态。
你想想看,如果主设备拉低 CS 后,SCK 还没开始跳,从设备会怎样?
大部分从设备会进入“等待”状态。但有些芯片,比如某些加速度传感器,CS 拉低后就开始采样 MISO 上的电平了。如果 MOSI 上还是高阻态,就可能读到错误数据。
2.3 时钟极性与相位配置实战:CPOL 和 CPHA 的“排列组合”
SPI 有四种模式,由 CPOL(时钟极性)和 CPHA(时钟相位)决定。很多初学者在这里绕晕了。我教你一个记忆方法:
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 常见外设 |
|---|---|---|---|---|---|
| 模式 0 | 0 | 0 | 低电平 | 上升沿 | W25Qxx Flash, ADXL345 |
| 模式 1 | 0 | 1 | 低电平 | 下降沿 | 部分 DAC 芯片 |
| 模式 2 | 1 | 0 | 高电平 | 下降沿 | 部分温度传感器 |
| 模式 3 | 1 | 1 | 高电平 | 上升沿 | SD 卡, 部分 LCD 驱动 |
怎么记?我总结了一个口诀:
- CPOL=0:空闲时时钟是低电平。就像“躺平”状态。
- CPOL=1:空闲时时钟是高电平。就像“站岗”状态。
- CPHA=0:在时钟的第一个边沿采样数据。不管是上升还是下降,反正第一个边沿就干活。
- CPHA=1:在时钟的第二个边沿采样数据。第一个边沿只是“准备”,第二个边沿才“动手”。
举个例子:模式 0(CPOL=0, CPHA=0),时钟空闲低电平,第一个边沿是上升沿,所以数据在上升沿采样。模式 3(CPOL=1, CPHA=1),时钟空闲高电平,第一个边沿是下降沿,但 CPHA=1 意味着第二个边沿才采样,所以数据在上升沿采样。
SPI_HandleTypeDef hspi1;
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_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
2.4 多从设备片选机制:让每个设备都“听话”
SPI 总线上挂多个从设备,靠的就是片选信号。每个从设备独占一根 CS 线,主设备通过拉低不同的 CS 来选择跟谁通信。
多从设备设计时,有几个关键点:
- CS 独占:每个从设备必须有自己的 CS 引脚。不能共用,否则两个设备同时响应,MISO 上就打架了。
- MISO 高阻:未选中的从设备,其 MISO 引脚必须输出高阻态(Hi-Z)。否则多个设备同时驱动 MISO,轻则数据错误,重则烧毁引脚。
- CS 切换间隔:从一个设备切换到另一个设备时,中间要留一段空闲时间。我一般至少留 1 微秒,让总线“喘口气”。
2.5 实战:用逻辑分析仪抓取 SPI 波形
理论讲完了,咱们来点实际的。假设你手头有一个 STM32 和一个 W25Q64 Flash 芯片,配置为模式 0,读取 ID 寄存器。波形应该长什么样?
- CS 拉低。
- 主设备发送 0x9F(读 ID 命令),8 个时钟周期。
- 主设备继续发送 3 个字节的 dummy 数据(0x00),同时从设备在 MISO 上返回 3 个字节的 ID。
- CS 拉高。
用逻辑分析仪抓到的波形,你应该能看到:CS 低电平期间,SCK 有 32 个完整的时钟周期。MOSI 上依次是 0x9F、0x00、0x00、0x00。MISO 上,前 8 个周期是高阻或无效数据,后 24 个周期是 ID 值。