4、中断管理基础:中断向量表、中断优先级、中断嵌套、中断延迟的组成
好,咱们今天聊聊中断管理。这玩意儿在ADAS系统里,可以说是命根子一样的存在。你想想看,一个紧急刹车信号来了,要是中断响应慢了半拍,那后果……嗯,我不说你也懂。
我个人习惯把中断管理拆成四个核心模块来讲:向量表、优先级、嵌套、还有延迟。咱们一个一个来。
4.1 中断向量表:系统的“紧急联络簿”
中断向量表是什么?说白了,就是一张表。这张表里存着每个中断号对应的处理函数地址。CPU一收到中断信号,就跑到这张表里查一下:“哦,这个中断号是第42号,对应的处理函数在地址0x0000A8C0,跳过去执行。”
我在项目中遇到过一个问题:有个同事把向量表放到了RAM里,结果上电时没初始化好,一进中断就飞了。查了两天才找到原因。所以我的建议是——向量表最好放在ROM里,或者至少确保上电后第一时间完成重映射。
关键点:在ARM Cortex-R系列(ADAS常用)中,向量表基地址可以通过VTOR寄存器设置。默认在0x00000000,但你可以挪到别处。
// 以ARM Cortex-R5为例,重映射向量表
#define VECTOR_TABLE_ADDR 0x80000000U
void vector_table_init(void)
{
// 1. 将向量表复制到目标地址
memcpy((void*)VECTOR_TABLE_ADDR, (void*)__Vectors, __Vectors_Size);
// 2. 设置VTOR寄存器
__set_VTOR(VECTOR_TABLE_ADDR);
// 3. 确保内存屏障
__DSB();
__ISB();
}
小技巧:调试阶段可以把向量表放在RAM里,方便动态修改。但量产时一定要放ROM,防止意外篡改。
4.2 中断优先级:谁更紧急谁先上
ADAS系统里中断多得很:CAN报文来了、雷达数据到了、摄像头帧同步信号来了、看门狗要喂了……这么多中断同时来怎么办?靠优先级。
每个中断都有一个优先级数值。数值越小,优先级越高(这是ARM的惯例,有些MCU反过来)。高优先级的中断可以打断低优先级的——这就是中断嵌套的基础。
| 中断源 | 优先级(0最高) | 典型用途 |
|---|---|---|
| NMI(不可屏蔽中断) | 0 | 硬件故障、看门狗超时 |
| 刹车信号中断 | 1 | 紧急制动响应 |
| 摄像头帧同步 | 2 | 图像采集时序 |
| CAN接收中断 | 3 | 报文接收 |
| 定时器中断 | 4 | 周期性任务调度 |
我曾经在一个项目里犯过傻:把所有中断优先级都设成一样。结果多个中断同时触发时,系统就卡住了。后来才意识到,优先级分组也很重要。ARM Cortex-M/R支持优先级分组,把优先级分成抢占优先级和子优先级两部分。
注意:中断优先级不是越多越好。一般8级(3位)或16级(4位)就够用了。优先级太多,反而容易搞混,而且会增加硬件判断的延迟。
4.3 中断嵌套:高优先级打断低优先级
中断嵌套,说白了就是“中断里套中断”。高优先级的中断来了,CPU正在处理低优先级的中断,那就先挂起低优先级的,去处理高优先级的。处理完再回来继续。
但这里有个坑——嵌套深度不能太深。我见过一个项目,嵌套了5层,结果栈空间爆了,系统直接复位。你想想看,每嵌套一层,就要压栈一次(保存现场),栈空间消耗很快。
我的经验是:嵌套深度控制在3层以内。如果超过3层,就要考虑是不是中断设计有问题了。比如,是不是有些中断处理时间太长?是不是可以把一些工作放到任务里做?
核心原则:中断服务函数(ISR)要短、平、快。只做最紧急的事,比如读数据、置标志位。复杂的处理放到任务里做。
// 好的ISR设计:只做必要的事
void CAN_IRQHandler(void)
{
// 1. 读取接收到的数据
can_msg_t msg = CAN->RX_DATA;
// 2. 置标志位,通知任务处理
g_can_msg_ready = 1;
g_can_msg = msg;
// 3. 清除中断标志
CAN->ICR |= CAN_ICR_RXIF;
// 绝不在这里做复杂运算!
}
4.4 中断延迟的组成:硬件延迟 + 软件延迟
中断延迟,就是从中断信号产生,到CPU开始执行ISR第一条指令的时间。这个时间很关键,尤其是在ADAS里。比如,刹车信号延迟1ms,可能就多跑了1米。
中断延迟由两部分组成:硬件延迟和软件延迟。
4.4.1 硬件延迟
硬件延迟是芯片本身决定的,你改不了太多。主要包括:
- 中断信号传递时间:从外设到中断控制器,再到CPU核心。一般几十ns。
- 中断优先级判断时间:多个中断同时来,硬件要比较优先级。这个时间跟优先级数量有关,一般几十到几百ns。
- 当前指令完成时间:CPU必须等当前指令执行完才能响应中断。最坏情况是遇到多周期指令(比如除法、浮点运算),可能几个us。
- 压栈时间:保存当前上下文(寄存器、PC、PSR等)。ARM Cortex-R系列一般12个周期左右。
硬件延迟加起来,一般在几百ns到几个us之间。嗯,这个你控制不了,但心里要有数。
4.4.2 软件延迟
软件延迟才是我们能优化的重点。包括:
- 中断屏蔽时间:你在代码里关了全局中断(比如调了__disable_irq()),这段时间里所有中断都进不来。这是最大的延迟来源。
- 高优先级ISR执行时间:如果高优先级中断正在执行,低优先级的就得等着。
- 中断向量表查找时间:从向量表里找到对应的ISR地址。如果向量表在慢速存储器里,这个时间会变长。
避坑指南:我曾经在一个项目里,为了做临界区保护,把全局中断关了200us。结果CAN报文全丢了。后来改用“关特定中断”的方式,只关自己关心的那个中断,延迟降到5us以内。
// 不好的做法:关全局中断
void critical_section_enter(void)
{
__disable_irq(); // 所有中断都被屏蔽!
}
// 好的做法:只关特定中断
void critical_section_enter(void)
{
// 只屏蔽CAN中断,其他中断不受影响
NVIC_DisableIRQ(CAN_IRQn);
}
4.5 总结一下
中断管理这块,说白了就是四个字:快、准、稳、短。
- 快:中断响应要快,延迟要低。
- 准:优先级分配要合理,不能乱。
- 稳:嵌套深度要控制,栈空间要够。
- 短:ISR要短,别在里面做复杂事。
嗯,今天就聊到这儿。下一节咱们讲任务调度,到时候会用到今天讲的中断知识。你先把这些基础打牢了,后面就好办了。