FreeRTOS临界段代码

本文是《ALIENTEK STM32F429 FreeRTOS 开发教程》第四章学习笔记的补充
第一章笔记–FreeRTOS简介与源码下载
第二章笔记–FreeRTOS在STM32F4上移植
第三章笔记-FreeRTOS系统配置
第四章笔记-FreeRTOS中断分析

1. 临界段代码简介

临界段代码也称为临界区,指那些必须完整运行,不能被打断的代码段。

FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。

FreeRTOS系统本身就有很多的临界段代码保护

2. 任务级临界段代码保护

进入临界段:taskENTER_CRITICAL()

退出临界段:taskEXIT_CRITICAL()

这两个函数其实是一个宏定义,定义为:

#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()

分别封装了个函数portENTER_CRITICAL()和portEXIT_CRITICAL(),而被封装的两个函数也是宏定义,定义为:

#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()

函数vPortEnterCritical()和vPortExitCritical()在文件port.c里,函数原型为:

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}

void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

进入函数vPortEnterCritical()先执行portDISABLE_INTERRUPTS()函数(关闭中断

再执行uxCriticalNesting++ 将变量加一 变量表示临界段嵌套次数

接下来如果为1,进入一个断言函数

configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );

#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } 

进入函数vPortExitCritical() 执行configASSERT( uxCriticalNesting );

再对uxCriticalNesting减1 只有当变量为0时才调用函数portENABLE_INTERRUPTS()使能中断

#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
    __asm
    {
        /* Barrier instructions are not used as this function is only used to
        lower the BASEPRI value. */
        msr basepri, ulBASEPRI
    }
}

portENABLE_INTERRUPTS()即是向 BASEPRI寄存器中写入0,即屏蔽优先级不高于0的中断,因为0优先级已经最高,所以停止屏蔽中断

这样做保证了,在有多个临界段代码时不会因为某一临界段代码退出而打乱其他临界段的保护,只有所有临界段代码都退出后才使能中断

3. 中断级临界段代码保护

用在中断服务程序中的临界段代码保护

进入临界段:taskENTER_CRITICAL_FROM_ISR()

退出临界段:taskEXIT_CRITICAL_FROM_ISR()

#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)

ulPortRaiseBASEPRI():

static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

    __asm
    {
        mrs ulReturn, basepri
        msr basepri, ulNewBASEPRI
        dsb
        isb
    }

    return ulReturn;
}

首先读出BASEPRI寄存器的值保存在ulReturn

将configMAX_SYSCALL_INTERRUPT_PRIORITY写入BASEPRI寄存器中

返回ulReturn的值

vPortSetBASEPRI():

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
    __asm
    {
        msr basepri, ulBASEPRI
    }
}

向BASEPRI寄存器中写入一个值

中断级临界代码保护使用方法:

//定时器5中断服务函数
void TIM5_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM5_Handler);
}

//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint32_t status_value
    if(htim==(&TIM5_Handler))
    {
        status_value=taskENTER_CRITICAL_FROM_ISR();
        printf("TIM5输出.......\r\n");
        taskEXIT_CRITICAL_FROM_ISR(status_value);
    }
}

你可能感兴趣的:(FreeRTOS,FreeRTOS,临界段代码)