一 函数taskENTER_CRITICAL,taskEXIT_CRITICAL
函数taskENTER_CRITICAL最终实现如下:
第①处按照系统设定的configMAX_SYSCALL_INTERRUPT_PRIORITY值对中断进行屏蔽
第②处调用一次自增一次
第③处检查中断状态寄存器位,如果有任何中断位置1,说明是在中断中,那么报错,因为此函数不允许中断中使用。
taskEXIT_CRITICAL函数最终实现如下:
第①处如果前面有过一次以上调用taskENTER_CRITICAL,那么仍然禁止中断configMAX_SYSCALL_INTERRUPT_PRIORITY值以上的中断都不能开启,直到最后一次退出才真正退出,这个意思就是从第一次调用taskENTER_CRITICAL的范围内的代码都不能被中断打扰。
第② 处直接将basepri寄存器写0开启所有中断,taskEXIT_CRITICAL函数没有参数带入,只要调用它就是开启所有中断。
二 taskENTER_CRITICAL_FROM_ISR,taskEXIT_CRITICAL_FROM_ISR
1)taskENTER_CRITICAL_FROM_ISR函数最终实现:
第①②处,将BASEPRI赋值之前先将其值取出来,之后返回
第③处,向BASEPRI寄存器赋新值,
注:BASEPRI是arm的一个寄存器,可以设置一个数值,向寄存器BASEPRI写入某数值时大于等于此数值的中断都会被屏蔽。
2) taskEXIT_CRITICAL_FROM_ISR(xReturn)实现
此函数是带参数的,并不是像taskEXIT_CRITICAL一样直接将0赋值给BASEPRI,而这个参数就是
taskENTER_CRITICAL_FROM_ISR的返回值。
3)这样做有什么作用呢?
直接用实例解释比较容易理解:
// 嵌套中断示例
void Nested_ISR(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xSavedInterruptStatus1, xSavedInterruptStatus2;
// 第一层临界区保护
xSavedInterruptStatus1 = taskENTER_CRITICAL_FROM_ISR();
/* 执行第一层临界区代码 */
// 第二层临界区保护(嵌套)
xSavedInterruptStatus2 = taskENTER_CRITICAL_FROM_ISR();
/* 执行第二层临界区代码 */
// 按相反顺序退出临界区
taskEXIT_CRITICAL_FROM_ISR(xSavedInterruptStatus2);
taskEXIT_CRITICAL_FROM_ISR(xSavedInterruptStatus1);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
假设系统设置configMAX_SYSCALL_INTERRUPT_PRIORITY为11
第一次调用前basepri寄存器应该为0,所以xSavedInterruptStatus1=0,调用之后basepri寄存器为11,(如果当前中断优先级为11以上,那么11~15的中断都不能响应,如果当前中断优先级位11以下比如9,那么9以上都不能响应)。
第二次调用前basepri寄存器应该为11,所以xSavedInterruptStatus2=11,调用之后basepri寄存器为11,(如果当前中断优先级为11以上,那么11~15的中断都不能响应,如果当前中断优先级位11以下比如9,那么9以上都不能响应)。
第一次返回时因为xSavedInterruptStatus2=11,basepri寄存器应该为11,0~11都能执行,12~15都不能执行,此时如果使用不带ISR的临界屏蔽taskEXIT_CRITICAL()返回 ,那么0~15都能执行,违背初衷。
第二次返回时因为xSavedInterruptStatus2=0,basepri寄存器应该为0,优先级0~15都能执行。
题外话:
也许中断使用带ISR的屏蔽也许还有一个作用,就是跟任务分开,
因为任务中使用临界段仅仅可能只是想能够屏蔽其他任务的干扰,因为其他任务切换依靠最低优先级pendSV切换, 设定一个屏蔽值,可能不想屏蔽高优先级的中断。
而中断使用临界段的意图是屏蔽更高优先级的中断,起码要比当前中断的优先级要高一级(自然比任务的优先级也要高),如果同样使用 taskEXIT_CRITICAL(),一是起不到作用起不到想要的效果(屏蔽更高优先级的中断),二是起不到嵌套分层的作用,所以两者不能使用相同的屏蔽方式?
总之freeRTOS的任务跟中断是分离的两套系统,一个是任务级临界段代码保护,通过嵌套计数实现,一个是中断级代码临界段保护,通过保存和恢复寄存器BASEPRI的数值实现嵌套使用,最好是互不干扰。
可以这样理解:把任务级taskENTER_CRITICAL当成屏蔽所有中断理解,那么中断中不想屏蔽所有中断,中断中中断,那么就必须设置成可嵌套的,可嵌套的就必须带返回。所以taskENTER_CRITICAL_FROM_ISR就是为了可嵌套。
以下来自deepseek的回答有点乱感觉不对稍微参考:
FreeRTOS中断中不能使用taskENTER_CRITICAL()
的主要原因如下:
-
嵌套机制冲突
taskENTER_CRITICAL()
通过递归计数管理临界区嵌套,但中断服务程序(ISR)可能被更高优先级中断打断,导致嵌套计数不一致,从而引发中断状态恢复错误。 -
中断上下文特殊性
中断中直接关闭所有中断(如taskENTER_CRITICAL()
的操作)会破坏实时性,可能导致高优先级中断无法及时响应。FreeRTOS为此专门提供taskENTER_CRITICAL_FROM_ISR()
,仅屏蔽特定优先级的中断而非全部。 -
优先级管理差异
taskENTER_CRITICAL()
通过操作BASEPRI
寄存器屏蔽低于某优先级的中断,而中断服务程序本身可能已处于高优先级上下文,直接调用会导致不可预测的行为。 -
任务调度限制
中断中若使用任务级临界区保护,可能因调度器状态不一致引发任务切换异常,甚至死锁。