当前位置: 首页 > article >正文

【5】STM32·FreeRTOS·临界段保护与调度器挂起

目录

一、临界段代码保护简介

二、临界段代码保护函数介绍

2.1、调用示例

2.2、内部实现

三、任务调度器的挂起和恢复

3.1、调用示例

3.2、内部实现


一、临界段代码保护简介

什么是临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段

适用场合如:

1、外设需严格按照时序初始化的外设:IIC、SPI等等
2、系统系统自身需求
3、用户用户需求

中断任务调度可以打断当前程序的运行

二、临界段代码保护函数介绍

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

函数描述

taskENTER_CRITICAL()

任务级进入临界段

taskEXIT_CRITICAL()

任务级退出临界段

taskENTER_CRITICAL_FROM_ISR()

中断级进入临界段

taskEXIT_CRITICAL_FROM_ISR()

中断级退出临界段

2.1、调用示例

任务级临界区调用格式示例:

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

中断级临界区调用格式示例:

uint32_t save_status;
save_status = taskENTER_CRITICAL_FROM_ISR();
{
    ...    /* 临界区 */
}
taskEXIT_CRITICAL_FROM_ISR(save_status);

特点

1、成对使用

2、支持嵌套

3、尽量保持临界段耗时短

2.2、内部实现

任务级进入临界段:taskENTER_CRITICAL()

#define taskENTER_CRITICAL()               portENTER_CRITICAL()
#define portENTER_CRITICAL()               vPortEnterCritical()

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;

    /* This is not the interrupt safe version of the enter critical function so
     * assert() if it is being called from an interrupt context.  Only API
     * functions that end in "FromISR" can be used in an interrupt.  Only assert if
     * the critical nesting count is 1 to protect against recursive calls if the
     * assert function also uses a critical section. */
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}

#define portDISABLE_INTERRUPTS()           vPortRaiseBASEPRI()

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
    {
        uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

        __asm
        {
            /* Set BASEPRI to the max syscall priority to effect a critical
             * section. */
/* *INDENT-OFF* */
            msr basepri, ulNewBASEPRI
            dsb
            isb
/* *INDENT-ON* */
        }
    }

任务级退出临界段:taskEXIT_CRITICAL()

#define taskEXIT_CRITICAL()                portEXIT_CRITICAL()
#define portEXIT_CRITICAL()                vPortExitCritical()

void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;

    if( uxCriticalNesting == 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. */
/* *INDENT-OFF* */
            msr basepri, ulBASEPRI
/* *INDENT-ON* */
        }
    }

中断级进入临界段:taskENTER_CRITICAL_FROM_ISR()

#define taskENTER_CRITICAL_FROM_ISR()      portSET_INTERRUPT_MASK_FROM_ISR()
#define portSET_INTERRUPT_MASK_FROM_ISR()  ulPortRaiseBASEPRI()

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

        __asm
        {
            /* Set BASEPRI to the max syscall priority to effect a critical
             * section. */
/* *INDENT-OFF* */
            mrs ulReturn, basepri
            msr basepri, ulNewBASEPRI
            dsb
            isb
/* *INDENT-ON* */
        }

        return ulReturn;
    }

中断级退出临界段:taskEXIT_CRITICAL_FROM_ISR()

#define taskEXIT_CRITICAL_FROM_ISR( x )           portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x )    vPortSetBASEPRI( x )

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. */
/* *INDENT-OFF* */
            msr basepri, ulBASEPRI
/* *INDENT-ON* */
        }
    }

三、任务调度器的挂起和恢复

挂起任务调度器,调用此函数不需要关闭中断

函数描述

vTaskSuspendAll()

挂起任务调度器

xTaskResumeAll()

恢复任务调度器

3.1、调用示例

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

1、与临界区不一样的是,挂起任务调度器,未关闭中断

2、它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应

3、挂起调度器的方式,适用于临界区位于任务与任务之间

4、既不用去延时中断,又可以做到临界区的安全

3.2、内部实现

挂起任务调度器:vTaskSuspendAll()

void vTaskSuspendAll( void )
{
    /* A critical section is not required as the variable is of type
     * BaseType_t.  Please read Richard Barry's reply in the following link to a
     * post in the FreeRTOS support forum before reporting this as a bug! -
     * https://goo.gl/wu4acr */

    /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that
     * do not otherwise exhibit real time behaviour. */
    portSOFTWARE_BARRIER();

    /* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment
     * is used to allow calls to vTaskSuspendAll() to nest. */
    ++uxSchedulerSuspended;

    /* Enforces ordering for ports and optimised compilers that may otherwise place
     * the above increment elsewhere. */
    portMEMORY_BARRIER();
}

