临界段保护

← FreeRTOS内核实现与应用开发实战指南


  1. 普通关中断:在 portmacro.h 里通过操作 BASEPRI寄存器 来实现,用 portDISABLE_INTERRUPTS()
  2. 带返回值的关中断:在中断中使用,临界段保护也要分级,有的低优先级中断不能打断,高优先级可以碰,还有的高优先级也不能打断,但是BASEPRI只有一个,所以需要那个东西存一下,用 portDISABLE_INTERRUPTS_MASK_FROM_ISR()
  3. 进临界段 : 就临界段嵌套计数(port.c)+1,加上 portDISABLE_INTERRUPTS(),代码放文末
  4. 带中断保护版本进临界段 :定义一个变量保存一下BASEPRI,然后用 portDISABLE_INTERRUPTS_MASK_FROM_ISR(),代码放文末

进临界段 — port.c(带嵌套计数)

任务上下文用 taskENTER_CRITICAL()/taskEXIT_CRITICAL()(宏链:taskENTER_CRITICALportENTER_CRITICALvPortEnterCritical)。嵌套计数保证「多次进入、只在最外层退出时开一次中断」:

/* 故意给非法初值:没进就退,计数变成 0xaaaaaaa9,configASSERT 必抓 */
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
 
void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
 
    /* 只在嵌套计数 == 1 时断言:防止断言函数自身进临界段导致递归误报 */
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}
 
void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
 
    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

中断保护版本进临界段

中断里不能用带嵌套计数的版本(会破坏别处中断现场),改成「定义变量接住旧 BASEPRI,退出时用旧值恢复」:

/* task.h 里的映射 */
#define taskENTER_CRITICAL_FROM_ISR()      portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x )    portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
/* 中断里的用法 */
UBaseType_t uxSavedInterruptStatus;
 
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();  /* 关中断 + 存旧 BASEPRI */
{
    /* 临界区代码:访问与任务共享的数据 */
}
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );    /* 用旧值恢复 */