SPI内核驱动开发:消息队列与DMA传输整合 - 第1节
1.1 SPI协议概述
SPI(Serial Peripheral Interface,串行外设接口)是由Motorola公司推出的一种高速、全双工、同步的串行通信总线。在嵌入式系统中,SPI广泛应用于传感器、ADC/DAC、LCD控制器、SD卡、Flash存储器等外设的连接。
1.1.1 SPI总线信号与拓扑
SPI总线使用主从架构,通常包含以下4根信号线:
| 信号名称 | 方向 | 功能描述 |
|---|---|---|
| SCLK(Serial Clock) | 主→从 | 由主设备产生的串行时钟信号,决定数据传输速率 |
| MOSI(Master Out Slave In) | 主→从 | 主设备数据输出线,从设备数据输入线 |
| MISO(Master In Slave Out) | 从→主 | 从设备数据输出线,主设备数据输入线 |
| CS/SS(Chip Select / Slave Select) | 主→从 | 片选信号,低电平有效,用于选择目标从设备 |
关键特性:SPI是全双工通信协议,意味着在每个时钟周期内,主设备可以同时发送和接收一个比特位。这与I²C的半双工特性形成鲜明对比。
1.1.2 SPI工作模式(CPOL与CPHA)
SPI协议通过时钟极性(CPOL)和时钟相位(CPHA)的组合定义4种工作模式:
| 模式 | CPOL | CPHA | 时钟空闲电平 | 数据采样边沿 |
|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿采样 |
| 模式1 | 0 | 1 | 低电平 | 下降沿采样 |
| 模式2 | 1 | 0 | 高电平 | 下降沿采样 |
| 模式3 | 1 | 1 | 高电平 | 上升沿采样 |
实践提示:大多数SPI外设默认使用模式0(CPOL=0, CPHA=0)。在驱动开发中,必须通过设备树或驱动代码正确配置SPI模式,否则通信将完全失败。
1.2 DMA原理简介
DMA(Direct Memory Access,直接存储器访问)是一种允许外设与内存之间直接传输数据的硬件机制,无需CPU逐字节参与。在SPI高速传输场景中,DMA是提升吞吐量的核心技术。
1.2.1 DMA传输流程
- 请求阶段:外设(如SPI控制器)发出DMA请求信号。
- 仲裁阶段:DMA控制器向总线仲裁器申请总线控制权。
- 传输阶段:DMA控制器获得总线控制权后,直接在外设寄存器和内存之间搬运数据。
- 完成阶段:传输完成后,DMA控制器释放总线,并向CPU发出中断通知。
1.2.2 轮询 vs 中断 vs DMA 对比
| 传输方式 | CPU占用率 | 吞吐量 | 延迟 | 适用场景 |
|---|---|---|---|---|
| 轮询(Polling) | 高(持续占用) | 低 | 低 | 短数据、低速率、实时性要求高 |
| 中断(IRQ) | 中(每次传输触发) | 中 | 中 | 中等数据量、非连续传输 |
| DMA | 极低(仅配置和完成) | 高 | 较高(需DMA启动时间) | 大数据块、高速率、批量传输 |
注意:DMA并非万能。对于小于32字节的短消息,DMA的启动开销可能超过其节省的CPU时间。实际驱动中常采用"短消息用中断,长消息用DMA"的混合策略。
1.3 消息队列在驱动中的作用
在Linux SPI驱动子系统中,消息队列(Message Queue)是连接用户空间请求与硬件传输的核心抽象层。每个SPI传输请求被封装为一个struct spi_message,多个消息按序排队,由SPI控制器驱动逐一处理。
1.3.1 消息队列的核心数据结构
struct spi_message {
struct list_head queue; // 消息队列链表节点
struct spi_device *spi; // 目标SPI设备
unsigned is_dma_mapped:1;// 是否已做DMA映射
struct completion *context; // 用于同步等待的完成量
int status; // 传输状态(0成功,负值错误码)
struct list_head transfers; // 该消息包含的传输段链表
};
struct spi_transfer {
const void *tx_buf; // 发送缓冲区
void *rx_buf; // 接收缓冲区
unsigned len; // 传输字节数
dma_addr_t tx_dma; // 发送DMA地址
dma_addr_t rx_dma; // 接收DMA地址
unsigned cs_change:1; // 传输完成后是否切换片选
u8 bits_per_word; // 每字位数
u16 delay_usecs; // 传输间延迟
};
1.3.2 消息队列的设计优势
- 解耦:用户空间通过
spi_sync()/spi_async()提交消息,无需关心底层硬件细节。 - 排队与调度:多个SPI设备共享同一控制器时,消息队列确保传输有序、互不干扰。
- 批处理优化:一个
spi_message可包含多个spi_transfer,实现连续片选下的多段传输。 - DMA友好:消息队列为DMA映射提供了清晰的缓冲区管理边界。
1.4 课程目标与前置知识
1.4.1 课程目标
完成本课程后,学员将能够:
- 理解 Linux SPI子系统架构,包括核心数据结构、控制器驱动框架和设备驱动模型。
- 掌握 SPI消息队列的创建、提交、排队与完成处理机制。
- 实现 SPI驱动的DMA传输整合,包括DMA映射、回调处理、环形缓冲区管理。
- 优化 传输性能,通过混合中断/DMA策略、零拷贝技术、批量调度等手段提升吞吐量。
- 调试 SPI驱动问题,使用ftrace、spidev测试工具、逻辑分析仪等方法定位故障。
1.4.2 前置知识要求
| 知识领域 | 具体要求 | 推荐参考 |
|---|---|---|
| C语言编程 | 熟练使用指针、链表、回调函数、内存管理 | 《C程序设计语言》K&R |
| Linux内核基础 | 理解字符设备驱动、file_operations、ioctl | 《Linux设备驱动程序》第三版 |
| 嵌入式硬件基础 | 了解GPIO、中断、时钟、寄存器操作 | 具体芯片参考手册 |
| 设备树(Device Tree) | 能阅读和编写基本设备树节点 | 内核文档 Documentation/devicetree |
| Linux内核编译 | 能配置、编译内核模块并加载到目标平台 | 内核源码 README |
学习建议:本课程将基于ARM64平台(如树莓派4B或QEMU模拟的virt平台)进行实战。建议学员提前搭建好交叉编译环境,并准备一块支持SPI接口的开发板或使用QEMU进行模拟调试。
1.5 本章小结
- SPI是一种全双工、同步、主从架构的串行总线,支持4种工作模式。
- DMA通过硬件直接搬运数据,大幅降低CPU占用,是SPI高速传输的关键技术。
- 消息队列是Linux SPI驱动的核心抽象,负责请求排队、传输调度和DMA管理。
- 本课程将从基础到进阶,系统讲解SPI驱动开发中的消息队列与DMA整合技术。