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总线的物理层、工作模式和连接方式串起来了,方便你整体把握。

SPI总线核心知识结构 物理层(四线) • SCLK:串行时钟 • MOSI:主机输出/从机输入 • MISO:主机输入/从机输出 • CS:片选(低电平有效) 全双工通信 四种工作模式 • CPOL:时钟极性 - 0:空闲低电平 - 1:空闲高电平 • CPHA:时钟相位 - 0:第一个边沿采样 - 1:第二个边沿采样 连接方式 • 单主单从 - 直接连接,CS可常低 • 多从机-独立片选 - 每个从机独立CS • 多从机-菊花链 - MOSI/MISO串联 关键注意事项 1. 主从设备的CPOL和CPHA必须一致,否则数据采样错误 2. 多从机独立片选时,同一时刻只能有一个CS被拉低 3. 菊花链方式下所有从机必须支持该模式,且数据延迟会累积 实战经验 • 我习惯用模式0,但必须确认从机手册支持 • 单从机也建议用GPIO控制CS,避免状态机卡死 • 多从机时注意MISO高阻特性,必要时加缓冲或串联电阻

4.6 实际应用中的注意事项

SPI看起来简单,但实际项目中容易出问题的点不少。我总结几个常见的:

  1. 时钟速率匹配:从机支持的SCLK频率是有限的,别一上来就开几十兆。我见过有人用40MHz去驱动一个只支持20MHz的传感器,结果数据全是乱的。
  2. 信号完整性:SCLK频率高了之后,走线太长会有反射和振铃。SPI线最好不要超过10cm,如果非要长距离传输,考虑用RS-422转一下。
  3. 电平匹配:主机是3.3V,从机是5V,直接连会烧芯片。需要加电平转换,或者用开漏输出加外部上拉。
  4. CS毛刺:GPIO初始化时如果默认电平不对,CS可能会产生一个短暂的毛刺,导致从机误触发。我习惯在GPIO配置完成后,先拉高CS再初始化SPI外设。

调试小技巧:用逻辑分析仪抓SPI波形时,重点关注CS的下降沿和SCLK的第一个边沿之间的时间间隔。有些从机要求CS拉低后需要一段“建立时间”才能开始接收时钟,这个时间在手册里叫tCSS(CS setup time)。如果这个时间不够,第一个字节就会丢。