中断系统基础:中断的概念、中断向量表、中断优先级、中断嵌套

各位同学,咱们今天聊聊中断系统。说实话,搞硬实时运动控制,中断就是你的命根子。没有中断,你根本别想做出一个能用的运动控制器。

我记得刚入行那会儿,带我的老工程师跟我说过一句话:「做运动控制,不懂中断,就像开车没有刹车。」当时我不太理解,后来自己踩了坑才明白——这话一点不夸张。

一、中断的概念

什么是中断?说白了就是:CPU正在干一件事,突然被一个紧急事件打断了,它得先放下手头的活,去处理这个紧急事件,处理完了再回来接着干。

你想想看,这像不像你正在写代码,突然电话响了?你接电话、处理完、挂掉,然后继续写代码。中断就是这个道理。

在硬实时系统里,中断有几个关键特性:

  • 异步性:中断什么时候来,CPU根本不知道。它可能在任何一条指令执行完后发生。
  • 抢占性:中断来了,CPU必须响应。你不能说「等我忙完再说」——实时系统不允许。
  • 可嵌套性:处理一个中断的时候,另一个更高优先级的中断来了怎么办?可以打断当前的中断服务程序。

核心要点:中断的本质是「事件驱动的上下文切换」。它让CPU从轮询的泥潭里解脱出来,实现真正的实时响应。

我在项目中遇到过一件事:有个同事用轮询方式检测编码器信号,结果电机转速一高,丢脉冲丢得惨不忍睹。后来改成中断触发,问题立马解决。嗯,这就是中断的价值。

二、中断向量表

中断向量表,我习惯叫它「中断的通讯录」。CPU收到中断信号后,得知道该去哪找对应的处理程序。这个「通讯录」就存在中断向量表里。

每个中断源都有一个唯一的向量号,对应向量表中的一个入口地址。CPU根据向量号,直接跳转到对应的中断服务程序。

咱们以ARM Cortex-M系列为例,它的向量表长这样:

; Cortex-M3 向量表示例
; 地址偏移    内容
0x00000000    栈顶地址
0x00000004    复位向量
0x00000008    NMI
0x0000000C    硬错误
0x00000010    内存管理错误
0x00000014    总线错误
0x00000018    用法错误
...
0x00000040    SysTick中断
0x00000044    TIM1中断
0x00000048    TIM2中断
...

个人经验:我建议你在项目初期就把向量表规划好。特别是中断优先级分组,一旦定下来,后期改起来非常麻烦。我曾经因为优先级分组没想清楚,导致一个定时器中断老是抢不过串口中断,电机控制周期直接崩了。

这里有个细节要注意:不同架构的向量表布局不一样。x86用的是IDT(中断描述符表),ARM用的是向量表,RISC-V又不一样。但核心思想都一样——查表跳转。

三、中断优先级

中断优先级,说白了就是「谁更紧急」。多个中断同时来的时候,CPU得有个决策依据:先处理谁?

在硬实时运动控制中,优先级设计直接决定了系统的实时性。我一般这么分:

优先级 中断源 说明
最高 位置比较中断 错过一个脉冲,位置就偏了
定时器溢出中断 控制周期必须严格保证
编码器捕获中断 速度反馈,允许少量延迟
串口/通信中断 非实时数据,可以等

为什么会这样排?你想想看,位置控制是运动控制的核心。一个脉冲没抓到,电机可能就多转或少转一个角度。这在精密加工里是致命的。

避坑指南:我曾经把串口中断优先级设得比定时器还高,结果上位机一发数据,电机控制周期就被打断了。电机开始抖动,现场工程师差点没把我骂死。从那以后,我定了个规矩:所有通信中断的优先级,必须低于控制周期中断。

四、中断嵌套

中断嵌套,就是「中断里套中断」。处理一个中断的时候,来了个更高优先级的,CPU就得先处理新的,处理完再回来继续旧的。

我画了个图,帮你理解这个过程:

中断嵌套执行流程 主程序执行 低优先级中断 ISR 高优先级中断 ISR 高优先级中断处理 返回低优先级 ISR 继续低优先级 ISR 返回主程序 时间 → ① 主程序执行中 ② 低优先级中断触发 ③ 高优先级中断打断 ④ 高优先级处理完成 ⑤ 恢复低优先级处理 ⑥ 返回主程序

从图上你能看到:主程序执行到一半,低优先级中断来了,CPU去处理。处理过程中,高优先级中断又来了,CPU立刻切换过去。等高优先级处理完,再回来继续处理低优先级。最后返回主程序。

这里有个关键点:中断嵌套的深度不能太深。我见过有人嵌套了四五层,结果栈空间不够,直接跑飞了。嗯,这属于典型的「设计过度」。

我的建议:中断嵌套控制在两层以内。如果超过三层,你就得重新审视你的优先级设计。很多时候,把中断服务程序做得短一点、快一点,比搞复杂的嵌套更靠谱。

五、实际设计中的几个坑

讲了这么多理论,我分享几个实际项目中踩过的坑:

  1. 中断服务程序太长:我曾经在一个中断里做了浮点运算,结果中断执行时间超过了中断触发周期,系统直接死循环。记住:中断里只做最必要的事,复杂计算放任务里做。
  2. 共享资源保护:中断和主程序共享一个变量,不加保护的话,数据可能被撕成两半。用关中断或者原子操作来解决。
  3. 优先级反转:低优先级中断占用了高优先级中断需要的资源,导致高优先级中断被阻塞。这问题在实时系统里很常见,得用优先级继承协议来处理。

特别提醒:关中断的时间一定要短。我见过有人关中断做了一整个数据包的解析,结果电机控制中断被堵了十几毫秒,电机直接失步。关中断的时间,最好控制在微秒级。

好了,中断系统的基础就讲到这里。这些概念看着简单,但真正用好、用对,需要大量的实践积累。下一节咱们会深入讲中断的硬件实现和配置方法,到时候我会带大家看具体的寄存器配置和代码实现。


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