4. 中断使能与屏蔽:全局中断使能、局部中断使能、中断屏蔽寄存器操作
中断系统就像MCU的“紧急事务处理中心”。你想想看,CPU正在忙着算数据,突然外设说“我有急事”,这时候怎么响应?谁来批准?谁有权拒绝?
这就要靠中断的使能与屏蔽机制来管理了。说白了,就是一套“谁可以打断CPU”的权限系统。
4.1 全局中断使能——总开关
全局中断使能是整个中断系统的总闸。在ARM Cortex-M系列里,这个总闸叫PRIMASK寄存器。在51单片机里,它就是EA位。
我习惯把全局中断比作“公司大门”。门开着,所有员工(中断源)才能进来汇报工作。门关了,谁也别想进。
核心操作:
- 使能全局中断:
__enable_irq()或EA = 1 - 关闭全局中断:
__disable_irq()或EA = 0
我在项目中遇到过一个问题:某次调试电机驱动,PWM输出总是不稳定。查了半天,发现是全局中断被意外关闭了。从那以后,我每次在关键代码段前后都会检查全局中断状态。
4.2 局部中断使能——分开关
全局中断打开了,不代表所有中断都能响应。每个外设都有自己的“分开关”——局部中断使能位。
比如STM32的定时器,你要单独使能TIMx的更新中断、捕获中断等。这些使能位通常放在外设的DIER(中断使能寄存器)里。
// STM32 定时器中断使能示例
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能通道1捕获中断
局部中断使能的好处是精细控制。你想想看,一个定时器可能有十几个中断源,你不可能让它们全部触发CPU。只开你需要的,其他屏蔽掉,这样系统才干净。
| 层级 | 控制对象 | 典型寄存器 | 作用范围 |
|---|---|---|---|
| 全局 | 所有中断 | PRIMASK / EA | 整个CPU |
| 外设级 | 单个外设 | NVIC_ISER / IE寄存器 | 特定外设 |
| 事件级 | 具体中断事件 | DIER / 外设使能位 | 单个中断源 |
4.3 中断屏蔽寄存器操作——精细控制
中断屏蔽寄存器(Mask Register)是更底层的控制手段。在Cortex-M内核里,有个叫BASEPRI的寄存器,它可以屏蔽掉优先级低于某个值的中断。
这有什么用?举个例子:你在处理一个时间要求极严的PWM控制任务,不希望被其他低优先级中断打断。这时候设置BASEPRI,只允许最高优先级的中断进来,其他统统屏蔽。
我的经验:在电机控制中,电流环中断通常设为最高优先级。我会在进入电流环前设置BASEPRI,屏蔽掉所有低于电流环优先级的中断,确保电流采样和PWM更新不被干扰。
// Cortex-M BASEPRI 操作示例
__set_BASEPRI(0x50); // 屏蔽优先级大于0x50的中断
// ... 执行关键代码 ...
__set_BASEPRI(0); // 恢复所有中断
4.4 中断使能与屏蔽的实战流程
在实际项目中,中断使能和屏蔽的操作顺序很重要。我见过不少新手上来就开全局中断,结果外设还没配置好,中断就来了,系统直接跑飞。
正确的流程应该是:
- 先配置外设——设置时钟、引脚、模式
- 再配置中断——设置优先级、使能局部中断
- 最后开全局中断——让系统开始响应
注意:千万不要在中断服务函数里长时间关闭全局中断。我曾经接手过一个项目,工程师在中断里用disable_irq()处理数据,结果导致PWM输出丢步,电机抖动。中断服务函数应该短平快,需要长时间处理的数据,放到主循环里做。
4.5 中断使能与屏蔽知识体系
下面这张图总结了中断使能与屏蔽的层级关系,我建议你把它记在脑子里:
4.6 常见陷阱与避坑指南
- 中断嵌套失控——我曾经在项目中同时使能了多个同优先级中断,结果它们互相打断,系统响应时间变得不可预测。后来我强制给每个中断分配不同优先级,问题解决。
- 屏蔽寄存器设置错误——BASEPRI的值是优先级阈值,不是具体中断号。我见过有人把BASEPRI设成0xFF,结果所有中断都被屏蔽了,系统“死机”。
- 中断使能遗漏——有时候外设配置好了,局部中断也开了,但就是进不了中断。检查一下NVIC里有没有使能该外设的中断通道。这个坑我踩过不止一次。
调试技巧:在中断服务函数入口处翻转一个GPIO引脚,用示波器看这个引脚的波形。如果波形正常,说明中断确实触发了;如果没有波形,说明中断根本没进来,问题在使能配置上。
中断使能与屏蔽,说白了就是一套“谁可以进来”的权限管理。全局中断是总闸,局部中断是分闸,屏蔽寄存器是精细过滤器。把这套机制用好,你的嵌入式系统才能既响应及时,又稳定可靠。