1. QNX SPI 驱动概述
1.1 SPI 协议简介
SPI(Serial Peripheral Interface,串行外设接口)是一种全双工、同步、主从式的串行通信协议,由 Motorola 公司于 20 世纪 80 年代提出。SPI 总线在嵌入式系统中广泛用于连接微控制器与传感器、ADC/DAC、存储器、显示驱动等外设,其核心优势在于硬件连线简单、传输速率高、协议开销低。
SPI 总线使用四根信号线完成通信:
| 信号线 | 全称 | 方向 | 功能描述 |
|---|---|---|---|
| SCLK | Serial Clock | 主→从 | 由主设备产生的同步时钟信号,决定数据传输速率 |
| MOSI | Master Out Slave In | 主→从 | 主设备发送数据至从设备的数据线 |
| MISO | Master In Slave Out | 从→主 | 从设备发送数据至主设备的数据线 |
| CS/SS | Chip Select / Slave Select | 主→从 | 片选信号,低电平有效,用于选中目标从设备 |
SPI 协议支持四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)组合决定:
- CPOL = 0:空闲时 SCLK 为低电平;CPOL = 1:空闲时 SCLK 为高电平。
- CPHA = 0:数据在 SCLK 的第一个边沿采样;CPHA = 1:数据在 SCLK 的第二个边沿采样。
- 四种模式分别为 Mode 0(CPOL=0, CPHA=0)、Mode 1(CPOL=0, CPHA=1)、Mode 2(CPOL=1, CPHA=0)、Mode 3(CPOL=1, CPHA=1)。
调试要点: 在 QNX SPI 驱动开发中,外设数据手册中标注的 SPI 模式必须与主控 SPI 控制器的模式寄存器配置严格一致。模式不匹配是导致数据移位或通信失败的常见原因。
1.2 QNX 下 SPI 驱动架构
QNX 实时操作系统采用微内核架构,其 SPI 驱动遵循标准的资源管理器(Resource Manager)框架。驱动以用户态进程的形式运行,通过 dev-spi-* 或自定义 SPI 驱动模块向应用层提供 POSIX 风格的设备接口。
QNX SPI 驱动架构自底向上分为三个层次:
- 硬件抽象层(HAL):直接操作 SPI 控制器的寄存器,完成时钟配置、模式设置、FIFO 管理、中断处理等底层操作。该层与具体 SoC 的 SPI 外设寄存器映射紧密相关。
- 核心驱动层(Core):实现资源管理器回调函数,包括
io_open、io_read、io_write、io_devctl等。该层负责将应用层的读写请求转换为 SPI 总线事务,并管理 DMA 传输描述符链。 - 设备接口层(Device Interface):通过
/dev/spiN设备节点暴露给用户空间。应用层使用标准的open()、read()、write()、devctl()系统调用与 SPI 驱动交互。
架构关键点: QNX SPI 驱动的核心优势在于资源管理器框架的标准化。驱动开发者只需实现
io_devctl 中的自定义命令(如设置 SPI 模式、时钟频率、DMA 缓冲区地址),即可在不修改应用层代码的前提下扩展硬件功能。
1.3 DMA 传输优势
在 SPI 驱动中,数据传输方式主要分为 PIO(Programmed I/O,程序控制输入输出) 和 DMA(Direct Memory Access,直接存储器访问) 两种。DMA 传输通过专用的 DMA 控制器在内存与外设之间直接搬运数据,无需 CPU 逐字节干预,从而大幅降低 CPU 占用率并提升吞吐量。
下表对比了 PIO 与 DMA 两种传输方式在 SPI 场景下的关键差异:
| 对比维度 | PIO 模式 | DMA 模式 |
|---|---|---|
| CPU 占用率 | 高(每字节触发中断或轮询) | 低(仅传输开始和结束时干预) |
| 吞吐量 | 受限于 CPU 频率和中断响应 | 接近 SPI 总线理论带宽上限 |
| 延迟抖动 | 受系统调度影响较大 | 硬件级传输,延迟稳定 |
| 适用场景 | 小数据量(< 64 字节)、低频通信 | 大数据块(≥ 256 字节)、高速流式传输 |
| 实现复杂度 | 简单,直接操作 FIFO 寄存器 | 较高,需配置 DMA 描述符链和缓冲区 |
工程注意: 在 QNX 环境下启用 SPI DMA 传输时,必须确保 DMA 缓冲区物理地址连续且对齐到缓存行边界(通常为 32 字节)。使用
mmap() 分配或通过 posix_memalign() 配合 shm_open() 创建共享内存对象是常见做法。非对齐缓冲区会导致 DMA 传输数据错位或总线错误。
DMA 传输在 SPI 驱动中的典型工作流程如下:
- 应用层通过
devctl()下发传输请求,指定发送缓冲区、接收缓冲区及传输长度。 - 核心驱动层将请求封装为 DMA 描述符(Descriptor),描述符中包含源地址、目的地址、传输长度、传输方向等参数。
- 驱动将描述符提交至 DMA 控制器的硬件描述符链(Descriptor Chain)中,并启动 DMA 传输。
- DMA 控制器自动从内存读取发送数据并写入 SPI 发送 FIFO,同时从 SPI 接收 FIFO 读取数据并写入内存接收缓冲区。
- 传输完成后,DMA 控制器触发完成中断,驱动在中断服务例程中更新传输状态并唤醒等待的应用线程。
性能参考: 在 i.MX8M 平台上实测,使用 SPI DMA 传输 4 KB 数据块时,CPU 占用率从 PIO 模式的 35% 降至 DMA 模式的 2% 以下,有效传输速率从 12 Mbps 提升至 48 Mbps(接近 SPI 时钟 50 MHz 的理论上限)。DMA 描述符链的批量提交机制是提升多帧传输效率的关键。