2. VxWorks SPI框架概述

在VxWorks 7中,SPI子系统采用分层、模块化的架构设计,将硬件细节与上层应用逻辑解耦。理解这一框架是进行DMA传输优化和吞吐量调优的基础。本章将深入剖析VxWorks 7的SPI驱动架构、总线控制器与从设备抽象层,以及核心的库函数接口。

2.1 VxWorks 7 SPI驱动架构

VxWorks 7的SPI驱动架构遵循VxBus设备驱动模型,分为三个主要层次:应用层、框架层和硬件抽象层。这种分层设计使得驱动开发者可以专注于特定硬件的适配,而无需关心上层协议细节。

架构核心原则: 分层解耦、接口标准化、支持即插即用(VxBus枚举)。
VxWorks 7 SPI驱动分层架构 VxWorks 7 SPI驱动分层架构 应用层 (Application Layer) 用户应用程序 / 文件系统 / 网络协议栈 框架层 (Framework Layer) SPI核心库 (spiLib) / 总线控制器抽象 DMA引擎接口 / 中断管理 / 同步机制 设备注册与发现 (VxBus) 硬件抽象层 (HAL / BSP) SPI控制器驱动 (如 i.MX, Zynq, STM32) GPIO片选控制 / DMA控制器驱动 时钟配置 / 引脚复用 (Pin Mux) 数据流方向:应用层 → 框架层 → HAL → 硬件寄存器

各层职责明确,数据流自上而下传递。应用层通过标准POSIX或VxWorks原生API发起SPI事务;框架层负责事务排队、DMA配置和中断分发;硬件抽象层则直接操作寄存器完成数据收发。

2.2 SPI总线控制器与从设备抽象层

VxWorks 7引入了清晰的总线控制器和从设备抽象概念,这是实现多实例、多设备管理的关键。

2.2.1 总线控制器抽象 (SPI Controller)

每个SPI硬件接口(如SPI1、SPI2)对应一个SPI_CONTROLLER实例。该结构体封装了控制器的硬件资源、配置参数和操作函数指针表。

SPI_CONTROLLER 核心成员
成员类型描述
regBaseUINT32控制器寄存器基地址
busNumUINT32总线编号(0, 1, 2...)
maxSpeedHzUINT32支持的最大SCK频率
dmaChanDMA_CHAN *关联的DMA通道句柄
lockSEM_ID总线互斥信号量
methodsSPI_CONTROLLER_METHODS操作函数表(init, xfer, config等)

2.2.2 从设备抽象 (SPI Slave Device)

挂载在SPI总线上的每个外设(如Flash、ADC、传感器)都被抽象为SPI_SLAVE结构体。该结构体描述了从设备的通信参数,使得同一控制器可以分时复用驱动多个不同配置的设备。

  • 片选管理: 每个从设备拥有独立的GPIO片选引脚或硬件片选编号。
  • 通信参数: 包含时钟极性(CPOL)、时钟相位(CPHA)、数据位宽(8/16/32位)、最大速率。
  • DMA偏好: 标记该设备是否启用DMA传输以及DMA传输的最小阈值(如大于64字节使用DMA)。
SPI总线控制器与从设备抽象关系 SPI总线控制器与从设备抽象关系 SPI_CONTROLLER (总线控制器) regBase, busNum, lock, methods SPI_SLAVE #0 CS=GPIO10, 8MHz, Mode0 SPI_SLAVE #1 CS=GPIO11, 1MHz, Mode3 SPI_SLAVE #2 CS=GPIO12, 20MHz, Mode1 物理总线 (SCK, MOSI, MISO) 共享;片选信号 (CS) 独立控制
设计优势: 通过抽象层,驱动代码无需关心具体硬件寄存器布局。更换BSP时,只需实现SPI_CONTROLLER_METHODS中的函数指针,上层代码完全不变。

2.3 SPI库函数接口

