优先级数字更小,中断为何仍会被拖住?
STM32里“数字更小”只是优先级编码的第一层;是否能抢占,还要看PRIGROUP、实现位数、当前活动异常与屏蔽寄存器
一个中断的优先级数字已经设得更小,理论上应该更高。可当低优先级ISR正在执行时,它并没有立即进入,只是先变成pending,等前一个ISR结束后才开始运行。
这时继续改数字,往往不能解决问题。Cortex-M的优先级还要经过位宽实现、PRIGROUP分组和当前屏蔽状态三层解码,最终得到的抢占级未必与API里看到的原始数字一样。
中断延迟还包含进入异常、现场保护、尾链与迟来中断的硬件时序。它们能减少额外入栈和出栈,但不会绕过优先级与屏蔽规则。
写入的数字,未必就是硬件真正比较的数字
Cortex-M将多个中断优先级字段放在NVIC的IPR寄存器中。架构留出8位,具体STM32系列可能只实现其中的高若干位,未实现位写入后并不参与仲裁。
图1 NVIC将使能、挂起与优先级字段一起送入仲裁逻辑
如果两个API参数只在未实现的低位上不同,写入寄存器后可能得到相同的有效优先级。所以排查不能只看源码宏定义,应当现场读回IPR字段。
还要确认库函数要求传入的是“未移位逻辑优先级”还是“已对齐寄存器字节”。在不同库、内核封装与手写寄存器代码之间混用,是优先级数字看起来正确却不生效的常见原因。
PRIGROUP决定哪些位真正用于抢占AIRCR寄存器中的PRIGROUP字段会把有效优先级位划分为抢占优先级与子优先级。只有抢占部分更高的新中断,才能在当前ISR执行中途打断它。
图2 尾链能减少两个连续ISR之间的额外入栈与出栈开销
子优先级只用于多个同抢占级中断同时挂起时决定先服务谁。它不会让同抢占级的中断嵌套进正在运行的ISR,这正是“数字更小仍不抢占”的高发原因。
项目启动时应先固定全局PRIGROUP,再给每个中断分配抢占与子优先级。如果不同模块在运行中重新配置分组,原先所有优先级值的解释都可能被改变。
PRIMASK可以屏蔽大多数可配置中断,BASEPRI则可屏蔽一段优先级范围。实时操作系统的临界区、底层驱动或手写寄存器代码都可能临时改变这些状态。
图3 AIRCR中的PRIGROUP字段决定有效优先级位的分组方式
如果屏蔽恢复路径不完整,中断会一直保持pending,直到屏蔽被解除。这时IPR与PRIGROUP都可能正确,但从外部观察仍会表现为“高优先级中断被拖住”。
调试时要同时记录中断的pending、active、enable和屏蔽寄存器。只看ISR入口的GPIO波形,能看到延迟,却无法分辨它是在等待仲裁,还是被软件临界区屏蔽。
用一张现场表把问题定位第一列写中断号和API配置值,第二列读回IPR原始字节,第三列按当前PRIGROUP解码抢占与子优先级。只要这三列对不上,问题就还在配置层。
第四列记录enable、pending和active,第五列记录PRIMASK、BASEPRI与FAULTMASK。中断已pending但没有active,优先级又确认更高时,屏蔽状态与当前异常级就是下一个重点。
图4 PRIGROUP将有效优先级位分成抢占与子优先级,同组中只能排队不能嵌套
时间测量可在中断源触发点和ISR入口各翻转一个GPIO,用示波器直接看延迟。如果可用DWT或追踪功能,还可以记录进入、退出和尾链的周期数,避免把软件打印开销当成中断延迟。
最后审查ISR长度。即使新中断不能抢占,只要前一个ISR保持很短,延迟仍可控。把循环、协议处理和大量拷贝移出ISR,只保留快照、清标志与事件通知,往往比继续调整优先级更有效。
还要检查中断源的清标志顺序。外设条件没有真正解除时,ISR退出后会再次挂起,看起来像其他中断被长期拖住。清状态前先读取必要数据,按芯片手册规定的写法清除标志,并确认总线写入已经到达外设,再退出ISR,才能把优先级问题与重复触发问题分开。
在实时系统中,能够抢占不等于应该把优先级设到最高。部分内核服务要求ISR优先级处在允许调用系统接口的范围内;超出范围后调用队列或信号量API,可能破坏临界区假设。优先级规划应同时标注响应期限、最长执行时间和允许调用的服务,再决定哪些中断真正需要嵌套。
STM32中“优先级数字更小”是必要条件,却不是抢占的全部条件。真正用于仲裁的,是读回后的有效优先级、PRIGROUP中的抢占部分和当前屏蔽状态。
下次遇到“高优先级中断不及时”,先不要继续改宏定义。把IPR、AIRCR、pending/active和BASEPRI读出来,再与ISR入口时间一起看,配置问题、屏蔽问题和ISR过长会很快分开。
声明:
本文由凡亿教育整理,转载请注明来源!
投稿/招聘/广告/课程合作/资源置换 请加微信:13237418207

扫码关注










































