4、SPI总线基础:物理层与工作模式
SPI,全称是Serial Peripheral Interface,串行外设接口。这玩意儿在嵌入式系统里太常见了,几乎每个工程师都会用到。说白了,它就是摩托罗拉当年搞出来的一种同步串行通信协议,结构简单,速度还快。
我刚开始做嵌入式那会儿,总觉得SPI比I2C好理解。为什么?因为它信号线多,分工明确,不像I2C那样只有两根线还要搞地址仲裁。但后来发现,SPI的坑也不少,尤其是模式匹配和从机选择这块,稍不注意就翻车。
4.1 SPI物理层:四根线的故事
SPI总线通常用四根线,我习惯叫它们“四剑客”:
- SCLK(Serial Clock):串行时钟,由主机产生。没有时钟就没有同步,这是SPI的节奏器。
- MOSI(Master Out Slave In):主机输出,从机输入。数据从主机流向从机。
- MISO(Master In Slave Out):主机输入,从机输出。数据从从机流向主机。
- CS(Chip Select):片选信号,也叫SS(Slave Select)。低电平有效,谁被拉低谁就说话。
这里有个关键点:SPI是全双工通信。什么意思?就是MOSI和MISO可以同时传输数据。你发一个字节的同时,也能收到一个字节。这和I2C那种半双工完全不一样。
核心记忆点:SPI的收发是同时进行的。主机每发出一个时钟脉冲,MOSI上送出一位数据,MISO上就收回一位数据。所以SPI的收发字节数总是相等的。
我记得有一次调试一个ADC芯片,读出来的数据总是错位。查了半天才发现,我发了两个字节想读一个结果,但芯片其实需要我发三个字节才能把完整数据吐出来。嗯,这就是没搞懂SPI“发多少收多少”的机制。
4.2 四种工作模式:CPOL和CPHA
SPI有四种工作模式,由两个参数决定:CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)。
- CPOL:决定SCLK空闲时的电平。CPOL=0,空闲低电平;CPOL=1,空闲高电平。
- CPHA:决定数据采样时刻。CPHA=0,在第一个时钟边沿采样;CPHA=1,在第二个时钟边沿采样。
你想想看,这两个参数一组合,就是2×2=4种模式。很多新手在这里栽跟头,因为主从设备的模式必须一致,否则数据全是乱的。
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 |
|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿(第一个边沿) |
| 模式1 | 0 | 1 | 低电平 | 下降沿(第二个边沿) |
| 模式2 | 1 | 0 | 高电平 | 下降沿(第一个边沿) |
| 模式3 | 1 | 1 | 高电平 | 上升沿(第二个边沿) |
我个人习惯用模式0,因为大部分SPI外设默认就是模式0。但这不是绝对的,一定要看芯片手册。我曾经被一个温湿度传感器坑过,手册上写着支持模式0和模式3,结果我用了模式0死活读不出数据,换成模式3就好了。后来才发现,那个芯片的勘误表里写着“模式0下存在时序竞争问题”。
避坑指南:主从设备的CPOL和CPHA必须完全一致。不一致时,数据会在错误的边沿被采样,导致通信失败。我曾经在产线上遇到过一批板子通信不稳定,最后发现是软件工程师把模式写错了,从机要求模式2,主机配成了模式0。
4.3 单主单从连接方式
最简单的SPI连接,就是一个主机配一个从机。连接方式非常直观:
- 主机的SCLK接从机的SCLK
- 主机的MOSI接从机的MOSI
- 主机的MISO接从机的MISO
- 主机的CS接从机的CS(片选)
这种连接下,CS可以一直拉低,因为只有一个从机,不需要切换。但我不建议这么做,为什么?因为有些芯片在CS拉高时会复位内部状态机,如果你一直拉低,万一通信过程中出现异常,芯片可能卡死在某个状态。
我的习惯:即使只有一个从机,我也用GPIO控制CS,每次通信前拉低,通信完成后拉高。这样能保证从机每次都是从已知的起始状态开始工作。
4.4 多从机连接方式
多从机连接有两种主流方式:独立片选和菊花链。
4.4.1 独立片选方式
这是最常用的方式。每个从机都有自己的CS线,主机用多个GPIO分别控制。SCLK、MOSI、MISO是所有从机共享的。
- 优点:每个从机独立寻址,互不干扰,通信速度快。
- 缺点:占用主机GPIO多,从机数量受限于GPIO数量。
连接时要注意:从机的MISO是推挽输出,如果多个从机同时被选中,它们的MISO会打架。所以同一时刻只能有一个从机的CS被拉低,其他从机的MISO必须处于高阻态。大部分SPI从机芯片在CS拉高时,MISO会自动变成高阻,但有些老芯片不会,这点要特别小心。
我曾经踩过的坑:用独立片选接了三个SPI从机,其中一个老款Flash芯片在CS拉高后MISO仍然是推挽输出。结果每次和别的从机通信时,这个Flash的MISO都在干扰总线,导致数据错误。后来我在它的MISO上串了一个100Ω电阻,才把问题解决。
4.4.2 菊花链方式
菊花链方式下,所有从机共享SCLK和CS,但MOSI和MISO是串联的。数据从主机的MOSI进入第一个从机,然后从第一个从机的MISO出来,进入第二个从机的MOSI,以此类推。
- 优点:节省GPIO,只需要一个CS。
- 缺点:数据延迟大,所有从机必须同时工作,灵活性差。
菊花链方式常用于LED驱动芯片或移位寄存器这类场景。普通传感器不建议用菊花链,因为每个从机都要转发数据,通信效率会降低。
4.5 知识结构图:SPI总线核心要点
下面这张图把SPI总线的物理层、工作模式和连接方式串起来了,方便你整体把握。
4.6 实际应用中的注意事项
SPI看起来简单,但实际项目中容易出问题的点不少。我总结几个常见的:
- 时钟速率匹配:从机支持的SCLK频率是有限的,别一上来就开几十兆。我见过有人用40MHz去驱动一个只支持20MHz的传感器,结果数据全是乱的。
- 信号完整性:SCLK频率高了之后,走线太长会有反射和振铃。SPI线最好不要超过10cm,如果非要长距离传输,考虑用RS-422转一下。
- 电平匹配:主机是3.3V,从机是5V,直接连会烧芯片。需要加电平转换,或者用开漏输出加外部上拉。
- CS毛刺:GPIO初始化时如果默认电平不对,CS可能会产生一个短暂的毛刺,导致从机误触发。我习惯在GPIO配置完成后,先拉高CS再初始化SPI外设。
调试小技巧:用逻辑分析仪抓SPI波形时,重点关注CS的下降沿和SCLK的第一个边沿之间的时间间隔。有些从机要求CS拉低后需要一段“建立时间”才能开始接收时钟,这个时间在手册里叫tCSS(CS setup time)。如果这个时间不够,第一个字节就会丢。