中断屏蔽
中断屏蔽
PRIMASK寄存器
禁止除了NMI和HardFalut外的所有中断

FAULTMASK寄存器
在PEIMASK寄存器的基础上屏蔽掉HardFault中断,写0使能,1禁止

BASEPRI寄存器
屏蔽中断,低于设定的优先级,写1使能,0禁止

freertos中断配置宏
在 Cortex-M 内核 里:
- 数字越小,优先级越高。
例如:- 优先级 0 → 最高
- 优先级 5 → 比较低
- 优先级 15 → 最低




config_MAX_SYSCALL_INTERRUPT_PRIORITY左移的原因和上面一样,STM32用高4位来表示,现在的值是低4位的
这是一个“分界线”:
- 优先级数字 ≥ 这个值的中断 → 允许调用 FreeRTOS 的 ISR API(带
FromISR后缀的)。 - 优先级数字 < 这个值的中断 → 不允许调用 FreeRTOS API。
FreeRTOS 把能在中断里调用的函数都加了 FromISR 后缀:
- 例如:
xQueueSendFromISR():在中断里往队列发数据。xSemaphoreGiveFromISR():在中断里释放信号量。xTaskNotifyFromISR():在中断里发任务通知。

当进入临界段的时候,我们不希望当前干的事被打扰,会开启中断屏蔽,5以下的优先级会被屏蔽,也就是不会响应。正因为如此,也就是说这些函数里面可以调用RTOS的API,反正进入临界段后会被屏蔽。
而5以上的中断不会被屏蔽,为了保证进入临界区发生这些中断时RTOS不会被打断:在RTOS的任务里面发生中断,中断再次调用RTOS来改变他自己的状态。所以5以上的中断函数里面不能有RTOS的API
RTOS 的临界区不是“禁止所有中断”,
而是:
“我只保证我管得住的中断不会来捣乱。”
你让“我管不住的中断”来调用我,
那我只能崩给你看。
开关中断
BASEPRI寄存器关键点:
👉 只要 BASEPRI != 0,它就生效了,不需要什么“额外使能”。
一个写入0,关闭BASEPRI,不再屏蔽中断
一个写入config_MAX_SYSCALL_INTERRUPT_PRIORITY屏蔽分界线(经过移位过的),STM32的优先级8位寄存器是高4位有效,要左移4位
,所以临界区函数是开关中断函数的再封装,支持嵌套

普通任务临界区保护
定义的宏最终会调用下面两个函数

进入临界区会先使用BASEPRI寄存器关闭中断,然后计数当前嵌套进入的临界区数量。
退出的时候会判断退出后是不是完全脱离临界区,完全出来才会开屏蔽中断
临界区的代码不要太多,不然被屏蔽的中断得不到及时响应

中断临界区保护
首先明确一点,在中断里面调用了rtos临界区保护的API,那么它的优先级一定是可被屏蔽的
进入临界区,保存BASEPRI的现场,并且重新给BASEPRI寄存器设置分界值,返回的是保存的BASEPRI寄存器的现场
退出临界区,BASEPRI恢复到保存的值


对比两种临界区退出
FreeRTOS 的设计哲学是:
内核假设:FreeRTOS 规定,所有常规任务在正常运行时,BASEPRI 必须为 0(即中断全开)。
- 任务上下文:完全由内核管理,假设它进入临界区之前 BASEPRI=0(即中断全开)。
- 所以退出时只需要恢复到 `0`,不需要保存/恢复。
- 如果有人在任务里直接乱动 `BASEPRI`,那就是用户错误(破坏了内核假设)。
- 中断上下文:FreeRTOS 不敢假设 ISR 里 BASEPRI 一定是 0,可能用户或别的库动过。
- 所以必须保存现场并恢复。普通函数退出临界区时直接清零BASEPRI恢复中断
任务是 FreeRTOS 控制的执行环境,不会有人在任务里乱改 BASEPRI。
所以进入前,BASEPRI 要么就是 0,要么就是 FreeRTOS 自己设置的。
退出时直接清零 = 把系统还原到“正常任务运行环境”。
中断函数退出临界区时保存现场恢复BASEPRI旧值
中断里不受 FreeRTOS 完全管控,应用代码可能自己写了
__set_BASEPRI(0x60); // 自己屏蔽某些中断例子
//任务
void Task(void *pvParameters)
{
taskENTER_CRITICAL(); // BASEPRI=0x40
{
// 操作关键数据
}
taskEXIT_CRITICAL(); // BASEPRI=0 (因为任务环境本来就是全开)
}
//中断
void MyISR_Handler(void)
{
__set_BASEPRI(0x60); // 屏蔽了比0x60代表的优先级还在后面的中断,即优先级数比0X60大的中断。
uint32_t old = ulPortRaiseBASEPRI(); // FreeRTOS 提升到 0x40
{
// 内核临界区操作
}
vPortSetBASEPRI(old); // 恢复=0x60,尊重用户的设定
}思考:当前中断任务的优先级是怎样的?
假设当前优先级是0X80(优先级数越小越先执行),设置了一个比当前大的优先级0x60
当前 ISR(0x80)已经在执行,不会被自己屏蔽掉。
但是其他优先级在后面的中断(例如 0x70)会被屏蔽。
更前面优先级的 ISR(例如 0x40)还是能打断它。
如果优先级是0x50,比手动设置的屏蔽优先级0x60高, 就只有下面的作用
__set_BASEPRI(0x60);这个的目的是在当中断结束后不希望0x60以下的中断来占用

