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 为共享总线。

SPI 总线多从设备拓扑结构 SPI 主设备 (SoC / MCU) 从设备 1 Flash 从设备 2 ADC 从设备 3 LCD SCLK MOSI MISO CS1 CS2 CS3 注意 CS 低电平有效 同一时刻只能 选中一个从设备
硬件连线要点:
  • SCLK、MOSI、MISO 为共享总线,所有从设备并联接入。
  • 每个从设备必须使用独立的 CS 引脚,不可共享。
  • 从设备的 MISO 引脚通常为三态输出,未选中时呈高阻态,避免总线冲突。
  • 总线长度不宜过长,高频下需考虑信号完整性,建议加串联匹配电阻(22Ω~33Ω)。

1.2 全双工通信原理

SPI 的核心优势在于全双工通信:在每个时钟周期内,主设备通过 MOSI 发送一位数据,同时从设备通过 MISO 返回一位数据。发送和接收在硬件层面同时进行,互不干扰。

移位寄存器机制

SPI 通信的底层实现基于环形移位寄存器。主设备和从设备内部各有一个相同位宽的移位寄存器(通常为 8 位或 16 位),通过 MOSI 和 MISO 连接成环形。

SPI 全双工移位寄存器通信原理 主设备移位寄存器 bit7 → bit6 → ... → bit0 从设备移位寄存器 bit7 → bit6 → ... → bit0 MOSI MISO SCLK(共享时钟) 每个 SCLK 时钟周期:主设备通过 MOSI 发送 1 bit,同时从设备通过 MISO 返回 1 bit
全双工通信流程(8 位数据传输为例):
  1. 主设备将待发送数据加载到移位寄存器,同时将 CS 拉低选中从设备。
  2. 主设备产生 8 个 SCLK 时钟脉冲。
  3. 每个时钟上升沿或下降沿(由 CPOL/CPHA 决定),主设备从 MOSI 移出一位,同时从设备从 MISO 移入一位。
  4. 8 个时钟后,主设备的移位寄存器中存放的是从设备发来的 8 位数据,从设备的移位寄存器中存放的是主设备发来的 8 位数据。
  5. 主设备将 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 四种模式时序对比 模式 0 (CPOL=0, CPHA=0) SCLK ↑采样 模式 1 (CPOL=0, CPHA=1) SCLK ↑采样 模式 2 (CPOL=1, CPHA=0) SCLK ↑采样 模式 3 (CPOL=1, CPHA=1) SCLK ↑采样 MOSI 数据示例(模式 0) MOSI D7 D6 D5 D4 D3 D2 D1 D0 CPOL 决定空闲电平,CPHA 决定数据采样发生在第一个边沿还是第二个边沿 红色圆点表示数据采样时刻,紫色方块表示 MOSI 数据位

模式选择与设备匹配

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 属性配置模式。