VxWorks提供了一套标准的SPI库函数(spiLib),用于初始化、配置和收发数据。这些接口是编写SPI设备驱动的核心工具。

2.3.1 核心API列表

spiLib 核心接口
函数原型功能描述
spiDevCreate()创建并注册一个SPI从设备实例
spiDevDelete()删除SPI从设备实例并释放资源
spiIoctl()控制命令接口(设置速率、模式、DMA使能等)
spiRead()从SPI从设备读取指定字节数
spiWrite()向SPI从设备写入指定字节数
spiExchange()全双工同步收发(同时读写)
spiCallbackInstall()注册SPI事务完成回调函数(用于异步模式)

2.3.2 典型使用流程

一个标准的SPI设备驱动初始化流程如下:

  1. 获取控制器句柄: 通过VxBus API或设备树获取SPI_CONTROLLER指针。
  2. 创建从设备: 调用spiDevCreate(),传入控制器句柄、片选引脚、速率等参数。
  3. 配置参数: 使用spiIoctl()设置CPOL/CPHA、DMA阈值等。
  4. 执行数据传输: 调用spiExchange()或spiWrite()/spiRead()。
  5. 清理: 卸载驱动时调用spiDevDelete()。

2.3.3 代码示例:SPI从设备创建与全双工传输

#include <vxWorks.h>
#include <spiLib.h>
#include <spiController.h>

/* 假设已通过VxBus获取到控制器句柄 */
SPI_CONTROLLER *pSpiCtrl = (SPI_CONTROLLER *)sysSpimGetCtrl(0);

/* 创建从设备实例 */
SPI_SLAVE *pSlave = spiDevCreate(pSpiCtrl,      /* 控制器 */
                                 10,             /* 片选GPIO编号 */
                                 8000000,        /* 8 MHz */
                                 SPI_MODE_0,     /* CPOL=0, CPHA=0 */
                                 8);             /* 8位数据宽度 */
if (pSlave == NULL)
    {
    printf("SPI slave creation failed!\n");
    return ERROR;
    }

/* 准备收发缓冲区 */
UINT8 txBuf[4] = {0x90, 0x00, 0x00, 0x00};  /* 读命令 */
UINT8 rxBuf[4] = {0};

/* 执行全双工传输 */
STATUS status = spiExchange(pSlave, txBuf, rxBuf, 4);
if (status == OK)
    {
    printf("Received data: 0x%02x 0x%02x 0x%02x 0x%02x\n",
           rxBuf[0], rxBuf[1], rxBuf[2], rxBuf[3]);
    }

/* 清理 */
spiDevDelete(pSlave);
注意事项:
  • spiExchange()是同步阻塞调用,在中断上下文中使用时需格外小心。
  • DMA传输通常需要缓冲区物理地址连续且对齐到缓存行边界。使用cacheDmaMalloc()分配DMA缓冲区。
  • 多任务环境下,确保每个从设备操作前后正确获取/释放控制器信号量,避免总线冲突。

2.4 框架与性能的关联

SPI框架的设计直接影响DMA传输的效率和吞吐量上限。以下是几个关键设计点:

  • 零拷贝接口: 框架层支持直接传递用户缓冲区物理地址给DMA引擎,避免数据在中间缓冲区的搬移。
  • 中断聚合: 支持批量完成中断(如传输完成N个字节后产生一次中断),减少CPU中断响应次数。
  • 事务队列: 框架层维护一个待处理事务链表,DMA引擎可以连续处理多个事务而无需CPU逐次启动。
SPI框架与DMA引擎交互流程 SPI框架与DMA引擎交互流程 应用层 SPI框架层 DMA引擎 spiExchange() DMA配置启动 DMA传输优化关键路径 1. 框架层将用户请求转换为DMA描述符链表 2. DMA引擎按描述符自动搬运数据,CPU仅处理完成中断 3. 框架层支持链式DMA,多个SPI事务无缝衔接