PRIMASK 和 BASEPRI 都是 Cortex-M 内核的中断屏蔽寄存器,区别在于屏蔽的“粒度”不同:
PRIMASK:全局开关,一关全关(除了 NMI 和 HardFault)。
BASEPRI:优先级阈值开关,只关掉优先级低于某个阈值的中断,高优先级中断不受影响。
1. PRIMASK
位宽:1 位。
作用:写 1 后,屏蔽所有可配置优先级的中断和异常,只有NMI和HardFault不受影响。
写 0:不屏蔽任何中断。
CMSIS 函数:
c
__disable_irq(); // 设置 PRIMASK = 1 __enable_irq(); // 设置 PRIMASK = 0
典型用途:全局关中断、低功耗进入 WFI 前防止临界区被打断。
对 WFI 的影响:
即使
PRIMASK = 1,WFI 仍然可以被中断唤醒。但唤醒后不会执行中断服务函数,直到
PRIMASK被清零。所以 FreeRTOS 的 tickless idle 常用
__disable_irq()包住__WFI(),这样任何中断都能唤醒 CPU,但不会立刻执行 ISR。
2. BASEPRI
位宽:8 位(实际有效位取决于芯片实现的优先级位数)。
作用:
写 0:不屏蔽任何中断。
写非 0 值 N:屏蔽所有优先级数值 ≥ N的中断(即逻辑优先级 ≤ N 的中断)。
优先级数值 < N 的高优先级中断不受影响。
CMSIS 函数:
c
__set_BASEPRI(N); __get_BASEPRI();
典型用途:FreeRTOS 临界区,选择性屏蔽低优先级中断,允许高优先级中断继续响应。
对 WFI 的影响:
被 BASEPRI 屏蔽的中断不能唤醒 WFI。
所以如果在
BASEPRI != 0时执行WFI,只有优先级高于 BASEPRI 阈值的中断才能唤醒 CPU。这也是为什么低功耗睡眠前通常不用 BASEPRI 屏蔽中断,而用 PRIMASK。
3. 对比总结
| 特性 | PRIMASK | BASEPRI |
|---|---|---|
| 位宽 | 1 位 | 8 位 |
| 屏蔽范围 | 所有可配置优先级中断 | 优先级数值 ≥ 阈值的中断 |
| 影响 NMI/HardFault | 否 | 否 |
| 写 0 | 允许所有中断 | 不屏蔽任何中断 |
| 典型用途 | 全局关中断、WFI 前 | FreeRTOS 临界区、选择性屏蔽 |
| WFI 唤醒 | 仍可唤醒,但不执行 ISR | 被屏蔽的中断不能唤醒 |
| CMSIS | __disable_irq()/__enable_irq() | __set_BASEPRI()/__get_BASEPRI() |
4. 在 FreeRTOS 中的使用
临界区:
c
taskENTER_CRITICAL(); // 此处 BASEPRI = configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY taskEXIT_CRITICAL(); // BASEPRI = 0
这样只有优先级高于
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY的中断能打断临界区,且这些中断不能调用 FreeRTOS 的 FromISR API。Tickless idle:
使用__disable_irq()(PRIMASK)而不是 BASEPRI,确保任何中断都能把 CPU 从WFI唤醒,唤醒后再__enable_irq()让 ISR 执行。
5. 常见误区
PRIMASK = 1 不是让 WFI 不唤醒,而是唤醒后不执行 ISR。
BASEPRI 屏蔽的中断不能唤醒 WFI。
BASEPRI = 0 时,任何已使能的中断都能唤醒 WFI,与优先级无关。
高优先级中断(优先级数值 <
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)不能调用 FreeRTOS 的FromISRAPI,因为 FreeRTOS 无法用 BASEPRI 屏蔽它们。
一句话:PRIMASK 是全局中断开关,BASEPRI 是优先级阈值开关;WFI 怕 BASEPRI,不怕 PRIMASK