Linux驱动开发SPI配置调试与常见问题排查 - 第1节
1.1 SPI总线物理层与信号定义
SPI(Serial Peripheral Interface,串行外设接口)由 Motorola 公司推出,是一种同步、全双工、主从式的串行通信总线。物理层采用四线制结构,在嵌入式系统中广泛用于连接传感器、ADC/DAC、LCD 控制器、Flash 存储器等外设。
四线制信号定义
| 信号线 | 全称 | 方向 | 功能描述 |
|---|---|---|---|
| SCLK | Serial Clock | 主→从 | 由主设备产生的同步时钟,决定数据传输速率 |
| MOSI | Master Out Slave In | 主→从 | 主设备发送数据给从设备 |
| MISO | Master In Slave Out | 从→主 | 从设备发送数据给主设备 |
| CS | Chip Select | 主→从 | 片选信号,低电平有效,选中对应的从设备 |
关键点:SPI 没有标准的应答机制和流控机制,CS 是唯一的选择信号。同一总线上多个从设备共享 SCLK、MOSI、MISO,但每个从设备需要独立的 CS 引脚。
SPI 总线拓扑结构
下图展示了一个典型的多从设备 SPI 总线连接方式:主设备通过独立的 CS 线分别选中不同的从设备,SCLK、MOSI、MISO 为共享总线。
硬件连线要点:
- SCLK、MOSI、MISO 为共享总线,所有从设备并联接入。
- 每个从设备必须使用独立的 CS 引脚,不可共享。
- 从设备的 MISO 引脚通常为三态输出,未选中时呈高阻态,避免总线冲突。
- 总线长度不宜过长,高频下需考虑信号完整性,建议加串联匹配电阻(22Ω~33Ω)。
1.2 全双工通信原理
SPI 的核心优势在于全双工通信:在每个时钟周期内,主设备通过 MOSI 发送一位数据,同时从设备通过 MISO 返回一位数据。发送和接收在硬件层面同时进行,互不干扰。
移位寄存器机制
SPI 通信的底层实现基于环形移位寄存器。主设备和从设备内部各有一个相同位宽的移位寄存器(通常为 8 位或 16 位),通过 MOSI 和 MISO 连接成环形。
全双工通信流程(8 位数据传输为例):
- 主设备将待发送数据加载到移位寄存器,同时将 CS 拉低选中从设备。
- 主设备产生 8 个 SCLK 时钟脉冲。
- 每个时钟上升沿或下降沿(由 CPOL/CPHA 决定),主设备从 MOSI 移出一位,同时从设备从 MISO 移入一位。
- 8 个时钟后,主设备的移位寄存器中存放的是从设备发来的 8 位数据,从设备的移位寄存器中存放的是主设备发来的 8 位数据。
- 主设备将 CS 拉高,结束本次传输。
常见误区:很多开发者认为 SPI 发送和接收是独立的操作。实际上,SPI 的每次读写操作都是同时进行的——当你向 SPI 发送一个字节时,硬件必然同时接收一个字节。如果只关心发送而不关心接收,接收到的数据必须被丢弃或忽略。
1.3 时钟极性与相位(CPOL / CPHA)详解
SPI 协议通过时钟极性(CPOL)和时钟相位(CPHA)两个参数定义了四种工作模式。这两个参数决定了 SCLK 空闲电平状态以及数据采样/移出的时钟边沿。
参数定义
| 参数 | 取值 | 含义 |
|---|---|---|
| CPOL | 0 | SCLK 空闲时为低电平(低电平空闲) |
| CPOL | 1 | SCLK 空闲时为高电平(高电平空闲) |
| CPHA | 0 | 数据在 SCLK 的第一个边沿(上升沿或下降沿)采样 |
| CPHA | 1 | 数据在 SCLK 的第二个边沿(上升沿或下降沿)采样 |
四种 SPI 模式
| 模式 | CPOL | CPHA | 空闲时钟电平 | 数据采样边沿 | 数据移出边沿 |
|---|---|---|---|---|---|
| 模式 0 | 0 | 0 | 低电平 | 上升沿(第一个边沿) | 下降沿 |
| 模式 1 | 0 | 1 | 低电平 | 下降沿(第二个边沿) | 上升沿 |
| 模式 2 | 1 | 0 | 高电平 | 下降沿(第一个边沿) | 上升沿 |
| 模式 3 | 1 | 1 | 高电平 | 上升沿(第二个边沿) | 下降沿 |
四种模式时序对比图
模式选择与设备匹配
SPI 从设备的 datasheet 中会明确标注其支持的 SPI 模式。主设备必须配置为与从设备相同的模式,否则通信将失败。常见的 SPI 设备(如 W25Qxx Flash、MCP3008 ADC)通常默认使用模式 0。
调试技巧:当 SPI 通信出现数据错位或乱码时,首先检查 CPOL 和 CPHA 配置是否与从设备一致。可以使用逻辑分析仪抓取 SCLK 和 MOSI/MISO 波形,观察数据采样边沿是否正确。
Linux 内核中 SPI 模式的配置
在 Linux SPI 子系统中,通过 spi_device 结构体的 mode 字段来设置 SPI 模式:
/* Linux 内核 SPI 模式宏定义 */
#define SPI_CPHA 0x01 /* 时钟相位 */
#define SPI_CPOL 0x02 /* 时钟极性 */
#define SPI_MODE_0 (0|0) /* CPOL=0, CPHA=0 */
#define SPI_MODE_1 (0|SPI_CPHA) /* CPOL=0, CPHA=1 */
#define SPI_MODE_2 (SPI_CPOL|0) /* CPOL=1, CPHA=0 */
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) /* CPOL=1, CPHA=1 */
/* 设备树中配置 SPI 模式示例 */
/* spi@... {
* compatible = "vendor,spi-controller";
* #address-cells = <1>;
* #size-cells = <0>;
* cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
*
* flash@0 {
* compatible = "jedec,spi-nor";
* reg = <0>;
* spi-max-frequency = <50000000>;
* spi-cpol; /* 设置 CPOL=1 */
* spi-cpha; /* 设置 CPHA=1 */
* // 上述两个属性同时存在 => 模式 3
* };
* };
*/
本章核心要点:
- SPI 是四线制同步全双工总线:SCLK、MOSI、MISO、CS。
- 全双工通信基于移位寄存器,每发送一位同时接收一位。
- CPOL 决定空闲时钟电平,CPHA 决定数据采样边沿。
- 四种 SPI 模式(0/1/2/3)必须与从设备严格匹配。
- Linux 设备树中通过
spi-cpol和spi-cpha属性配置模式。