📚 SPI 技术系列 · 持续更新中
本系列从推挽电气层一路打到 Linux spi 子系统,把"四根线"背后的电气/协议/控制器/驱动/实战全拧成一根线。
👉 想系统追更,戳这里查看完整专栏:
【SPI 技术系列】
👉 其他技术系列专栏,欢迎关注、交流、共同成长:
【I2C 技术系列】 【串口技术系列】【RISC-V 架构系列】【Linux内核系列】
本篇定位:SPI 没有 I2C 那样的 7bit 地址,多从靠 CS 片选。本篇讲独立 CS(星型)、菊花链(Daisy Chain)、广播三种多从机制,以及 CS 时序、GPIO 软控 CS、Linux 框架 CS。读完你能设计任何 SPI 多从系统。
CS 是 SPI 的"寻址"机制
I2C 用地址寻址,SPI 用 CS 片选寻址。理解 CS 的工作方式,是理解 SPI 多从的关键。菊花链更是 SPI 独有的多从绝技(I2C 做不到),值得掌握。对照 【I2C 技术系列 07】I2C 寻址与协议扩展 看,理解两种寻址哲学的差异。
目录
- 一、CS 片选的作用
- 1.1 CS 的三大作用
- 1.2 CS 让 MISO 多从共享成为可能
- 1.3 CS vs I2C 地址
- 二、独立 CS 多从(星型)
- 2.1 接线
- 2.2 传输流程
- 2.3 优势
- 2.4 缺点
- 2.5 适用
- 三、菊花链多从(Daisy Chain)
- 3.1 原理
- 3.2 工作流程
- 3.3 菊花链 vs 独立 CS
- 3.4 适用场景
- 3.5 菊花链的代码
- 3.6 菊花链的限制
- 四、广播
- 4.1 原理
- 4.2 适用
- 4.3 缺点
- 4.4 菊花链 vs 广播
- 五、CS 时序要求
- 5.1 CS 建立保持
- 5.2 CS 时序违规的后果
- 5.3 CS 与 SCK 空闲电平
- 5.4 CS 上升沿锁存
- 六、GPIO 软控 CS
- 6.1 为什么用 GPIO 软控
- 6.2 软控 CS 代码
- 6.3 硬件 CS vs GPIO CS
- 6.4 Linux GPIO CS
- 七、关键问题 Q&A
- Q1:菊花链 3 个从机,主发 24 位,各从收到什么?
- Q2:菊花链能读吗?
- Q3:独立 CS 多从,MISO 共享会不会冲突?
- Q4:CS 拉低后多久能发 SCK?
- Q5:GPIO CS 在高速 SPI 有什么问题?
- Q6:多个从机模式不同,怎么处理?
- 八、实战案例
- 案例 1:多从 MISO 冲突,数据错乱
- 案例 2:菊花链 LED 驱动,顺序错
- 案例 3:Flash 读首字节错
- 案例 4:GPIO CS 高速抖动
- 案例 5:多从模式不同,切换后首字节错
- 案例 6:菊花链一个从机故障,全链断
- 九、本篇小结与速查
- 核心要点
- 多从机制对照
- CS 时序速查
- 延伸阅读
- 参考资料
一、CS 片选的作用
1.1 CS 的三大作用
- 选中从机:CS 拉低,从机 MISO 输出使能(推挽驱动)
- 未选中高阻:CS 高,从机 MISO 高阻,不影响总线
- 帧同步:CS 下降沿 = 传输开始,CS 上升沿 = 传输结束
1.2 CS 让 MISO 多从共享成为可能
Master MISO ──┬── 从0 MISO(CS0 高,高阻) ├── 从1 MISO(CS1 低,驱动)← 当前选中 └── 从2 MISO(CS2 高,高阻)- 同一时刻只有一个 CS 有效
- 只有被选中的从机驱动 MISO,其他高阻
- 这就是 SPI 多从共享 MISO 的物理基础(对比 I2C OD 开漏线与)
1.3 CS vs I2C 地址
| 维度 | SPI CS | I2C 地址 |
|---|---|---|
| 寻址方式 | 硬件(引脚选) | 软件(协议层发地址) |
| 占用资源 | 每从一根 CS(引脚多) | 共享两线(引脚少) |
| 切换速度 | 快(拉 GPIO) | 慢(发 START+地址) |
| 冲突可能 | 无(CS 独立) | 有(地址冲突) |
| 适合 | 异构多器件 | 同类多器件 |
嵌入式视角:CS 的代价是引脚
SPI 挂 5 个从机,要 5 根 CS。MCU 引脚紧张时这很奢侈。I2C 两线挂 128 个器件。所以 SPI 适合"少而高速"的场景(Flash+显示+ADC),I2C 适合"多而低速"的场景(传感器阵列)。工程上常混合用:高速设备 SPI,低速设备 I2C。
二、独立 CS 多从(星型)
2.1 接线
Master ├─ SCK ────┬───────┬───────┐ (共享) │ │ │ │ ├─ MOSI ───┬───────┬───────┤ (共享) │ │ │ │ ├─ MISO ───┬───────┬───────┤ (共享) │ │ │ │ ├─ CS0 ────┘ │ │ → 从0 ├─ CS1 ────────────┘ │ → 从1 ├─ CS2 ────────────────────┘ → 从2- SCK/MOSI/MISO 三线共享
- 每从独立 CS
- 主机拉低某 CS 选中对应从机
2.2 传输流程
// 读从机 1gpio_low(CS1);// 选中从机 1spi_transfer(cmd);// 发命令spi_transfer(dummy);// 读数据gpio_high(CS1);// 释放2.3 优势
- 各从独立寻址,简单
- 可任意时刻选某从
- 各从速率/模式可不同(切换时重配控制器)
2.4 缺点
- 走线多(每从一根 CS)
- 多从共享 SCK/MOSI/MISO,负载电容大,高速 SI 受影响
- MCU 引脚消耗多
2.5 适用
- 异构多器件(Flash + 显示 + ADC)
- 各从需要独立访问
- 多数 SPI 系统用这种
三、菊花链多从(Daisy Chain)
3.1 原理
多个从机的移位寄存器串行级联,数据从主机 MOSI 进入第一个从机,其 DOUT 接下一个从机的 DIN,末从 DOUT 回主机 MISO:
Master ── MOSI ──→ 从0 DIN 从0 DOUT ──→ 从1 DIN 从1 DOUT ──→ 从2 DIN 从2 DOUT ──→ Master MISO Master ── SCK ────┬─────┬─────┬── (共享,所有从) Master ── CS ─────┬─────┬─────┬── (共享,所有从同时选中)- 所有从共享 SCK 和 CS
- 数据串行穿过各从的移位寄存器
- 一次传输 N×8 个 SCK(N 个从机,每个 8 位)
3.2 工作流程
3 个从机,每个 8 位移位寄存器,菊花链连接: - 主机发 24 个 SCK - 前 8 位:数据进入从0,从0 原数据移到从1,从1 原数据移到从2,从2 原数据移出(到 MISO) - 中 8 位:继续移位 - 后 8 位:继续移位 - 24 个 SCK 后:从0 收到原"中8位",从1 收到原"后8位",从2 收到原"前8位" (各从最终收到主发的不同 8 位段)菊花链的数据分配
主发 24 位 [D0…D7][D8…D15][D16…D23],24 个 SCK 后:
- 从0 收到 [D16…D23](最后 8 位,因为最先进入的移到最远)
- 从1 收到 [D8…D15]
- 从2 收到 [D0…D7](最先发的 8 位,移到从2)
顺序与直觉相反:先发的数据移到链尾的从机。软件要按"链尾从机的数据先发"组织。
3.3 菊花链 vs 独立 CS
| 维度 | 菊花链 | 独立 CS |
|---|---|---|
| CS 线数 | 1(共享) | N(每从一根) |
| 寻址 | 靠位置(链中顺序) | 靠 CS 选 |
| 独立访问 | 不能(必须全链传) | 能 |
| 延迟 | 大(N×8 SCK) | 小(8 SCK) |
| 数据流 | 串行穿过 | 并行选一 |
| 适合 | 同类器件级联(ADC/LED驱动) | 异构器件 |
3.4 适用场景
- LED 驱动级联:WS2812(虽然单线,但思想类似)、TLC5940 LED 驱动
- 多通道 ADC:多个 ADC 移位寄存器级联,一次读所有通道
- 移位寄存器扩展 IO:74HC595(输出)/74HC165(输入)级联
- 同类器件批量配置:多个相同 DAC/ADC 同时更新
3.5 菊花链的代码
// 3 个从机菊花链,每个收 8 位// 想让 从0 收 0xAA, 从1 收 0xBB, 从2 收 0xCC// 发送顺序:从2 的数据先发(0xCC),再从1(0xBB),再从0(0xAA)voiddaisy_chain_write(uint8_td0,uint8_td1,uint8_td2){gpio_low(CS);spi_transfer(d2);// 先发链尾数据spi_transfer(d1);spi_transfer(d0);// 最后发链头数据gpio_high(CS);// CS 上升沿锁存各从数据}// 调用:daisy_chain_write(0xAA, 0xBB, 0xCC)3.6 菊花链的限制
- 各从必须移位寄存器兼容(同型号或同移位长度)
- 不能独立访问某从(必须全链传)
- 一次传输 N×8 SCK,延迟大
- 链中一个从机故障,全链断
四、广播
4.1 原理
所有 CS 同时拉低,主机 MOSI 发的数据所有从机同时收:
Master ── MOSI ──┬── 从0 ├── 从1 └── 从2 Master ── CS ────┬── 从0 CS ├── 从1 CS └── 从2 CS (全部同时拉低)- 所有从 CS 接同一根线(或多个 CS 同时拉低)
- 主机发数据,所有从同时收相同数据
4.2 适用
- 同类器件同步更新(多个 DAC 同时输出)
- 固件广播(少见)
- 配置同步
4.3 缺点
- 不能读(MISO 多从冲突,推挽不能线与)
- 只能单向(主→从)
- 各从必须同命令格式
4.4 菊花链 vs 广播
- 菊花链:各从收不同数据(链中位置决定)
- 广播:各从收相同数据
五、CS 时序要求
5.1 CS 建立保持
CS ‾‾‾‾‾\_____________________________/‾‾‾ ↑ ↑ CS ↓ ↓ CS SCK 空闲 \___/‾\_/‾\_/‾\___/ 空闲 ←tCSS→ ←tCST→ 建立 保持| 参数 | 含义 | 典型 |
|---|---|---|
| tCSS | CS 下降到首 SCK 边沿 | 50~100ns |
| tCST | 末 SCK 边沿到 CS 上升 | 50~100ns |
| tCSH | CS 高电平最小脉宽(两次传输间) | 50~100ns |
5.2 CS 时序违规的后果
- tCSS 太短:CS 拉低后 SCK 立即来,从机还没准备好,首字节错
- tCST 太短:末 SCK 后 CS 立即拉高,从机没完成最后一位,末字节错
- CS 抖动:EMI 让 CS 误触发,误传输
5.3 CS 与 SCK 空闲电平
- CS 拉低时,SCK 应已在空闲电平(CPOL 决定)
- 不能 CS 拉低时 SCK 还在跳变(会被误识别为时钟)
5.4 CS 上升沿锁存
- 部分器件(Flash / 移位寄存器)在 CS 上升沿锁存移入的数据
- 即:SCK 移位期间数据进移位寄存器,CS 上升沿把数据锁到输出/执行
- 这种器件 CS 时序尤其关键
六、GPIO 软控 CS
6.1 为什么用 GPIO 软控
- 控制器硬件 CS 引脚不够(多从时)
- 需要灵活控制(菊花链、广播)
- 硬件 CS 时序固定,不满足特殊器件
6.2 软控 CS 代码
voidspi_read_flash(uint8_tcmd,uint32_taddr,uint8_t*buf,intlen){gpio_low(CS_FLASH);// 软控 CS 拉低spi_transfer(cmd);// 发命令spi_transfer(addr>>16);spi_transfer(addr>>8);spi_transfer(addr);for(inti=0;i<len;i++){buf[i]=spi_transfer(0xFF);// dummy 换数据}gpio_high(CS_FLASH);// 软控 CS 拉高}6.3 硬件 CS vs GPIO CS
| 维度 | 硬件 CS | GPIO CS |
|---|---|---|
| 时序 | 精确(自动) | 靠软件(有抖动) |
| 灵活性 | 差 | 好 |
| 多从 | 受限(引脚少) | 任意(GPIO 够就行) |
| 高速 | 优 | 软件开销大 |
| Linux 支持 | 自动 | cs-gpios 属性 |
6.4 Linux GPIO CS
&spi0 { cs-gpios = <&gpio0 4 0>, <&gpio0 5 0>, <&gpio0 6 0>; /* 3 个 GPIO CS */ flash@0 { reg = <0>; compatible = "w25q128"; }; /* 用 cs-gpios[0] */ oled@1 { reg = <1>; compatible = "ili9341"; }; /* 用 cs-gpios[1] */ };- Linux spi 框架支持 GPIO CS(
cs-gpios) - 框架自动管理 CS 拉低/拉高
- 详见 [[11-SPI Linux驱动框架]]
七、关键问题 Q&A
Q1:菊花链 3 个从机,主发 24 位,各从收到什么?
A:主发 [D0…D7][D8…D15][D16…D23],24 个 SCK 后:
- 从0(链头,最靠近 MOSI)收到 D16…D23
- 从1 收到 [D8…D15]
- 从2(链尾,最靠近 MISO)收到 D0…D7
先发的数据移到链尾。软件要"链尾从机数据先发"。
Q2:菊花链能读吗?
A:能。主发 24 个 dummy SCK,各从的移位寄存器内容串行移出到 MISO。主收 24 位 = [从2 原][从1 原][从0 原]。菊花链读写都行,但都是"全链批量",不能独立读某从。
Q3:独立 CS 多从,MISO 共享会不会冲突?
A:不会。每个从 MISO 是三态:CS 未选中高阻,选中推挽驱动。同一时刻只有一个 CS 有效 → 只有一个从驱动 MISO。前提:CS 时序正确,切换前一个 CS 拉高(tDIS),再拉低下一个 CS。
Q4:CS 拉低后多久能发 SCK?
A:看器件 datasheet 的 tCSS(典型 50~100ns)。CS 拉低后等 tCSS 再发 SCK,给从机响应时间。高速时这个间隔占周期比例要注意。GPIO 软控 CS 时,GPIO 操作本身有 μs 级开销,远大于 tCSS,一般够。
Q5:GPIO CS 在高速 SPI 有什么问题?
A:GPIO 拉低/拉高是单独操作,和 SCK 之间有软件延迟(几 ns 到 μs)。高速时 SCK 周期 20ns,GPIO 延迟可能让 CS 时序违规。50MHz+ 建议硬件 CS。Linux 框架的 GPIO CS 在高速时也可能有抖动。
Q6:多个从机模式不同,怎么处理?
A:每次切换从机前,重新配控制器 CPOL/CPHA/速度/位序。Linux spi 框架支持每 transfer 配模式(spi_transfer.cs_change+ 模式字段),框架自动重配。裸机要手动重配控制器寄存器。
八、实战案例
案例 1:多从 MISO 冲突,数据错乱
现象:3 从独立 CS,读从机 1 偶发读到从机 0 的数据。
分析:切换从机时,从机 0 的 CS 没拉高就拉低从机 1 的 CS,两个 CS 同时有效,MISO 冲突。
解决:严格"先拉高旧 CS,等 tDIS,再拉低新 CS"。
案例 2:菊花链 LED 驱动,顺序错
现象:3 个 TLC5940 LED 驱动菊花链,写入数据后 LED 亮度和预期反。
分析:菊花链数据顺序错——先发的数据移到链尾,软件按"链头先发"组织,反了。
解决:发送顺序反过来(链尾从机数据先发)。
案例 3:Flash 读首字节错
现象:W25Q 读 Flash,首字节恒错,后续字节对。
分析:CS 拉低后立即发 SCK,tCSS 不够,从机没准备好,首字节采错。
解决:CS 拉低后 delay 几 ns(或加几个 dummy SCK)再发命令。
案例 4:GPIO CS 高速抖动
现象:50MHz SPI 用 GPIO CS,偶发首字节错。
分析:GPIO 操作有 μs 级软件延迟和抖动,CS 与 SCK 时序不严格。
解决:改用硬件 CS,或降到 30MHz。
案例 5:多从模式不同,切换后首字节错
现象:Flash(Mode 0)+ OLED(Mode 3)共总线,切到 OLED 后首字节错。
分析:切换时没重配 CPOL/CPHA,OLED 用了 Flash 的 Mode 0。
解决:每次切换从机前重配控制器模式。
案例 6:菊花链一个从机故障,全链断
现象:3 从菊花链,中间从机故障,链尾从机收不到数据。
分析:菊花链数据串行穿过,中间从机移位寄存器坏,数据断流。
解决:菊花链可靠性差,关键场景用独立 CS。或加链路检测(末从 MISO 反馈)。
九、本篇小结与速查
核心要点
- CS 是 SPI 的寻址机制:拉低选中,MISO 推挽驱动;拉高未选,MISO 高阻
- 独立 CS(星型):每从一 CS,独立访问,异构多器件首选
- 菊花链:数据串行穿过各从,共享 CS,适合同类级联(ADC/LED)
- 广播:所有 CS 同拉低,单向主→从,各从收相同数据
- 菊花链数据顺序:先发的移到链尾,软件反向组织
- CS 时序:tCSS(建立)/ tCST(保持),违规首末字节错
- GPIO 软控 CS 灵活但高速有抖动,50MHz+ 用硬件 CS
- 多从模式不同:切换前重配控制器
- Linux GPIO CS:
cs-gpios设备树属性
多从机制对照
| 机制 | CS 线 | 独立访问 | 适合 |
|---|---|---|---|
| 独立 CS 星型 | N 根 | 能 | 异构多器件 |
| 菊花链 | 1 根 | 不能 | 同类级联 |
| 广播 | 1 根 | 不能 | 同步更新 |
CS 时序速查
| 参数 | 含义 | 典型 |
|---|---|---|
| tCSS | CS↓ 到首 SCK | 50~100ns |
| tCST | 末 SCK 到 CS↑ | 50~100ns |
| tDIS | CS↑ 到 MISO 高阻 | 50~100ns |
| tCSH | CS 高最小脉宽 | 50~100ns |
延伸阅读
- [[01-SPI电气特性与物理层]] —— 推挽与三态
- [[02-SPI信号完整性与时钟分布]] —— 多从走线 SI
- [[04-SPI协议与时序详解-CPOL-CPHA]] —— CS 与模式
- [[06-SPI数据帧与位序]] —— 多字节 burst
- [[11-SPI Linux驱动框架]] —— Linux CS 管理
- [[07-I2C寻址与协议扩展]] —— 对比 I2C 寻址
参考资料
- Motorola SPI Block Guide
- W25Q Datasheet(CS 时序)
- TLC5940 Datasheet(菊花链)
- Linux Documentation/spi/spi-summary
SPI 技术系列 00· 总目录 · 上一篇:[[04-SPI协议与时序详解-CPOL-CPHA]] ·下一篇:[[06-SPI数据帧与位序]]
✨ 感谢你的阅读 ✨
技术之路漫漫,分享是为了更好地交流。
如果本文的内容对你有启发,希望能得到你的支持:
👍 点赞|⭐ 收藏|👀 关注
如果你在调试过程中遇到了其他问题,
欢迎在评论区 💬留言,我们一起探讨。
关注我,一起交流底层开发的那些事儿!🚀