FreeRTOS从入门到精通 第六章(FreeRTOS临界段代码保护及任务调度器挂起和恢复)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、临界段代码保护

1、临界段

(1)临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段。

(2)临界段适合于需严格按照时序的场合,如软件通信协议的实现,如果其实现过程中被中断打断,将会扰乱时序,从而引发通讯错误。

(3)临界区外,中断与任务调度可以打断当前程序的运行,而临界区内直接屏蔽了中断(实际上,任务调度也是依靠中断实现的)。

2、临界段代码保护函数

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

(2)临界段代码保护函数分为任务级和中断级,也就是分为在任务函数中使用或者和在中断服务函数中使用。

函数

描述

taskENTER_CRITICAL()

任务级进入临界段

taskEXIT_CRITICAL()

任务级退出临界段

taskENTER_CRITICAL_FROM_ISR()

中断级进入临界段

taskEXIT_CRITICAL_FROM_ISR()

中断级退出临界段

(3)临界段代码保护函数使用方法:

①任务级临界区调用格式(可嵌套使用):

taskENTER_CRITICAL();
{
        … …	/* 临界区 */
}
taskEXIT_CRITICAL();

②中断级临界区调用格式(可嵌套使用):

uint32_t  save_status;    //存储中断屏蔽寄存器的值
save_status = taskENTER_CRITICAL_FROM_ISR(); 
{
        … …	/* 临界区 */
}
taskEXIT_CRITICAL_FROM_ISR(save_status);

(4)需注意,临界区中是屏蔽了中断的,而在实际运用中,中断服务程序往往需要执行非常重要的动作,为了防止这些“非常重要的动作”迟迟没有执行,临界区的代码段不宜过长(或者说执行时间不宜过长)。

二、任务调度器挂起和恢复

1、任务调度器挂起与进入临界区

        与进入临界区不一样的是,挂起任务调度器之后,中断并未被关闭,它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应。

2、任务调度器挂起和恢复函数

(1)函数概览:

函数

描述

vTaskSuspendAll()

挂起任务调度器

xTaskResumeAll()

恢复任务调度器

(2)任务调度器挂起和恢复函数使用方法:

vTaskSuspendAll() ;
{
        … …	/* 内容 */
}
xTaskResumeAll();

三、相关函数源码剖析

1、taskENTER_CRITICAL函数

(1)通过层层Go To Definition,可发现该函数的底层是vPortEnterCritical函数。

(2)vPortEnterCritical函数源码剖析:

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();  //关中断(BASEPRI设置为0x50)
    uxCriticalNesting++;           //临界区嵌套计数自增(其初始值为0)

    if( uxCriticalNesting == 1 )
    {
        configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0);
    }
}

2、taskEXIT_CRITICAL函数

(1)通过层层Go To Definition,可发现该函数的底层是vPortExitCritical函数。

(2)vPortExitCritical函数源码剖析:

void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;          //临界区嵌套计数自减(其初始值为0)

    if( uxCriticalNesting == 0 )  //如果退出临界区后没有下一层嵌套
    {
        portENABLE_INTERRUPTS();  //开中断(BASEPRI设置为0x00)
    }
}

3、vTaskSuspendAll函数

void vTaskSuspendAll( void )
{
    traceENTER_vTaskSuspendAll();

    #if ( configNUMBER_OF_CORES == 1 )
    {
        portSOFTWARE_BARRIER();

        portMEMORY_BARRIER();
    }
    #else /* #if ( configNUMBER_OF_CORES == 1 ) */
    {
        UBaseType_t ulState;

        portASSERT_IF_IN_ISR();

        if( xSchedulerRunning != pdFALSE )
        {
            ulState = portSET_INTERRUPT_MASK();

            configASSERT( portGET_CRITICAL_NESTING_COUNT() == 0 );

            portSOFTWARE_BARRIER();

            portGET_TASK_LOCK();

            if( uxSchedulerSuspended == 0U )
            {
                prvCheckForRunStateChange();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            portGET_ISR_LOCK();

            ++uxSchedulerSuspended;   //该变量只要不为0,PendSV中断关闭,不会执行任务切换
            portRELEASE_ISR_LOCK();

            portCLEAR_INTERRUPT_MASK( ulState );
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* #if ( configNUMBER_OF_CORES == 1 ) */

    traceRETURN_vTaskSuspendAll();
}

4、xTaskResumeAll函数

BaseType_t xTaskResumeAll( void )
{
    TCB_t * pxTCB = NULL;
    BaseType_t xAlreadyYielded = pdFALSE;

    traceENTER_xTaskResumeAll();

    #if ( configNUMBER_OF_CORES > 1 )
        if( xSchedulerRunning != pdFALSE )
    #endif
    {
        taskENTER_CRITICAL();  //进入临界区
        {
            BaseType_t xCoreID;
            xCoreID = ( BaseType_t ) portGET_CORE_ID();

            configASSERT( uxSchedulerSuspended != 0U );

            uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended - 1U );  //挂起嵌套值减1
            portRELEASE_TASK_LOCK();

            if( uxSchedulerSuspended == ( UBaseType_t ) 0U )  //挂起嵌套值为0,说明需要恢复任务调度器
            {
                if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )  //判断已创建的任务数量是否大于0
                {
                    while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )  //判断等待就绪列表中是否有任务,有则全部移出去
                    {
                        pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
                        listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
                        portMEMORY_BARRIER();
                        listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
						
                        prvAddTaskToReadyList( pxTCB );  //从等待就绪列表中移出的任务全部加进就绪列表中

                        #if ( configNUMBER_OF_CORES == 1 )
                        {
                            //判断是否需要进行任务切换
                            if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                                xYieldPendings[ xCoreID ] = pdTRUE;
                            else
                                mtCOVERAGE_TEST_MARKER();
                        }
                        #else /* #if ( configNUMBER_OF_CORES == 1 ) */
                        {
                        }
                        #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
                    }

                    if( pxTCB != NULL )
                    {
                        //更新下一个任务的阻塞超时时间
                        prvResetNextTaskUnblockTime();
                    }

					/* 恢复滴答定时器在任务调度器被挂起时丢失的节拍数 */
                    {
                        TickType_t xPendedCounts = xPendedTicks; 

                        if( xPendedCounts > ( TickType_t ) 0U )
                        {
                            do
                            {
								//补偿丢失的节拍数并判断是否需要任务切换
                                if( xTaskIncrementTick() != pdFALSE )
                                {
                                    xYieldPendings[ xCoreID ] = pdTRUE;
                                }
                                else
                                {
                                    mtCOVERAGE_TEST_MARKER();
                                }

                                --xPendedCounts;
                            } while( xPendedCounts > ( TickType_t ) 0U );

                            xPendedTicks = 0;
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }

                    if(xYieldPendings[xCoreID] != pdFALSE) //判断是否需要任务切换
                    {
                        #if ( configUSE_PREEMPTION != 0 )
                        {
                            xAlreadyYielded = pdTRUE;
                        }
                        #endif /* #if ( configUSE_PREEMPTION != 0 ) */

                        #if ( configNUMBER_OF_CORES == 1 )
                        {
                            taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxCurrentTCB );
                        }
                        #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();  //退出临界区
    }

    traceRETURN_xTaskResumeAll( xAlreadyYielded );

    return xAlreadyYielded;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值