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 控制器的模式寄存器配置严格一致。模式不匹配是导致数据移位或通信失败的常见原因。
SPI 总线主从连接拓扑图 SPI 主设备 (SoC / MCU) 从设备 1 (传感器) 从设备 2 (ADC) 从设备 3 (Flash) SCLK MOSI MISO CS1 CS2 SPI 总线特点: 全双工 · 同步 · 主从 多从设备共享 SCLK/MOSI/MISO

1.2 QNX 下 SPI 驱动架构

QNX 实时操作系统采用微内核架构,其 SPI 驱动遵循标准的资源管理器(Resource Manager)框架。驱动以用户态进程的形式运行,通过 dev-spi-* 或自定义 SPI 驱动模块向应用层提供 POSIX 风格的设备接口。

QNX SPI 驱动架构自底向上分为三个层次:

  1. 硬件抽象层(HAL):直接操作 SPI 控制器的寄存器,完成时钟配置、模式设置、FIFO 管理、中断处理等底层操作。该层与具体 SoC 的 SPI 外设寄存器映射紧密相关。
  2. 核心驱动层(Core):实现资源管理器回调函数,包括 io_open、io_read、io_write、io_devctl 等。该层负责将应用层的读写请求转换为 SPI 总线事务,并管理 DMA 传输描述符链。
  3. 设备接口层(Device Interface):通过 /dev/spiN 设备节点暴露给用户空间。应用层使用标准的 open()、read()、write()、devctl() 系统调用与 SPI 驱动交互。
架构关键点: QNX SPI 驱动的核心优势在于资源管理器框架的标准化。驱动开发者只需实现 io_devctl 中的自定义命令(如设置 SPI 模式、时钟频率、DMA 缓冲区地址),即可在不修改应用层代码的前提下扩展硬件功能。
QNX SPI 驱动三层架构图 应用层(Application) open / read / write / devctl 设备接口层 /dev/spi0 · 设备节点管理 核心驱动层(Core) 资源管理器回调 · DMA 描述符管理 硬件抽象层(HAL) 寄存器操作 · 中断处理 · FIFO 控制 用户态 进程隔离 内核态 资源管理器

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 驱动中的典型工作流程如下:

  1. 应用层通过 devctl() 下发传输请求,指定发送缓冲区、接收缓冲区及传输长度。
  2. 核心驱动层将请求封装为 DMA 描述符(Descriptor),描述符中包含源地址、目的地址、传输长度、传输方向等参数。
  3. 驱动将描述符提交至 DMA 控制器的硬件描述符链(Descriptor Chain)中,并启动 DMA 传输。
  4. DMA 控制器自动从内存读取发送数据并写入 SPI 发送 FIFO,同时从 SPI 接收 FIFO 读取数据并写入内存接收缓冲区。
  5. 传输完成后,DMA 控制器触发完成中断,驱动在中断服务例程中更新传输状态并唤醒等待的应用线程。
SPI DMA 传输流程示意图 ① 应用层 devctl() ② 封装 DMA 描述符 ③ 提交描述符链 ④ DMA 硬件传输 ⑤ 完成中断 · 唤醒应用 DMA 传输关键参数: • 源地址(内存) • 目的地址(SPI FIFO) • 传输长度(字节) • 传输方向(读/写)
性能参考: 在 i.MX8M 平台上实测,使用 SPI DMA 传输 4 KB 数据块时,CPU 占用率从 PIO 模式的 35% 降至 DMA 模式的 2% 以下,有效传输速率从 12 Mbps 提升至 48 Mbps(接近 SPI 时钟 50 MHz 的理论上限)。DMA 描述符链的批量提交机制是提升多帧传输效率的关键。