2. SPI 总线拓扑:单主多从连接方式、菊花链拓扑、片选信号管理、多从设备地址区分机制
2.1 单主多从连接方式(标准 SPI 拓扑)
标准 SPI 总线采用 单主多从 的星型拓扑结构。一个 SPI 主机(通常是 SoC 或 MCU)通过共享的 SCLK、MOSI、MISO 三根总线连接多个从设备,每个从设备独占一根 片选信号(CS/SS)。
关键特征: 主机通过独立片选线决定与哪个从设备通信,未选中的从设备必须将 MISO 引脚置为高阻态,避免总线冲突。
2.2 菊花链拓扑(Daisy-Chain)
菊花链拓扑通过 数据转发 方式串联多个从设备,所有从设备共享同一根片选信号。数据从主机发出后,依次经过每个从设备,直到最后一个设备将数据返回主机。
| 对比项 | 标准 SPI(独立片选) | 菊花链 SPI |
|---|---|---|
| 片选线数量 | N 根(每从设备 1 根) | 1 根(所有从设备共享) |
| 数据路径 | 主机 ↔ 从设备(点对点) | 主机 → 从设备 0 → 从设备 1 → … → 主机 |
| 从设备数量扩展 | 受限于主机 GPIO 数量 | 几乎无限制(受时序约束) |
| 传输延迟 | 低(直接通信) | 高(逐级转发) |
| 典型应用 | 通用 SPI 外设(ADC、DAC、传感器) | 移位寄存器、LED 驱动、级联 ADC |
驱动开发提示: 菊花链拓扑中,每个从设备需要支持 数据直通(Data Passthrough) 功能。在 Linux SPI 子系统中,菊花链通常通过自定义
spi_board_info 的 mode 字段或设备树中的 spi-daisy-chain 属性来配置。
2.3 片选信号管理
片选信号(Chip Select,CS)是 SPI 总线中 从设备寻址 的核心机制。主机通过拉低某个从设备的 CS 引脚来启动通信,通信结束后拉高 CS 释放总线。
2.3.1 片选信号时序要求
- CS 建立时间(CS Setup Time): CS 拉低后,必须等待至少一个时钟周期才能开始发送 SCLK,确保从设备内部逻辑稳定。
- CS 保持时间(CS Hold Time): 最后一个 SCLK 边沿后,CS 必须保持低电平至少一个时钟周期,保证数据被正确锁存。
- CS 释放时间(CS Deselect Time): 两次 SPI 传输之间,CS 必须保持高电平至少一个时钟周期,让从设备完成内部复位。
2.3.2 Linux 内核中的片选管理
在 Linux SPI 子系统中,片选信号的管理由 SPI 控制器驱动 和 SPI 核心层 共同完成:
// drivers/spi/spi.c - SPI 核心层片选管理
static int spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_message *msg)
{
struct spi_transfer *xfer;
bool cs_change = false;
int ret;
// 1. 设置片选:调用控制器驱动的 prepare_message 或 set_cs
if (msg->spi->mode & SPI_CS_HIGH)
spi_set_cs(msg->spi, true); // 高电平有效
else
spi_set_cs(msg->spi, false); // 低电平有效(默认)
// 2. 遍历传输列表
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
// 执行实际传输
ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
// ...
}
// 3. 释放片选
spi_set_cs(msg->spi, true); // 拉高 CS 结束传输
}
常见问题: 如果从设备要求 CS 在整条消息(多个 transfer)之间保持低电平,必须设置
spi_transfer->cs_change = 0。默认情况下,Linux SPI 核心会在每个 transfer 之间释放并重新拉低 CS,可能导致某些从设备通信失败。
2.4 多从设备地址区分机制
SPI 总线本身 没有定义设备地址,这与 I²C 总线不同。SPI 的多从设备区分完全依赖 硬件片选信号。但在某些场景下,也可以通过 软件寻址 或 命令帧 实现地址区分。
2.4.1 硬件片选寻址(标准方式)
| 寻址方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 独立片选 | 每个从设备独占一根 CS 引脚 | 简单、速度快、无地址冲突 | 占用 GPIO 多,扩展受限 |
| 菊花链 | 数据逐级转发,位置即地址 | 节省 GPIO,支持大量设备 | 延迟大,所有设备必须支持菊花链 |
| GPIO 扩展器 | 通过 I²C/GPIO 扩展芯片生成更多 CS | 突破 GPIO 数量限制 | 增加硬件成本,CS 切换延迟 |
2.4.2 软件命令帧寻址(无片选方式)
某些 SPI 从设备(如 RF 收发器、多通道 ADC)支持通过 命令帧中的地址字段 来区分内部寄存器或通道,所有设备共享同一根 CS:
// 示例:通过命令帧寻址的 SPI 通信
// 设备内部寄存器地址 0x3A,写入数据 0x55
uint8_t tx_buf[2] = {0x3A, 0x55}; // 地址 + 数据
uint8_t rx_buf[2] = {0, 0};
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 2,
.cs_change = 0, // 保持 CS 低电平
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
spi_sync(spi_dev, &m); // 发送地址+数据
2.4.3 设备树中的从设备地址配置
在 Linux 设备树中,SPI 从设备的 reg 属性 用于指定片选编号(即硬件地址):
// 设备树示例:SPI 总线上挂载 3 个从设备
&spi0 {
status = "okay";
cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>, // CS0
<&gpio1 11 GPIO_ACTIVE_LOW>, // CS1
<&gpio1 12 GPIO_ACTIVE_LOW>; // CS2
adc@0 { // reg = 0 对应 CS0
compatible = "ti,ads1256";
reg = <0>; // 片选编号 0
spi-max-frequency = <10000000>;
};
dac@1 { // reg = 1 对应 CS1
compatible = "ti,dac8568";
reg = <1>; // 片选编号 1
spi-max-frequency = <50000000>;
};
temp@2 { // reg = 2 对应 CS2
compatible = "maxim,max31865";
reg = <2>; // 片选编号 2
spi-max-frequency = <5000000>;
};
};
驱动开发要点: 在 Linux SPI 驱动中,通过
spi_device->chip_select 获取设备树中配置的片选编号。控制器驱动根据此编号操作对应的 GPIO 或硬件 CS 引脚。如果使用 GPIO 模拟 CS,需要在 cs-gpios 属性中声明。
2.5 拓扑选择决策树
2.6 本章小结
- 单主多从拓扑 是 SPI 最常用的连接方式,每个从设备独占片选线,MISO 必须支持高阻态。
- 菊花链拓扑 通过数据转发节省片选引脚,适用于移位寄存器、LED 驱动等场景,但增加传输延迟。
- 片选信号管理 涉及建立时间、保持时间和释放时间,Linux 内核通过
spi_set_cs()和cs_change标志控制。 - 地址区分机制 以硬件片选为主,软件命令帧寻址为辅,设备树中通过
reg属性配置片选编号。