第一百四十三章:SPI
合道期涉及内核源码:
一
离开 I2C 的小桥后,林小源沿着溪流继续前行。溪流渐渐变宽,水面上出现了一座更大的桥——这座桥有四根绳索,比 I2C 的桥宽了一倍。
桥面上的水流湍急得多,而且奇特的是,水流同时向两个方向流动——从左到右和从右到左互不干扰。
"SPI。"守山老者说,"Serial Peripheral Interface——高速串行总线。"
桥头站着一个身穿蓝色铠甲的武士,手持四根不同颜色的光索——金色的 MOSI、银色的 MISO、紫色的 SCLK、红色的 CS。
"我是 SPI 的守护者。"武士说,声音干脆利落,"四根线——MOSI 是主机输出从机输入,MISO 是主机输入从机输出,SCLK 是时钟,CS 是片选。"
"片选?"林小源问。
"对。"武士指向那根红色的光索,"SPI 通过 CS 引脚选择从机——拉低 CS 表示'我要跟你说话了'。跟 I2C 的地址寻址不同,SPI 是物理选择——每根 CS 线连一个从机设备。"
"别忘了,它只是事实标准。"武士把四根光索并在一起,"SPI 没有像 USB 那样的完整标准化枚举,也没有统一的设备发现协议。大多数 SPI target 需要设备树、ACPI 或板级表手工声明。内核里还要区分两类驱动:controller driver 驱动主机控制器、排队传输、可能使用 DMA;protocol driver 则通过 controller 与某个 target 设备说话。"
"所以 struct spi_device 是谁?"
"它封装的是某个 target 在某个 controller 片选上的接口。protocol driver 拿到它,才能发消息。"
四线初试
SPI 子系统中,驱动主机控制器并排队执行传输的是 controller driver;与具体 target 协议通信的是哪类 driver?
/*
* SPI 的特点:
*
* 四根线: MOSI, MISO, SCLK, CS
* 全双工: 同时收发
* 主从模式
* 速度: 可达几十 MHz
*
* SPI vs I2C:
* SPI: 快、全双工、四根线
* I2C: 慢、半双工、两根线
*
* SPI 驱动:
* struct spi_driver:
* probe — 初始化
* remove — 移除
*
* SPI 通信:
* spi_write — 发送
* spi_read — 接收
* spi_sync_transfer — 同步传输
*/
printf("=== SPI — 高速串行总线 ===\n\n");
printf("SPI 的特点:\n");
printf(" 四根线: MOSI, MISO, SCLK, CS\n");
printf(" 全双工\n");
printf(" 主从模式\n");
printf(" 速度: 可达几十 MHz\n\n");
printf("--- SPI vs I2C ---\n");
printf("%-10s %-15s %-15s\n",
"特性", "SPI", "I2C");
printf("%-10s %-15s %-15s\n",
"---", "---", "---");
printf("%-10s %-15s %-15s\n",
"引脚", "4", "2");
printf("%-10s %-15s %-15s\n",
"速度", "几十 MHz", "400KHz");
printf("%-10s %-15s %-15s\n",
"双工", "全双工", "半双工\n");
printf("--- SPI 通信 ---\n");
printf("spi_write(spi, buf, len):\n");
printf(" 发送数据\n\n");
printf("spi_read(spi, buf, len):\n");
printf(" 接收数据\n\n");
printf("spi_sync_transfer(spi, msg):\n");
printf(" 同步传输\n\n");
printf("--- SPI 设备 ---\n");
printf("Flash 存储: SPI NOR Flash\n");
printf("显示屏: TFT LCD\n");
printf("传感器: 高速 ADC\n");
printf("SD 卡: SPI 模式\n");#include <stdio.h>
/*
* SPI 的特点:
*
* 四根线: MOSI, MISO, SCLK, CS
* 全双工: 同时收发
* 主从模式
* 速度: 可达几十 MHz
*
* SPI vs I2C:
* SPI: 快、全双工、四根线
* I2C: 慢、半双工、两根线
*
* SPI 驱动:
* struct spi_driver:
* probe — 初始化
* remove — 移除
*
* SPI 通信:
* spi_write — 发送
* spi_read — 接收
* spi_sync_transfer — 同步传输
*/
int main() {
printf("=== SPI — 高速串行总线 ===\n\n");
printf("SPI 的特点:\n");
printf(" 四根线: MOSI, MISO, SCLK, CS\n");
printf(" 全双工\n");
printf(" 主从模式\n");
printf(" 速度: 可达几十 MHz\n\n");
printf("--- SPI vs I2C ---\n");
printf("%-10s %-15s %-15s\n",
"特性", "SPI", "I2C");
printf("%-10s %-15s %-15s\n",
"---", "---", "---");
printf("%-10s %-15s %-15s\n",
"引脚", "4", "2");
printf("%-10s %-15s %-15s\n",
"速度", "几十 MHz", "400KHz");
printf("%-10s %-15s %-15s\n",
"双工", "全双工", "半双工\n");
printf("--- SPI 通信 ---\n");
printf("spi_write(spi, buf, len):\n");
printf(" 发送数据\n\n");
printf("spi_read(spi, buf, len):\n");
printf(" 接收数据\n\n");
printf("spi_sync_transfer(spi, msg):\n");
printf(" 同步传输\n\n");
printf("--- SPI 设备 ---\n");
printf("Flash 存储: SPI NOR Flash\n");
printf("显示屏: TFT LCD\n");
printf("传感器: 高速 ADC\n");
printf("SD 卡: SPI 模式\n");
return 0;
}二
"全双工是什么意思?"林小源问。
武士微微一笑,同时举起 MOSI 和 MISO 两根光索。金色的光从左向右流,银色的光从右向左流——两道光流在桥中央交错,却互不干扰。
"全双工就是——主机和从机可以同时发送和接收数据。"武士说,"I2C 是半双工,同一时刻只能一个方向传输。SPI 不一样——MOSI 和 MISO 是独立的线路,主机往 MOSI 上发数据的同时,从机可以往 MISO 上回数据。"
"所以速度更快?"
"不只是因为全双工。"武士说,"SPI 没有地址寻址的开销——不需要先发地址再发数据。也没有 ACK/NACK 的应答机制——数据发了就是发了。时钟频率也可以做得更高——标准 SPI 可以跑到几十 MHz,有些高速 SPI 甚至能到 100MHz 以上。"
"还有四种时钟模式。"武士在桥面上刻下 CPOL 与 CPHA,"CPOL 决定时钟空闲时高还是低,CPHA 决定在 leading edge 还是 trailing edge 采样。模式号由这两个 bit 拼出,mode 0 和 mode 3 最常见。很多芯片手册不直接写 SPI mode,只给时序图,你必须从图里读出极性和相位。"
"如果模式错了?"
"你以为发的是命令,设备看到的可能是杂音。SPI 很快,也很不宽容。"
林小源看着桥面上湍急的水流,跟 I2C 那条涓涓细流形成了鲜明对比。
相位之试
SPI 四种 clock mode 由哪两个模式位组合而成?
三
"SPI 有什么缺点?"林小源问。
"引脚多。"武士坦然说,"四根线是基本配置——如果要挂多个从机,每个从机需要一根 CS 线。挂 4 个从机就需要 7 根线(MOSI、MISO、SCLK 加 4 根 CS),而 I2C 只需要 2 根线加地址就够了。"
"还有呢?"
"没有标准的协议层。"武士说,"SPI 只定义了物理层——怎么传数据。至于数据的格式、命令的含义,完全由设备自己定义。你读 SPI Flash 的命令跟读 SPI 传感器的命令完全不同——每种设备都要单独写驱动。"
"I2C 也是一样的吧?"
"I2C 至少有标准的地址寻址机制和 ACK/NACK 应答。"武士说,"SPI 连这些都没有——更简单,但也更原始。"
"Linux SPI 请求会进入 I/O 队列。"武士继续说,"给同一个 SPI device 的请求按 FIFO 顺序执行,异步完成时走 completion callback;同步接口只是把常见模式包装起来。很多 controller 会用 DMA 搬大块数据,也可能只是 GPIO bitbanging。"
林小源站在桥中央,左手边是 I2C 的小桥——两根线、半双工、慢但简洁;右手边是 SPI 的大桥——四根线、全双工、快但费引脚。两条桥各有各的用途,没有谁比谁更好,只有谁更适合。
"Flash 存储、高速 ADC、TFT 屏幕——这些需要高带宽的设备,走 SPI。"武士说,"传感器、EEPROM、RTC——这些低速设备,走 I2C。选错了,不是不能用,是浪费。"
队列之试
Linux SPI 中,同一 spi_device 的请求通常按什么顺序执行?
道藏笔记
内核启示
SPI 是高速的串行总线。
SPI 的特点:
- 四根线(MOSI、MISO、SCLK、CS)
- 全双工
- 速度快(可达几十 MHz)
- CPOL/CPHA 组合成四种 clock mode
- 通常没有自动发现协议,设备需由固件/板级信息声明
- controller driver 负责控制器,protocol driver 负责具体设备协议
- 请求进入 I/O 队列,同一设备按 FIFO 执行
SPI vs I2C:
- SPI — 快、全双工、四根线
- I2C — 慢、半双工、两根线
SPI 是"快速"的总线——适合高速数据传输。
SPI 之试
本章讲全双工、片选线和主从设备时,对应的是哪一种串行外设总线?