

哈哈,这个问题非常经典,也是很多嵌入式新手和老鸟都很关心的坑。我们就用 STM32F103C8T6 来讲,顺便解释底层原理。咱一步步分析。
1️⃣ 场景设定
- 定时器:10ms 自动重装载(ARR),也就是每 10ms 触发一次中断。
- ISR 内容:里面做了大量操作 + 延时,总共耗时 1.3 秒(1300ms)。
- CPU:STM32F103C8T6 是 Cortex-M3,单核,单线程,没有操作系统。
2️⃣ STM32 中断基本规则
-
Cortex-M3 内核 中断嵌套是允许的,但有条件:
- 默认 同优先级中断不嵌套。
- 高优先级中断可以打断低优先级中断。
-
同一个定时器的中断默认同优先级,也就是:
- 如果前一次中断还在执行,下一次中断触发时不会立即执行 ISR。
- 内核会把"中断挂起"状态标记为 pending。
- ISR 执行完之后,CPU 才会去处理 pending 的中断。
3️⃣ 你这个例子会发生什么
情况:
-
第一次中断触发:
- ISR 开始执行,执行了 1.3 秒。
-
10ms 后,定时器再次到期:
- 中断挂起标志被设置(pending)。
- ISR 还没结束,所以这次中断不会立即打断 ISR。
-
ISR 执行完 1.3 秒后:
- CPU 会检测到 pending 标志。
- ISR 会立即被再次执行一次。
-
期间定时器继续计数:
- 如果 10ms 又触发,pending 标志再次置位。
- ISR 结束后又会再次执行。
⚠️ 结果:ISR 永远跑不完"实时",定时器节奏完全失效。
4️⃣ 实际影响总结
| 影响 | 解释 |
|---|---|
| 定时器无法保持准确周期 | ISR 太长导致定时事件被积压,每次执行完 ISR 才处理下一次,周期不再是 10ms,而是 1.3s+积压次数 |
| 系统响应变慢 | CPU 被 ISR 占满,其他任务几乎无机会执行(特别是没有 RTOS) |
| 堆积中断 pending | 如果 ISR 内部又有延时,可能造成更严重的"卡死" |
| 可能触发 watchdog 重置 | MCU 的看门狗可能认为系统"死循环" |
5️⃣ 解决方案建议
-
不要在定时器 ISR 里做耗时操作
- ISR 里只做 标记、采样、清计数器等轻量操作。
- 把重任务放在主循环或者 RTOS 任务里执行。
cvolatile uint8_t flag = 0; void TIMx_IRQHandler(void) { if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIMx, TIM_IT_Update); flag = 1; // 只设置标志 } } int main(void) { while (1) { if (flag) { flag = 0; heavy_processing(); // 耗时操作放主循环 } } } -
缩短 ISR 执行时间
- ISR 内不要调用
delay(),不要打印日志。 - 只处理必要硬件操作。
- ISR 内不要调用
-
使用 RTOS 任务代替
- 定时器 ISR 发信号/设置标志。
- 重任务在低优先级任务中执行。
- 保证高优先级 ISR 快速退出,防止打断积压。
💡 总结一句话:
在 STM32 上,如果 ISR 执行时间远超定时周期,定时器中断会积压,系统节奏彻底失效,而且可能让 MCU 看起来"卡死"。
我可以帮你画一张 "ISR 太长导致中断积压的时间轴图" ,用 STM32 的例子标注 pending 状态,非常直观。
你想要我画吗?
好啊,这个概念在 Cortex-M 系列 MCU 上很关键,也和你之前提的"ISR 太长"问题直接相关。咱用 STM32F103C8T6(Cortex-M3) 来讲。
1️⃣ 中断嵌套(Nested Interrupt)是什么?
中断嵌套就是:
当一个中断正在执行时,如果另一个中断发生,并且优先级比当前中断高,CPU 可以"打断"当前中断去处理新的高优先级中断,处理完再回来继续执行原来的中断。
简单比喻:
- 你正在厨房煮饭(中断A正在执行)
- 电话响了(中断B发生)
- 如果电话比饭重要(中断B优先级更高),你先接电话,接完再回来继续煮饭
2️⃣ Cortex-M3 的中断嵌套机制
STM32F103C8T6 是 Cortex-M3 ,它有一个叫 NVIC(Nested Vectored Interrupt Controller) 的中断控制器。
NVIC 特性:
-
支持 优先级分组:可以配置 preemption 优先级和子优先级
-
当 ISR 执行时:
- 如果新中断 优先级高 → ISR 被"打断",执行高优先级 ISR
- 如果新中断 优先级低或相同 → 挂起,等当前 ISR 结束再处理
默认行为:
- 同一个定时器的中断默认同优先级
- 所以 ISR 执行时,下一次中断不会立即打断,必须等 ISR 结束
3️⃣ 实例说明
假设:
- TIM2 中断优先级 = 2
- USART1 中断优先级 = 1 (更高)
时间轴:
t=0ms: TIM2 中断触发 → ISR_TIM2 开始
t=1ms: USART1 接收到数据 → ISR_USART1 打断 ISR_TIM2
t=1.5ms: ISR_USART1 执行完 → 返回 ISR_TIM2
t=... 继续执行 ISR_TIM2
⚠️ 如果 TIM2 和 USART1 同优先级:
t=0ms: TIM2 ISR开始
t=10ms: TIM2 再次触发 → 设 pending
t=1300ms: ISR_TIM2 结束 → CPU 检查 pending → 再执行 ISR_TIM2
✅ 这个就是你前面说的"ISR太长导致中断积压"的典型场景
4️⃣ 为什么要中断嵌套?
-
高优先级任务快速响应:
- 例如:电机过流保护、紧急停止信号必须立刻处理
-
实时性保证:
- 允许关键中断不被低优先级中断拖慢
5️⃣ 注意事项
-
嵌套会增加 栈使用:
- 每个 ISR 都会把寄存器压入栈
- 嵌套太深 → 可能造成 栈溢出
-
ISR 仍要尽量短:
- 即使支持嵌套,也不意味着可以在 ISR 内做"1.3秒处理"
-
优先级分组设置:
- Cortex-M3 可以划分 preemption 优先级和子优先级
- 决定哪些中断可以打断哪些
💡 小总结:
- 中断嵌套 = 高优先级 ISR 可以打断低优先级 ISR
- 同优先级 ISR 不会打断自己 → 累积 pending
- ISR 太长 → 定时器节奏彻底失效 → 可能让系统"卡死"
如果你愿意,我可以帮你画一张 Cortex-M3 中断嵌套示意图,直观地看到 pending、打断、返回的过程,非常容易理解。
你想让我画吗?
好,这个问题非常关键,你问的正是 STM32/Cortex-M 系列 MCU 中断底层执行流程 的核心,理解了这些,ISR 太长、嵌套、挂起这些问题就都能看明白了。咱用 STM32F103C8T6(Cortex-M3) 来讲。
1️⃣ 中断挂起(Pending)标志位什么时候清除?
每个中断在 NVIC 和 外设寄存器里都有一个挂起标志(Pending Bit),它的作用是记录"这个中断已经触发了,但还没被处理"。
清除时机:
-
手动清除(软件清除)
- 大多数 STM32 外设都有中断挂起清除寄存器(例如
TIMx->SR的UIF位)。 - ISR 内通常第一步就是清除这个标志,否则同一次中断会被"反复触发"。
cvoid TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除挂起标志 // 用户处理代码 } } - 大多数 STM32 外设都有中断挂起清除寄存器(例如
-
自动清除
- 某些外设中断,例如 STM32F1 的 EXTI(外部中断),某些配置下可以硬件自动清除。
- 大多数情况下还是 软件清除更安全。
⚠️ 如果不清除挂起位:
- ISR 执行完后 NVIC 会发现挂起位仍然置位 → ISR 会立即再次执行 → 造成"无限循环"中断
2️⃣ Cortex-M3 中断处理流程(简化版)
假设外设 TIM2 触发中断:
1️⃣ 中断触发
- 外设产生中断请求
- 对应的 NVIC Pending 位被置 1
2️⃣ NVIC 响应中断
- CPU 检查中断优先级
- 如果当前没有更高优先级的 ISR 在执行 → 准备进入 ISR
3️⃣ ISR 入口(硬件自动操作)
- 自动保存寄存器 r0-r3, r12, lr, pc, xPSR 到栈
- PC 跳转到 ISR 入口地址
4️⃣ ISR 执行(软件操作)
- 用户代码开始执行
- **第一步通常清除中断挂起标志**
- 执行实际业务逻辑(采样、标志置位、发送数据等)
5️⃣ ISR 退出(硬件自动操作)
- 执行 `BX LR` 返回
- Cortex-M3 自动弹出栈上的寄存器
- 如果有 pending 中断且优先级合适 → CPU 自动跳转去处理
6️⃣ 返回主程序或下一个中断
3️⃣ 图示化理解
text
时间轴 (ms)
|---------------------------|
t=0 TIM2中断触发 → pending=1
t=0.1 CPU进入ISR
t=0.1~0.2 ISR执行 → 清除挂起标志
t=0.2 ISR结束 → 栈恢复,pending=0
t=0.25 有其他中断触发
注意点:
- 挂起标志位必须清除,否则 ISR 会重复执行
- NVIC 会根据优先级决定是否立即打断当前 ISR
- 嵌套中断会增加栈使用,深度太深可能溢出
💡 总结一句话:
中断挂起标志位在 ISR 执行期间由软件(通常第一步)清除,清除后 ISR 才能顺利退出;CPU 执行中断时,硬件自动保存现场,处理完 ISR 再恢复现场,检查是否有 pending 中断并决定是否立即进入下一次中断。
我可以帮你画一张 STM32F1 中断执行+挂起标志清除的流程图,能直观看到 pending、NVIC、ISR 执行顺序,非常容易理解。
你想让我画吗?