SPI时序分析:四种工作模式与核心机制
SPI通信,说白了就是主设备跟从设备之间的一场「时钟对话」。我刚开始做驱动时,总觉得时序这东西太抽象,不就是几个波形嘛。直到有一次,我调试一块ADC芯片,数据死活读不对——后来才发现,是SPI模式配错了。嗯,从那以后,我再也不敢小看时序分析了。
这一节,咱们把SPI的四种工作模式、数据采样与移位机制、片选控制策略,还有传输速率计算,一次性讲透。
SPI四种工作模式波形图
SPI有四种工作模式,由两个参数决定:时钟极性(CPOL)和时钟相位(CPHA)。CPOL决定时钟空闲时的电平,CPHA决定数据在哪个时钟边沿被采样。
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据移位边沿 |
|---|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿 | 下降沿 |
| 模式1 | 0 | 1 | 低电平 | 下降沿 | 上升沿 |
| 模式2 | 1 | 0 | 高电平 | 上升沿 | 下降沿 |
| 模式3 | 1 | 1 | 高电平 | 下降沿 | 上升沿 |
你想想看,模式0和模式3是最常用的。我个人习惯,只要从设备手册没特别说明,优先用模式0。为什么?因为很多MCU的SPI外设默认就是模式0,省事。
关键记忆法:CPOL=0时空闲低电平,CPOL=1时空闲高电平。CPHA=0时在第一个边沿采样,CPHA=1时在第二个边沿采样。
下面这张SVG图,把四种模式的波形关系画清楚了。注意看采样时刻和移位时刻的对应关系。
数据采样与移位机制
SPI的数据传输,本质上是主从设备各自有一个移位寄存器。主设备在每个时钟边沿把数据从MOSI线移出去,从设备在另一个边沿把数据从MISO线采样进来。这两个动作是同步的,所以SPI是全双工通信。
我举个例子,假设主设备要发送0xA5(二进制10100101)给从设备。在模式0下,主设备在SCK的下降沿把数据一位一位移出,从设备在SCK的上升沿采样。反过来,从设备也在同一时刻把自己的数据移出到MISO线上。
经验之谈:很多新手以为SPI是「发完再收」,其实不是。SPI的收发是同时进行的。你发一个字节的同时,也收到了一个字节。如果你只关心发送,收到的数据可以忽略;如果你只关心接收,那你也得发一个字节(通常是0x00或0xFF)来产生时钟。
移位机制的核心,就是MSB优先还是LSB优先。大部分SPI设备默认MSB优先,但有些传感器(比如某些温度传感器)用LSB优先。我踩过这个坑——有一次读一个加速度计的数据,读出来的值总是对不上,折腾了半天才发现是位序搞反了。
// MSB优先发送0xA5 (10100101)
// 发送顺序: 1 -> 0 -> 1 -> 0 -> 0 -> 1 -> 0 -> 1
for (int i = 7; i >= 0; i--) {
MOSI = (data >> i) & 0x01;
// 产生时钟边沿...
}
// LSB优先发送0xA5 (10100101)
// 发送顺序: 1 -> 0 -> 1 -> 0 -> 0 -> 1 -> 0 -> 1
for (int i = 0; i <= 7; i++) {
MOSI = (data >> i) & 0x01;
// 产生时钟边沿...
}
片选信号控制策略
片选信号(CS/SS),说白了就是主设备用来「点名」从设备的。低电平有效,拉低表示选中,拉高表示释放。
片选控制有几个关键点,我总结一下:
- 传输前拉低CS:在第一个时钟边沿之前,CS必须已经拉低。有些设备要求CS拉低后等待一小段时间(tCSS)才能开始传数据。
- 传输后拉高CS:最后一个时钟边沿之后,CS才能拉高。同样,有些设备要求最后一个时钟边沿后等待一小段时间(tCSH)。
- 多字节传输:有些设备支持连续传输多个字节时保持CS低电平(比如Flash存储器),有些设备则要求每个字节之间拉高CS(比如某些ADC)。
- 多从设备:每个从设备独立一根CS线。同一时刻只能有一个CS为低。
注意:CS信号的毛刺是常见问题。我曾经在一个项目里,CS线太长,信号反射导致CS在拉低瞬间出现了一个短暂的高电平脉冲,结果从设备以为传输结束了,数据全乱套。解决办法是在CS引脚上加一个10kΩ上拉电阻,或者降低GPIO的驱动速度。
传输速率与时钟频率计算
SPI的传输速率,直接由SCK时钟频率决定。但时钟频率不是想多高就多高,它受几个因素限制:
| 限制因素 | 说明 | 典型值 |
|---|---|---|
| 主设备最大SCK | MCU的SPI外设支持的最高频率 | 通常为系统时钟的1/2或1/4 |
| 从设备最大SCK | 从设备数据手册中规定的最高频率 | 几MHz到几十MHz不等 |
| PCB走线长度 | 走线越长,信号完整性越差,频率上限越低 | 10cm以内可跑几十MHz |
| 电容负载 | 每个从设备的引脚电容会累加 | 每个引脚约5-10pF |
传输速率的计算公式很简单:
传输速率(bps) = SCK频率(Hz) × 数据位宽(bit)
实际吞吐量(bps) = 传输速率 × (有效数据位 / 总传输位)
举例:
SCK = 10MHz, 8位数据, 无额外开销
理论速率 = 10M × 8 = 80Mbps = 10MB/s
实际吞吐量 ≈ 10MB/s(因为SPI没有协议开销)
但要注意,实际吞吐量还要考虑片选切换时间、中断响应时间、DMA配置时间等。我做过一个测试,用10MHz的SPI读一个Flash芯片,理论速率是10MB/s,但实际用CPU轮询方式只能跑到6MB/s左右,换成DMA方式才接近9MB/s。
选型建议:如果你的系统需要高速SPI通信,优先考虑:1) 使用DMA而不是CPU轮询;2) 减少CS切换次数,尽量用连续传输模式;3) 缩短PCB走线,控制在5cm以内;4) 每个从设备单独走线,不要共用一条长总线。