第2章 中断控制器配置:NVIC寄存器详解、中断使能与屏蔽、中断挂起与清除
中断控制器,说白了就是CPU的“秘书”。它帮你决定:哪个中断先处理?哪个中断现在不能打扰?哪个中断正在排队?在CMS32单片机里,这个秘书叫NVIC——嵌套向量中断控制器。
我刚开始用CMS32时,总觉得NVIC配置就是填几个寄存器,没什么技术含量。直到有一次,一个外设中断死活不响应,查了两天才发现是挂起位没清干净。嗯,从那以后我再也不敢小看NVIC了。
2.1 NVIC核心寄存器一览
CMS32的NVIC支持最多32个中断源,每个中断都有独立的控制位。你不需要记住所有寄存器地址,但必须理解这四类寄存器的作用:
| 寄存器组 | 功能 | 位宽 | 操作特点 |
|---|---|---|---|
| ISER | 中断使能设置 | 32位 | 写1使能,读回当前状态 |
| ICER | 中断清除/屏蔽 | 32位 | 写1屏蔽,读回使能状态 |
| ISPR | 中断挂起设置 | 32位 | 写1强制挂起,用于软件触发 |
| ICPR | 中断挂起清除 | 32位 | 写1清除挂起状态 |
关键点:这四个寄存器都是“写1有效,写0无效”。读回的值表示当前状态,不是写入的值。这个设计是为了避免读-改-写带来的竞态问题。
2.2 中断使能与屏蔽——别搞反了
使能和屏蔽,听起来像一回事,但在NVIC里完全是两码事。
- 中断使能(ISER):允许这个中断源向CPU发请求。默认所有中断都是禁能的。
- 中断屏蔽(ICER):临时关闭某个中断,但不改变它的使能状态。说白了就是“先闭嘴,但没开除你”。
我个人习惯这样用:系统初始化时统一配置ISER,把需要用到的中断都打开。运行时如果需要临时关掉某个中断,用ICER屏蔽。等条件满足了,再重新使能。
小技巧:如果你只是想在临界区临时关中断,别动ICER。直接用__disable_irq()全局关中断,退出时用__enable_irq()恢复。这样更安全,也不会丢中断标志。
代码示例:使能与屏蔽
/* 使能UART1中断(假设中断号17) */
NVIC->ISER[0] = (1UL << 17);
/* 屏蔽UART1中断(不清除使能位) */
NVIC->ICER[0] = (1UL << 17);
/* 重新使能UART1中断 */
NVIC->ISER[0] = (1UL << 17);
你看,使能和屏蔽用的是不同的寄存器。ICER只是把中断请求挡在门外,ISER里的使能位还在。这就是“屏蔽不改变使能状态”的含义。
2.3 中断挂起与清除——最容易踩的坑
挂起(Pending)是NVIC里一个非常巧妙的设计。当中断条件满足但CPU正在处理更高优先级的中断时,NVIC会把该中断标记为“挂起”。等CPU空闲了,再根据优先级决定是否响应。
为什么会这样?因为中断信号可能是脉冲式的。如果CPU没来得及响应,脉冲就消失了,那这个中断就丢了。挂起机制相当于一个“记忆位”,帮你把中断请求存下来。
挂起寄存器(ISPR)
写1可以软件触发一个中断。我在调试时经常用这招:先写ISPR模拟中断发生,看中断服务程序能不能正确执行。不用等外设信号,效率高很多。
清除挂起寄存器(ICPR)
写1清除挂起状态。注意:必须在中断服务程序退出前清除,否则会反复进入中断。
我曾经踩过的坑:有一次做ADC采集,中断服务程序里忘了清ICPR。结果每次ADC转换完成,中断标志确实清了,但NVIC的挂起位还在。CPU刚退出中断,又立刻进来,系统直接卡死。查了整整一个下午才发现是ICPR没写。
代码示例:挂起与清除
/* 软件触发中断17(调试用) */
NVIC->ISPR[0] = (1UL << 17);
/* 在中断服务程序中清除挂起 */
void UART1_IRQHandler(void)
{
/* 先处理中断业务 */
uint8_t data = UART1->DR;
/* 清除NVIC挂起位 —— 这一步不能省 */
NVIC->ICPR[0] = (1UL << 17);
/* 清除外设中断标志 */
UART1->ICR |= (1UL << 0);
}
2.4 中断优先级配置
CMS32支持4级可编程优先级(0~3),0最高,3最低。复位后默认都是0,如果不配置,所有中断优先级一样高。
优先级配置寄存器是IPR(Interrupt Priority Register),每个中断占用2位。配置方法很简单:
/* 设置中断17的优先级为2 */
NVIC->IPR[17] = 2; /* 注意:IPR数组索引就是中断号 */
我的建议:优先级别全设成0。至少留一个低优先级中断做“兜底”。否则高优先级中断嵌套太多,栈空间容易爆。我一般把定时器中断设成0,串口设成1,ADC设成2,按键设成3。
2.5 NVIC配置流程图
下面这张图总结了NVIC配置的完整流程。你可以把它贴在工位旁边,每次配中断时对照着走一遍。
2.6 实战避坑清单
最后,我把自己这些年踩过的坑整理成了一份清单。你配置NVIC时,逐条核对一遍,能省不少调试时间。
- 中断号别搞错——CMS32的中断号从0开始,不是1。查数据手册时看清楚。
- ISER和ICER不是互斥的——使能了中断不代表不能屏蔽,屏蔽了也不代表使能位被清。
- ICPR一定要清——中断服务程序里,先清ICPR再清外设标志。顺序反了可能导致中断再触发。
- 优先级别全设0——至少留一个低优先级中断,防止嵌套过深导致栈溢出。
- 调试时善用ISPR——软件触发中断比等外设信号快得多,排查问题首选。
总结一句话:NVIC配置不难,但细节决定成败。ISER管“能不能进来”,ICER管“暂时别进来”,ISPR管“假装进来了”,ICPR管“已经处理完了”。把这四个寄存器搞明白,中断系统就稳了。