3、SPI协议详解:SPI总线四线制、模式配置(CPOL/CPHA)、多从机通信

SPI协议,说实话,是我在嵌入式开发中用得最多的串行总线之一。它简单、高效,而且非常灵活。很多同学刚接触时觉得它不就是四根线嘛,但真到项目里调起来,各种坑就出来了。今天我就把SPI协议掰开了揉碎了讲清楚,尤其是那些容易踩坑的地方。

3.1 SPI总线四线制:到底哪四根?

SPI全称是Serial Peripheral Interface,串行外设接口。它由Motorola公司提出,后来成了事实上的工业标准。标准的SPI总线有四根信号线:

  • SCLK(Serial Clock):串行时钟,由主机产生
  • MOSI(Master Out Slave In):主机输出,从机输入
  • MISO(Master In Slave Out):主机输入,从机输出
  • SS(Slave Select):从机选择,低电平有效

嗯,这里要注意一下命名。有些厂商喜欢用SDI/SDO,或者DIN/DOUT。我个人习惯用MOSI/MISO,因为方向一目了然。你在看芯片手册时,先搞清楚哪个是输入哪个是输出,不然很容易接反。

重要提醒:SPI是全双工通信。什么意思?就是主机发数据的同时,从机也在发数据。这一点和I2C不同,I2C是半双工的。

我在项目中遇到过一件事。有个同事把MOSI和MISO接反了,结果调了一整天,数据死活不对。后来用示波器一看,才发现两根线焊反了。所以啊,画原理图时一定要仔细核对引脚定义。

3.2 模式配置:CPOL和CPHA到底怎么选?

SPI有四种工作模式,由两个参数决定:CPOL(Clock Polarity)CPHA(Clock Phase)

  • CPOL:决定时钟空闲时的电平。CPOL=0,空闲时低电平;CPOL=1,空闲时高电平。
  • CPHA:决定数据采样是在时钟的哪个边沿。CPHA=0,第一个边沿采样;CPHA=1,第二个边沿采样。

说白了,CPOL决定了时钟的"起始状态",CPHA决定了数据是"上升沿采"还是"下降沿采"。组合起来就是四种模式:

模式 CPOL CPHA 时钟空闲电平 数据采样边沿
模式0 0 0 低电平 上升沿
模式1 0 1 低电平 下降沿
模式2 1 0 高电平 下降沿
模式3 1 1 高电平 上升沿

你可能会问,到底用哪个模式?答案是:看从机的手册。每个SPI从机设备都会明确告诉你它支持哪种模式。比如常见的W25Q系列Flash,默认是模式0和模式3都支持。但有些老器件只支持模式0,你非要用模式3,它就不干活。

我的经验:如果从机手册没写清楚,先试试模式0。因为模式0是最常用的,兼容性最好。我曾经在一个项目里,因为从机手册写得不清楚,四种模式挨个试了一遍,最后发现模式0和模式3都能用,但模式0的时序裕量更大。

3.3 多从机通信:怎么接?怎么选?

SPI支持一主多从。有两种接法:

3.3.1 独立片选方式

每个从机单独一根SS线。主机有几个从机,就配几个GPIO做片选。这是最常用的方式。

// 伪代码示例:独立片选
void spi_select_slave(uint8_t slave_id) {
    // 先全部取消选中
    GPIO_ResetBits(GPIOB, GPIO_Pin_12);  // SS0
    GPIO_ResetBits(GPIOB, GPIO_Pin_13);  // SS1
    GPIO_ResetBits(GPIOB, GPIO_Pin_14);  // SS2
    
    // 选中目标从机
    switch(slave_id) {
        case 0: GPIO_SetBits(GPIOB, GPIO_Pin_12); break;
        case 1: GPIO_SetBits(GPIOB, GPIO_Pin_13); break;
        case 2: GPIO_SetBits(GPIOB, GPIO_Pin_14); break;
    }
}

3.3.2 菊花链方式

所有从机共用一根SS线,数据从第一个从机的DOUT传到第二个从机的DIN,依次串联。这种方式用得少,主要是为了节省GPIO。但缺点也很明显:延迟大,而且一个从机坏了,整条链都废了。

避坑指南:我曾经在一个项目里用菊花链接了4个ADC芯片。调试时发现,只要其中一个芯片的电源纹波大一点,整条链的数据就全乱了。后来还是改回了独立片选,虽然多用了几个GPIO,但稳定性好太多了。

3.4 实际项目中的注意事项

嗯,最后再聊几个实际项目中容易忽略的点:

  • 时钟频率:不是越快越好。从机有最大时钟限制,超了它就不认了。我一般先看手册,然后留20%的余量。
  • 信号完整性:如果SPI线走得很长(比如超过10cm),建议加个33Ω的串联电阻,减少反射。
  • 片选时序:很多从机要求在片选拉低后,等一小段时间再发时钟。这个时间叫tCSS(CS to SCK setup time),手册里都有。
  • 多从机时的MISO处理:如果多个从机共用MISO线,必须保证同一时刻只有一个从机在驱动MISO。否则就是总线冲突,轻则数据错乱,重则烧芯片。

说到MISO冲突,我想起一个案例。有个同事在设计时,忘了给从机的MISO加三态控制。结果两个从机同时被选中(片选逻辑写错了),MISO上两个信号打架,直接把主机的GPIO口烧了。所以啊,多从机设计时,一定要确保片选逻辑是互斥的。

好了,SPI协议的核心内容就这些。下一章我们开始讲具体的EEPROM和Flash芯片的驱动实现,到时候会用到今天讲的这些知识。