2. 硬件接口基础:SPI总线协议详解(四线制、时钟极性、相位、主从模式)
SPI(Serial Peripheral Interface,串行外设接口)是嵌入式系统中应用最广泛的同步串行通信协议之一,由摩托罗拉公司在20世纪80年代中期提出。在网关设备中,SPI常用于连接高速ADC/DAC、LCD控制器、RF模块、SD卡以及各类传感器。理解SPI的四线制结构、时钟极性与相位配置以及主从模式是驱动开发的基础。
2.1 SPI四线制信号定义
标准的SPI总线采用四线制连接,每条信号线都有明确的方向和功能。下表列出了四根信号线的名称与作用:
| 信号线 | 全称 | 方向(主设备视角) | 功能描述 |
|---|---|---|---|
| SCLK | Serial Clock | 输出 | 由主设备产生的同步时钟信号,决定数据传输速率 |
| MOSI | Master Out Slave In | 输出 | 主设备发送数据到从设备的数据线 |
| MISO | Master In Slave Out | 输入 | 从设备发送数据到主设备的数据线 |
| SS | Slave Select | 输出 | 片选信号,低电平有效,用于选择与主设备通信的从设备 |
关键概念:SPI是全双工通信协议。在每一个时钟周期内,主设备通过MOSI发送一位数据,同时从设备通过MISO返回一位数据。这意味着SPI的收发操作是同步完成的,不存在单独的“发送”和“接收”阶段。
下图展示了SPI四线制总线中主设备与单个从设备的典型连接拓扑:
2.2 时钟极性与时钟相位
SPI协议的核心灵活性体现在时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)这两个配置参数上。它们共同决定了数据采样和切换的时序关系。
- CPOL(时钟极性):定义SCLK在空闲状态(无数据传输时)的电平。
- CPOL = 0:空闲时SCLK为低电平。
- CPOL = 1:空闲时SCLK为高电平。
- CPHA(时钟相位):定义数据采样发生在SCLK的哪个边沿。
- CPHA = 0:在SCLK的第一个边沿(上升沿或下降沿,取决于CPOL)采样数据。
- CPHA = 1:在SCLK的第二个边沿采样数据。
通过组合CPOL和CPHA,SPI支持四种工作模式,如下表所示:
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据切换边沿 |
|---|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿(第一个边沿) | 下降沿 |
| 模式1 | 0 | 1 | 低电平 | 下降沿(第二个边沿) | 上升沿 |
| 模式2 | 1 | 0 | 高电平 | 下降沿(第一个边沿) | 上升沿 |
| 模式3 | 1 | 1 | 高电平 | 上升沿(第二个边沿) | 下降沿 |
驱动开发常见错误:主设备和从设备的SPI模式必须完全一致。如果主设备配置为模式0(CPOL=0, CPHA=0),而从设备要求模式3(CPOL=1, CPHA=1),则通信将完全失败,数据采样会错位一个时钟周期。在Linux内核中,通过
spi_device->mode 字段设置模式,例如 SPI_MODE_0 到 SPI_MODE_3。
下图以模式0(CPOL=0, CPHA=0)为例,展示一个字节(0x53)的传输时序:
2.3 主从模式与多设备拓扑
SPI总线采用主从(Master-Slave)架构。主设备负责产生时钟信号并控制片选线,从设备被动响应。在网关硬件中,一个SPI主控制器通常需要挂载多个从设备。
多设备连接有两种常见拓扑:
- 独立片选(Independent Slave Select):每个从设备使用独立的SS线,主设备通过拉低对应SS线来选择目标设备。这是最常用的方式,支持任意数量从设备(受限于主设备GPIO数量)。
- 菊花链(Daisy Chain):所有从设备共享SS和SCLK,数据从主设备依次经过每个从设备再返回。这种方式节省引脚,但延迟较大,且要求从设备支持菊花链模式。
下图展示了网关中常见的独立片选多设备拓扑:
2.4 SPI传输流程与数据帧格式
一次完整的SPI数据传输遵循以下步骤:
- 片选激活:主设备将目标从设备的SS线从高电平拉低,通知从设备准备通信。
- 时钟启动:主设备开始产生SCLK时钟信号,频率由主设备的时钟分频器决定。
- 数据移位:在每个时钟周期内,主设备和从设备通过移位寄存器同步交换一位数据。MOSI线上的数据由主设备发出,MISO线上的数据由从设备发出。
- 帧结束:传输完指定字节数后,主设备停止SCLK,并将SS线拉高,释放从设备。
Linux内核中的SPI传输结构:在驱动开发中,SPI传输通过
struct spi_transfer 描述,支持散聚(scatter-gather)方式,即一次消息可以包含多个不连续的缓冲区。以下是一个典型的SPI写-读传输代码片段:
struct spi_transfer t[2] = {
[0] = {
.tx_buf = tx_buffer, // 发送缓冲区
.len = 4, // 发送4字节命令
},
[1] = {
.rx_buf = rx_buffer, // 接收缓冲区
.len = 16, // 接收16字节数据
},
};
struct spi_message msg;
spi_message_init(&msg);
spi_message_add_tail(&t[0], &msg);
spi_message_add_tail(&t[1], &msg);
spi_sync(spi_device, &msg); // 同步执行传输
2.5 SPI与I2C的对比(网关选型参考)
在网关外设驱动开发中,SPI和I2C是最常用的两种总线。下表从驱动开发角度对比两者的关键差异:
| 对比维度 | SPI | I2C |
|---|---|---|
| 信号线数量 | 4线(SCLK, MOSI, MISO, SS) | 2线(SCL, SDA) |
| 通信方式 | 全双工 | 半双工 |
| 最大速率 | 通常10-50 MHz,可达100 MHz+ | 标准100 kHz,快速400 kHz,高速3.4 MHz |
| 从设备选择 | 硬件片选(SS线) | 软件寻址(7位或10位地址) |
| 数据帧格式 | 无固定格式,主设备决定 | 固定格式:起始位 + 地址 + 读/写位 + 数据 + 应答位 |
| 多主设备支持 | 不支持(单主设备) | 支持(多主设备仲裁) |
| Linux内核驱动接口 | spi_sync() / spi_async() |
i2c_transfer() / i2c_smbus_*() |
| 典型网关应用 | 高速ADC、RF模块、LCD、FPGA配置 | 传感器、RTC、EEPROM、温度监控 |
网关选型建议:当外设需要高速、连续、大数据量传输(如射频数据采集、LCD刷新)时,优先选择SPI。当外设对速率要求不高,但需要多设备共享总线且引脚受限时(如多个传感器),I2C更为合适。在网关主板上,两者通常共存,分别服务于不同速率等级的外设。
2.6 本章小结
SPI总线协议的核心要点可归纳为:
- 四线制:SCLK、MOSI、MISO、SS,支持全双工同步通信。
- 四种模式:由CPOL和CPHA组合决定,主从设备必须匹配。
- 主从架构:主设备控制时钟和片选,从设备被动响应。
- 多设备拓扑:独立片选是最常用方式,菊花链适用于特定场景。
- 驱动开发要点:正确配置模式、速率和片选逻辑,使用
spi_transfer和spi_message构建传输。