4. 中断向量表配置:中断向量号分配、intConnect()函数详解、intVecSet()与intVecGet()使用

在VxWorks实时系统中,中断向量表是连接硬件中断源与软件ISR的桥梁。正确配置中断向量表,是确保中断响应实时性、可靠性的第一步。本节将深入剖析中断向量号的分配机制,并详细讲解三个核心API:intConnect()、intVecSet()与intVecGet()的使用方法与性能考量。

4.1 中断向量号分配机制

VxWorks将中断向量号分为两个逻辑区域:CPU内部异常向量和外部设备中断向量。向量号的分配直接决定了中断优先级和ISR的查找效率。

关键概念: 向量号(Vector Number)是一个整数标识符,用于在中断向量表中索引对应的ISR入口地址。在PowerPC架构中,向量号通常与硬件中断控制器(如MPIC)的IRQ号一一映射;在ARM架构中,则与GIC的中断ID对应。
架构 向量号范围 典型分配 说明
PowerPC (MPC74xx) 0 – 255 0-15: 异常; 16-255: 外部中断 向量号 = 基地址 + IRQ号
ARM (Cortex-A9) 0 – 1023 0-31: 异常; 32-1023: SPI/PPI GIC中断ID直接作为向量号
x86 (IA-32) 0 – 255 0-31: 异常; 32-255: IRQ 通过IDT表映射,向量号=中断门索引
性能提示: 向量号越小,在中断向量表中的查找路径越短。对于高频中断(如定时器、网络DMA),建议分配较小的向量号,以减少中断延迟抖动。
中断向量表结构示意图 中断向量表结构(PowerPC示例) 异常向量区 向量号 0 – 15 复位、机器检查、 TLB miss、系统调用 固定分配,不可更改 外部设备中断向量区 向量号 16 – 255 已分配 定时器: 16 串口: 17 网卡: 18 空闲池 19 – 255 动态分配 保留 BSP/内核使用 不可用户分配 向量号 → 内存地址偏移

4.2 intConnect() 函数详解

intConnect() 是VxWorks中最常用的中断连接函数。它将一个中断向量号与一个ISR函数绑定,并允许传递一个参数给ISR。其原型如下:

STATUS intConnect
    (
    VOIDFUNCPTR *vector,    /* 中断向量号指针 */
    VOIDFUNCPTR routine,    /* ISR函数指针 */
    int parameter           /* 传递给ISR的参数 */
    )

4.2.1 参数说明与使用要点

参数 类型 说明 注意事项
vector VOIDFUNCPTR * 指向中断向量号的指针,通常使用 INUM_TO_IVEC(intNum) 宏转换 必须确保向量号在有效范围内,且未被其他ISR占用
routine VOIDFUNCPTR ISR函数入口地址 ISR必须符合VxWorks ISR规范:无返回值、无阻塞、无浮点运算
parameter int 传递给ISR的整型参数,通常用于区分同类型设备的不同实例 参数在中断上下文中传递,不能传递指针到栈变量
常见错误: 直接传递 intNum 而非 INUM_TO_IVEC(intNum) 会导致向量号被错误解释,造成系统崩溃。务必使用宏进行转换。

4.2.2 典型使用示例

/* 定义ISR */
void myIsr(int param)
{
    /* 读取中断状态寄存器 */
    unsigned int status = *(volatile unsigned int *)(DEV_BASE + INT_STATUS);
    /* 处理中断 */
    if (status & TX_DONE) {
        /* 发送完成处理 */
    }
    /* 写中断清除寄存器 */
    *(volatile unsigned int *)(DEV_BASE + INT_CLEAR) = status;
}

/* 在任务初始化中连接中断 */
STATUS connectStatus;
connectStatus = intConnect(INUM_TO_IVEC(IRQ_ETHERNET), 
                           (VOIDFUNCPTR)myIsr, 
                           DEVICE_ID_0);
if (connectStatus != OK) {
    logMsg("intConnect failed for IRQ %d\n", IRQ_ETHERNET, 0,0,0,0,0);
    return ERROR;
}

4.3 intVecSet() 与 intVecGet() 使用

intVecSet() 和 intVecGet() 提供了对中断向量表的底层直接访问能力。与 intConnect() 不同,这两个函数不进行任何参数封装或上下文保存,直接读写向量表中的ISR地址。

4.3.1 函数原型与差异对比

函数 原型 功能 适用场景
intVecSet() STATUS intVecSet(FUNCPTR *vector, FUNCPTR routine) 直接设置向量号对应的ISR入口地址 需要绕过 intConnect() 的封装,实现极低延迟的ISR安装
intVecGet() FUNCPTR intVecGet(FUNCPTR *vector) 读取向量号当前绑定的ISR入口地址 调试、动态检查中断绑定状态、保存/恢复中断上下文
性能差异: intVecSet() 仅执行一次内存写操作(写入向量表),而 intConnect() 内部会调用 intVecSet() 并额外执行中断栈初始化、参数保存等操作。在启动阶段批量安装ISR时,直接使用 intVecSet() 可减少约 30% 的安装时间。

4.3.2 使用示例:保存与恢复中断向量

/* 保存原中断向量 */
FUNCPTR oldIsr;
oldIsr = intVecGet(INUM_TO_IVEC(IRQ_TIMER));

/* 安装临时ISR */
intVecSet(INUM_TO_IVEC(IRQ_TIMER), (FUNCPTR)temporaryIsr);

/* 执行需要临时屏蔽的操作 */
temporaryOperation();

/* 恢复原中断向量 */
intVecSet(INUM_TO_IVEC(IRQ_TIMER), oldIsr);

4.3.3 底层实现原理

intVecSet() 和 intVecGet() 直接操作中断向量表基地址。在VxWorks中,中断向量表通常位于内存的固定区域(如PowerPC的0x0000-0x0FFF)。这两个函数通过计算 vector 指针与基地址的偏移,直接读写对应的表项。

intVecSet/intVecGet 底层操作流程 intVecSet / intVecGet 底层操作流程 用户代码 intVecSet(INUM_TO_IVEC(16), isr) 内核API 计算向量表偏移 中断向量表 直接读写表项 操作说明: 1. intVecGet: 读取向量表[偏移] → 返回当前ISR地址 2. intVecSet: 写入向量表[偏移] = 新ISR地址 → 返回OK/ERROR

4.4 中断向量配置最佳实践

  1. 优先使用 intConnect():对于大多数应用场景,intConnect() 提供了参数传递和上下文保存,代码可读性和可维护性更好。
  2. 启动阶段批量安装使用 intVecSet():在系统初始化阶段,当需要快速安装大量ISR时,直接使用 intVecSet() 可减少函数调用开销。
  3. 动态切换ISR时保存原向量:使用 intVecGet() 保存原ISR地址,在临时操作完成后恢复,避免中断丢失。
  4. 向量号分配避免冲突:在BSP配置文件中明确记录已使用的向量号,使用 sysIntNumAlloc() 等函数进行动态分配。
  5. 使用 INUM_TO_IVEC 宏:始终使用 INUM_TO_IVEC() 和 IVEC_TO_INUM() 宏进行向量号与向量指针的转换,确保跨平台兼容性。
性能优化要点: 对于延迟敏感的中断(如音频帧中断、控制环路中断),建议在系统启动阶段使用 intVecSet() 直接安装ISR,并配合 intLock()/intUnlock() 进行原子操作,避免 intConnect() 内部的额外锁开销。