2. SPI 总线拓扑:单主多从连接方式、菊花链拓扑、片选信号管理、多从设备地址区分机制

2.1 单主多从连接方式(标准 SPI 拓扑)

标准 SPI 总线采用 单主多从 的星型拓扑结构。一个 SPI 主机(通常是 SoC 或 MCU)通过共享的 SCLK、MOSI、MISO 三根总线连接多个从设备,每个从设备独占一根 片选信号(CS/SS)。

关键特征: 主机通过独立片选线决定与哪个从设备通信,未选中的从设备必须将 MISO 引脚置为高阻态,避免总线冲突。
单主多从 SPI 拓扑(独立片选) SPI 主机 从设备 0 从设备 1 从设备 2 SCLK / MOSI / MISO CS0 CS1 CS2 ⚠ 总线冲突风险 未选中从设备的 MISO 引脚必须为高阻态(Hi-Z) 否则多个从设备同时驱动 MISO 会导致信号冲突、数据损坏

2.2 菊花链拓扑(Daisy-Chain)

菊花链拓扑通过 数据转发 方式串联多个从设备,所有从设备共享同一根片选信号。数据从主机发出后,依次经过每个从设备,直到最后一个设备将数据返回主机。

对比项 标准 SPI(独立片选) 菊花链 SPI
片选线数量 N 根(每从设备 1 根) 1 根(所有从设备共享)
数据路径 主机 ↔ 从设备(点对点) 主机 → 从设备 0 → 从设备 1 → … → 主机
从设备数量扩展 受限于主机 GPIO 数量 几乎无限制(受时序约束)
传输延迟 低(直接通信) 高(逐级转发)
典型应用 通用 SPI 外设(ADC、DAC、传感器) 移位寄存器、LED 驱动、级联 ADC
SPI 菊花链拓扑 SPI 主机 从设备 0 从设备 1 从设备 2 共享 CS(所有从设备同时选中) 数据转发机制 MOSI:主机 → 从设备 0 → 从设备 1 → 从设备 2 MISO:从设备 2 → 从设备 1 → 从设备 0 → 主机
驱动开发提示: 菊花链拓扑中,每个从设备需要支持 数据直通(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 必须保持高电平至少一个时钟周期,让从设备完成内部复位。
片选信号时序要求 SCLK 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 拓扑选择决策树

SPI 拓扑选择决策树 从设备数量 N N ≤ 可用 GPIO 数量 使用独立片选 N > 可用 GPIO 数量 设备支持菊花链? 使用菊花链拓扑 使用 GPIO 扩展器 决策依据:从设备数量、GPIO 资源、设备菊花链支持能力、实时性要求 独立片选延迟最低,菊花链节省引脚,GPIO 扩展器最灵活

2.6 本章小结

  • 单主多从拓扑 是 SPI 最常用的连接方式,每个从设备独占片选线,MISO 必须支持高阻态。
  • 菊花链拓扑 通过数据转发节省片选引脚,适用于移位寄存器、LED 驱动等场景,但增加传输延迟。
  • 片选信号管理 涉及建立时间、保持时间和释放时间,Linux 内核通过 spi_set_cs() 和 cs_change 标志控制。
  • 地址区分机制 以硬件片选为主,软件命令帧寻址为辅,设备树中通过 reg 属性配置片选编号。