1. 中断系统概述:中断、RTOS角色与轮询对比

大家好,我是老李。做嵌入式这行快二十年了,从8位单片机一路干到多核处理器。今天咱们聊聊中断——这个RTOS里最核心、也最容易出坑的话题。

说实话,我见过太多工程师把中断用成了轮询,或者把ISR写得比主任务还长。嗯,这都不对。咱们从头捋一遍。

1.1 什么是中断?

中断,说白了就是CPU正在干一件事,突然被另一件更紧急的事打断。

举个例子:你正在写代码,突然电话响了。你放下键盘接电话,接完再回来继续写。这个电话就是中断。

在硬件层面,中断是这样工作的:

  • 外设(比如网卡、串口、定时器)拉高一根中断请求线
  • CPU检测到中断信号,暂停当前指令流
  • CPU保存现场(PC、寄存器等)到栈里
  • CPU跳转到中断向量表,找到对应的ISR入口
  • 执行ISR,处理紧急事件
  • ISR执行完毕,恢复现场,继续原来的工作

整个过程,硬件自动完成大部分。你只需要写好ISR就行。

关键点:中断是异步事件。CPU不知道它什么时候来,来了就得停下手里的活。

1.2 中断在RTOS中的角色

在VxWorks这样的RTOS里,中断不是可有可无的。它是系统响应的基石。

我个人习惯把RTOS里的中断分成三类:

中断类型 典型来源 响应要求
外部中断 GPIO、按键、传感器 微秒级
内部中断 定时器、DMA、看门狗 纳秒级
异常中断 除零、缺页、非法指令 立即处理

你想想看,如果没有中断,CPU得不停地轮询外设状态。那效率得多低?

在VxWorks里,中断处理有几个特点:

  • 中断优先级可配置:VxWorks支持多级中断嵌套,高优先级中断可以打断低优先级ISR
  • 中断与任务隔离:ISR运行在中断上下文中,不能调用阻塞API(比如semTake、taskDelay)
  • 中断延迟可控:VxWorks通过关中断时间最小化,保证实时性

我的经验:在项目中,我习惯把中断优先级分成三级:紧急(定时器、看门狗)、重要(通信接口)、普通(按键、传感器)。这样嵌套起来不会乱。

1.3 中断 vs 轮询:到底选哪个?

这个问题,我每次带新人都会问。答案其实不复杂,但很多人搞混。

咱们直接对比一下:

对比项 中断 轮询
CPU利用率 高(无事时CPU干别的) 低(一直占着CPU问状态)
响应延迟 确定(微秒级) 不确定(取决于轮询周期)
实现复杂度 较高(需配置中断控制器、写ISR) 简单(一个while循环搞定)
适用场景 事件驱动、实时性要求高 数据量大、实时性要求低

说白了,中断适合「等事件」,轮询适合「查数据」。

举个例子:

  • 按键按下——用中断。因为按键事件不频繁,但需要立即响应
  • ADC连续采样——用轮询。因为数据量大,中断太频繁反而浪费

避坑指南:我曾经在一个项目中,把UART接收用中断处理,结果每来一个字节就触发一次中断。波特率115200时,CPU几乎全耗在ISR里了。后来改成DMA+中断,只在缓冲区满时触发一次,问题解决。

1.4 中断在VxWorks中的特殊之处

VxWorks的中断处理,和Linux、FreeRTOS不太一样。这里说几个关键点:

  1. 中断栈独立:VxWorks为ISR分配独立的中断栈,不占用任务栈。这避免了栈溢出问题。
  2. 中断延迟可测量:VxWorks提供vxTasKDelayvxTime等工具,可以精确测量中断响应时间。
  3. 中断嵌套可控:通过intLock/intUnlock控制中断开关,粒度很细。

嗯,这里要注意:VxWorks的ISR里不能调用semTaketaskDelay这些阻塞函数。为什么?因为ISR运行在中断上下文,没有任务控制块(TCB),调度器管不到它。

核心原则:ISR里只做最紧急的事——读取数据、清除中断标志、通知任务。剩下的交给任务去处理。

1.5 一个简单的例子

咱们看一段VxWorks下的中断注册代码:

/* 中断服务函数 */
void myIsr(void)
{
    /* 读取中断状态寄存器 */
    UINT32 status = *(volatile UINT32 *)INT_STATUS_REG;
    
    /* 清除中断标志 */
    *(volatile UINT32 *)INT_CLEAR_REG = status;
    
    /* 通知任务处理 */
    semGive(mySem);
}

/* 中断初始化 */
void initInterrupt(void)
{
    /* 注册中断 */
    intConnect(INUM_TO_IVEC(INT_NUM), myIsr, 0);
    
    /* 使能中断 */
    sysIntEnable(INT_NUM);
}

这段代码很简单,但有几个坑:

  • intConnect必须在任务上下文中调用,不能在ISR里注册中断
  • semGive是安全的,但semTake不行
  • 中断标志必须及时清除,否则会一直触发

我的习惯:ISR里只做三件事——读状态、清标志、发信号。剩下的数据处理,全部交给任务。这样ISR执行时间控制在几微秒内,系统响应才快。

1.6 小结

中断是RTOS的命脉。用好了,系统实时性杠杠的;用不好,CPU全耗在ISR里,任务反而跑不起来。

记住几个要点:

  • 中断适合事件驱动,轮询适合数据采集
  • ISR要短小精悍,别在里面做复杂运算
  • VxWorks的ISR不能调用阻塞API
  • 中断优先级要合理规划,别让低优先级中断饿死

下一章,咱们聊聊VxWorks的中断向量表和中断控制器配置。到时候我会分享一个实际项目中踩过的坑——中断向量表配置错了,系统直接死机。嗯,那场面,记忆犹新。


公众号:蓝海资料掘金营,微信deep3321