1. QNX SPI 子系统概述

SPI(Serial Peripheral Interface)是一种全双工、同步、主从式的串行通信总线,广泛应用于嵌入式系统中传感器、存储器、显示控制器等外设的连接。在 QNX 实时操作系统中,SPI 驱动被纳入标准 I/O 子系统管理,遵循资源管理器(Resource Manager)框架,提供统一的用户空间访问接口。

1.1 SPI 协议简介

SPI 总线由四条基本信号线组成:

信号线 方向 功能描述
SCLK 主→从 串行时钟,由主设备产生,决定数据传输速率
MOSI 主→从 主设备输出,从设备输入
MISO 从→主 从设备输出,主设备输入
CS/SS 主→从 片选信号,低电平有效,用于选择目标从设备

SPI 协议通过配置时钟极性(CPOL)和时钟相位(CPHA)组合出四种工作模式:

模式 CPOL CPHA 数据采样边沿
模式 0 0 0 上升沿采样,下降沿移位
模式 1 0 1 下降沿采样,上升沿移位
模式 2 1 0 下降沿采样,上升沿移位
模式 3 1 1 上升沿采样,下降沿移位
开发提示: 在 QNX 中配置 SPI 模式时,务必核对从设备数据手册中指定的 CPOL/CPHA 组合。模式不匹配将导致数据采样错位,表现为接收数据全为 0xFF 或 0x00。
SPI 总线主从连接拓扑 主设备 (Master) SCLK 发生器 从设备 1 (Slave) 从设备 2 (Slave) SCLK MOSI MISO CS1 CS2 注意: CS 线独立控制,同一时刻 只能有一个从设备被选中

1.2 QNX 中 SPI 框架层次

QNX 的 SPI 驱动体系采用分层架构设计,从上到下依次为:

  1. 用户空间 API 层:通过 posix_devctl() 或 spi_open() 等接口与驱动交互。
  2. 资源管理器层:QNX 资源管理器框架将 SPI 设备注册为路径节点(如 /dev/spi0),处理 open/close/read/write/devctl 消息。
  3. SPI 核心层:提供与硬件无关的 SPI 传输控制逻辑,包括消息队列管理、片选控制、时钟配置。
  4. SPI 控制器驱动层:直接操作 SoC 的 SPI 控制器寄存器,实现数据收发、中断处理、DMA 传输。
  5. 硬件抽象层(HAL):封装底层寄存器访问、时钟门控、引脚复用等平台相关操作。
QNX SPI 驱动分层架构 用户空间 API 层 posix_devctl() / spi_open() / spi_transfer() 资源管理器层 路径注册 / 消息分发 / 权限控制 SPI 核心层 传输队列 / 片选管理 / 时钟参数计算 SPI 控制器驱动层 寄存器操作 / 中断处理 / DMA 触发 硬件抽象层 (HAL)

1.3 SPI 驱动在 IO 子系统中的位置

在 QNX 微内核架构中,SPI 驱动作为一个用户态进程运行,通过资源管理器框架挂接到 pathname 空间。其与 IO 子系统的关系如下:

  • 进程模型:SPI 驱动是一个独立的资源管理器进程(如 dev-spi-omap),不运行在内核空间。
  • 消息传递:应用程序通过 open()、devctl() 等系统调用向 SPI 驱动发送 IO 消息,驱动处理后返回结果。
  • 中断处理:SPI 驱动通过 InterruptAttach() 注册中断处理函数,中断事件通过 Pulse 机制通知驱动线程。
  • DMA 协同:SPI 驱动可调用 DMA API(如 dma_alloc()、dma_transfer())实现大数据块的高速传输,减少 CPU 占用。
架构要点: QNX 将 SPI 驱动置于用户空间,利用微内核的消息传递机制实现隔离。这种设计使得 SPI 驱动崩溃不会导致整个系统宕机,但要求开发者对资源管理器编程模型有深入理解。
SPI 驱动在 QNX IO 子系统中的位置 应用程序 open / devctl / read / write QNX 微内核 SPI 资源管理器驱动 /dev/spi0 SPI 控制器硬件 消息传递路径 IO 消息 → 微内核 → 资源管理器 → 驱动 → 硬件寄存器

1.4 关键数据结构与接口

QNX SPI 驱动开发中需要理解的核心数据结构:

数据结构/接口 所在头文件 用途说明
spi_dev_t <hw/spi.h> SPI 设备实例结构体,包含基地址、中断号、DMA 通道、时钟频率等硬件资源
spi_msg_t <hw/spi.h> SPI 传输消息结构体,包含发送/接收缓冲区指针、数据长度、片选控制、延迟参数
spi_config_t <hw/spi.h> SPI 配置结构体,包含模式、位宽、时钟速率、DMA 使能标志
spi_transfer() <hw/spi.h> 核心传输函数,同步或异步执行 SPI 消息队列
常见误区: 初学者容易混淆 spi_config_t 和 spi_msg_t 的作用域。spi_config_t 用于初始化时设置控制器全局参数,而 spi_msg_t 用于每次传输的细节控制。在 DMA 协同调试时,spi_msg_t 中的 dma_chan 字段必须正确关联已分配的 DMA 通道。

典型的 SPI 驱动初始化流程如下:

// SPI 驱动初始化伪代码
#include <hw/spi.h>

spi_dev_t *spi_dev;
spi_config_t cfg;

// 1. 映射 SPI 控制器寄存器地址空间
spi_dev = spi_attach(SPI_BASE_ADDR, SPI_IRQ);

// 2. 配置 SPI 参数
cfg.mode     = SPI_MODE_0;       // CPOL=0, CPHA=0
cfg.bits     = 8;                // 8 位数据宽度
cfg.speed_hz = 10000000;         // 10 MHz 时钟
cfg.dma_en   = 1;                // 使能 DMA 传输

// 3. 应用配置
spi_config(spi_dev, &cfg);

// 4. 注册资源管理器路径
spi_register_path(spi_dev, "/dev/spi0");

通过以上分层结构和接口设计,QNX SPI 子系统为上层应用提供了清晰、可扩展的 SPI 通信能力,同时也为后续的 DMA 传输协同调试奠定了框架基础。