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。
1.2 QNX 中 SPI 框架层次
QNX 的 SPI 驱动体系采用分层架构设计,从上到下依次为:
- 用户空间 API 层:通过
posix_devctl()或spi_open()等接口与驱动交互。 - 资源管理器层:QNX 资源管理器框架将 SPI 设备注册为路径节点(如
/dev/spi0),处理 open/close/read/write/devctl 消息。 - SPI 核心层:提供与硬件无关的 SPI 传输控制逻辑,包括消息队列管理、片选控制、时钟配置。
- SPI 控制器驱动层:直接操作 SoC 的 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 驱动崩溃不会导致整个系统宕机,但要求开发者对资源管理器编程模型有深入理解。
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 传输协同调试奠定了框架基础。