当变量 uxSchedulerSuspended 的值不为 0,将会导致 Systick 无法触发 PendSV 中断,即挂起任务调度器

恢复任务调度器:xTaskResumeAll()

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

    /* If uxSchedulerSuspended is zero then this function does not match a
     * previous call to vTaskSuspendAll(). */
    configASSERT( uxSchedulerSuspended );

    /* It is possible that an ISR caused a task to be removed from an event
     * list while the scheduler was suspended.  If this was the case then the
     * removed task will have been added to the xPendingReadyList.  Once the
     * scheduler has been resumed it is safe to move all the pending ready
     * tasks from this list into their appropriate ready list. */
    taskENTER_CRITICAL();
    {
        --uxSchedulerSuspended;

        if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
        {
            if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
            {
                /* Move any readied tasks from the pending list into the
                 * appropriate ready list. */
                while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
                {
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
                    listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
                    portMEMORY_BARRIER();
                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
                    prvAddTaskToReadyList( pxTCB );

                    /* If the moved task has a priority higher than or equal to
                     * the current task then a yield must be performed. */
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {
                        xYieldPending = pdTRUE;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }

                if( pxTCB != NULL )
                {
                    /* A task was unblocked while the scheduler was suspended,
                     * which may have prevented the next unblock time from being
                     * re-calculated, in which case re-calculate it now.  Mainly
                     * important for low power tickless implementations, where
                     * this can prevent an unnecessary exit from low power
                     * state. */
                    prvResetNextTaskUnblockTime();
                }

                /* If any ticks occurred while the scheduler was suspended then
                 * they should be processed now.  This ensures the tick count does
                 * not  slip, and that any delayed tasks are resumed at the correct
                 * time. */
                {
                    TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */

                    if( xPendedCounts > ( TickType_t ) 0U )
                    {
                        do
                        {
                            if( xTaskIncrementTick() != pdFALSE )
                            {
                                xYieldPending = pdTRUE;
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }

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

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

                if( xYieldPending != pdFALSE )
                {
                    #if ( configUSE_PREEMPTION != 0 )
                        {
                            xAlreadyYielded = pdTRUE;
                        }
                    #endif
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();

    return xAlreadyYielded;
}

当变量 uxSchedulerSuspended 的值等于 0,则允许调度

1、当任务数量大于 0 时,恢复调度器才有意义,如果没有一个已创建的任务就无意义

2、移除等待就绪列表中的列表项,恢复至就绪列表,直到 xPendingReadyList 列表为空

3、如果恢复的任务优先级比当前正在执行任务优先级更高,则将 xYieldPending 赋值为 pdTRUE,表示需要进行一次任务切换

4、在调度器被挂起的期间内,是否有丢失未处理的滴答数。xPendedTicks 是丢失的滴答数,有则调用 xTaskIncrementTick() 补齐丢失的滴答数

5、判断是否允许任务切换

6、返回任务是否已经切换,已经切换返回 pdTRUE,反之返回 pdFALSE


http://www.kler.cn/a/408617.html

相关文章:

  • 文小言1:
  • C++中定义类型名的方法
  • Ruby 模块(Module)
  • 传奇996_32——npc及怪物顶戴花翎
  • 实时检测跟踪模块
  • 《筑牢安全防线:培养 C++安全编程思维习惯之道》
  • TCP三次握手的过程是怎样的?
  • 嵌入式开发 “微观世界”:位、字、字节、字符的精细解读与实战关联
  • Image fusion meets deep learning: A survey and perspective译文
  • 神经网络10-Temporal Fusion Transformer (TFT)
  • 【大语言模型】ACL2024论文-18 MINPROMPT:基于图的最小提示数据增强用于少样本问答
  • 《探索 C++:一门强大且多功能的编程语言》
  • 【Mac】VMware Fusion Pro 安装 CentOS 7
  • 深度解析MyBatis增删查改(XML方式):快速掌握数据库操作
  • Linux基本指令【Linux系统】
  • ftdi_sio应用学习笔记 5 - SPI
  • Spring Security @PreAuthorize注解
  • Ubuntu20.04安装ROS1
  • 微信小程序被攻击怎么选择高防产品
  • 如何通过docker容器将ASP.NET Core站点部署到CentOS
  • 【python系列】python数据类型之字典
  • Vue (一)
  • Linux笔记---进程:进程切换与O(1)调度算法
  • 微前端+qiankun
  • 3DEXPERIENCE软件是干什么的—3DE软件代理商微辰三维
  • WebApis学习笔记,第二节:高级语法