4. SPI协议基础:SPI四线制、时钟极性/相位、数据帧格式、多从机连接

大家好,我是你们的嵌入式讲师。今天咱们来聊聊SPI,这个在传感器世界里出镜率极高的接口协议。

说实话,SPI这玩意儿,看着简单,但坑不少。我最早接触SPI是在一个加速度计项目上,当时读数据老是错位,折腾了两天才发现是时钟相位没配对。嗯,从那以后,我对SPI的每个细节都不敢马虎了。

4.1 SPI四线制:到底哪四根线?

SPI全称是Serial Peripheral Interface,串行外设接口。它最经典的就是四线制。你想想看,就四根线,却能实现全双工通信,效率很高。

这四根线分别是:

  • SCLK(Serial Clock):串行时钟,由主机产生。说白了,这根线就是心跳,决定了数据传输的节奏。
  • MOSI(Master Out Slave In):主机输出,从机输入。数据从主机流向从机。
  • MISO(Master In Slave Out):主机输入,从机输出。数据从从机流向主机。
  • SS(Slave Select):从机选择线,也叫片选线。低电平有效,用来选中你要通信的那个从机。

我个人习惯把SS叫做“点名线”。你拉低哪根SS,就是在跟哪个从机说话。其他从机呢?乖乖闭嘴听着就行。

核心要点:SPI是同步、全双工、主从式通信。主机负责产生时钟,从机被动响应。每次通信,主机和从机同时交换一个字节的数据。

4.2 时钟极性(CPOL)和时钟相位(CPHA)

这部分是SPI最容易出问题的地方。我见过太多工程师在这里翻车了。

时钟极性(CPOL),决定了SCLK空闲时的电平状态:

  • CPOL = 0:空闲时SCLK为低电平
  • CPOL = 1:空闲时SCLK为高电平

时钟相位(CPHA),决定了数据在哪个时钟边沿被采样:

  • CPHA = 0:在第一个时钟边沿(上升沿或下降沿)采样数据
  • CPHA = 1:在第二个时钟边沿采样数据

组合起来,就是四种模式:

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

我的经验:大多数传感器芯片默认使用模式0(CPOL=0, CPHA=0)。但千万别想当然。我曾经用过一个温湿度传感器,手册上写的是模式3,我按模式0去配,读回来的数据全是0xFF。后来仔细看手册才发现问题。所以,拿到一个新芯片,第一件事就是查它的SPI模式。

4.3 数据帧格式

SPI的数据帧格式,说白了就是数据怎么组织、怎么传输的。

数据位宽:最常见的是8位,也就是一个字节。但有些传感器支持16位甚至32位。比如一些高精度ADC,一次传输就是24位数据。

数据顺序:大多数SPI设备采用MSB First(最高位在前)。但也有少数设备用LSB First。这个在手册里一般会明确写出来。

典型的SPI读写操作:

以读取一个寄存器为例,通常分两步:

  1. 主机发送寄存器地址(写操作),同时从机返回无效数据
  2. 主机发送任意数据(通常是0x00或0xFF),同时从机返回寄存器内容

看个代码示例,用STM32的HAL库读取一个传感器:

// 读取传感器寄存器0x10的值
uint8_t reg_addr = 0x10;
uint8_t rx_data = 0;

// 第一步:发送地址
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);  // 拉低片选
HAL_SPI_Transmit(&hspi1, &reg_addr, 1, 100);              // 发送地址

// 第二步:读取数据
HAL_SPI_TransmitReceive(&hspi1, &dummy, &rx_data, 1, 100); // 发送dummy,接收数据
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);    // 拉高片选

注意:有些传感器在发送地址时,需要将最高位置1表示读操作。比如地址0x10,读操作要发送0x90。这个细节很容易被忽略,我就在这上面吃过亏。

4.4 多从机连接方式

一个SPI主机可以连接多个从机。怎么连?主要有两种方式。

方式一:独立片选(最常用)

每个从机都有自己的SS引脚。主机有多少个从机,就需要多少个GPIO口来控制片选。

优点:简单、可靠,从机之间互不干扰。
缺点:占用GPIO口多。

方式二:菊花链连接

所有从机共用SCLK、MOSI、MISO,但数据像串糖葫芦一样,从一个从机传到下一个从机。

这种方式不常用,主要用在一些特殊的应用场景,比如多个移位寄存器级联。普通传感器很少用菊花链。

我个人强烈建议:能用独立片选就别用菊花链。菊花链的时序要求高,调试起来很麻烦。

多从机连接的关键点:

  • 所有从机的SCLK、MOSI可以并联在一起
  • MISO线要注意:未选中的从机必须将MISO置为高阻态,否则会冲突
  • 片选线要独立控制,不能共用
  • 总线上挂的从机越多,信号质量越差,可能需要降低时钟频率

4.5 避坑指南

最后,分享几个我踩过的坑:

  • 片选时序:有些传感器要求在发送数据前,片选拉低后要等一小段时间(比如几微秒)才能开始发时钟。这个在手册里叫CS Setup Time。我遇到过不等的,数据死活读不对。
  • 时钟频率:别一上来就上最高速。先用手册推荐的速度,调通了再慢慢往上加。我见过有人用10MHz去读一个只支持1MHz的传感器,结果可想而知。
  • MISO上拉/下拉:如果从机MISO是三态输出,建议在MISO线上加一个弱上拉或下拉电阻,防止浮空时误触发。
  • 电平匹配:主机是3.3V,从机是5V?别忘了加电平转换。直接连的话,轻则读不到数据,重则烧芯片。

好了,SPI协议的基础就讲到这里。下一节我们会深入SPI的高级应用,包括DMA传输、双线模式等。有什么问题,欢迎在课后交流。

课后小练习:找一款你手边的传感器,查它的数据手册,找出它的SPI模式(CPOL/CPHA)、数据位宽、最大时钟频率。然后写一段代码,成功读取它的ID寄存器。相信我,这个练习做完,你对SPI的理解会上一个台阶。