FreeRTOS临界段代码保护、调度器挂起与恢复 | FreeRTOS 六

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

说明

一、临界段代码保护简介

1.1、什么是临界段

1.2、临界段使用场景

1.3、注意点

二、临界段代码保护函数

2.1、任务进入临界段函数与退出临界段函数

2.2、使用说明

2.3、中断级进入临界段函数与退出临界段函数

2.4、使用说明

2.5、特点

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

3.1、挂起解释

3.2、特点

3.3、挂起与恢复API函数

3.4、使用说明


说明

关于内容

1以下内容多为概念了解与步骤分析

2暂无个人示例代码使用的是FreeRTOS的官方示例代码

3若想移植代码测试的请移步其它地方寻找下文内容暂无个人示例代码供测试

关于其它

1操作系统win 10

2平台keil 5 mdk

3语言c语言

4板子STM32系列移植FreeRTOS
 

一、临界段代码保护简介

1.1、什么是临界段

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

什么可以打断代码的运行呢

        1中断

        2任务调度器的任务切换

怎么保证代码不被打断呢

        进入临界区即可。进入临界区内部实现是关闭了中断

1.2、临界段使用场景

        1外设使用需要严格按照时序初始化的外设如IIC,SPI接口等

        2系统使用系统自身存在需求如规定了某一个线程必须完整运行

        3用户使用用户自身存在需求如规定了某一代码或程序必须完整运行。

1.3、注意点

        1FreeRTOS进入临界段代码时需要关闭中断当处理结束临界段代码后再打开中断。

二、临界段代码保护函数

2.1、任务进入临界段函数与退出临界段函数

函数名taskENTER_CRITICAL()-->进入

函数名taskEXIT_CRITICAL()-->退出

FreeRTOS官方示例

参数

Returns:


用法示例
/* A function that makes use of a critical section. */
void vDemoFunction( void )
{
    /* Enter the critical section.  In this example, this function is itself called
    from within a critical section, so entering this critical section will result
    in a nesting depth of 2. */
    taskENTER_CRITICAL();

    /* Perform the action that is being protected by the critical section here. */

    /* Exit the critical section.  In this example, this function is itself called
    from a critical section, so this call to taskEXIT_CRITICAL() will decrement the
    nesting count by one, but not result in interrupts becoming enabled. */
    taskEXIT_CRITICAL();
}

/* A task that calls vDemoFunction() from within a critical section. */
void vTask1( void * pvParameters )
{
    for( ;; )
    {
        /* Perform some functionality here. */

        /* Call taskENTER_CRITICAL() to create a critical section. */
        taskENTER_CRITICAL();


        /* Execute the code that requires the critical section here. */


        /* Calls to taskENTER_CRITICAL() can be nested so it is safe to call a
        function that includes its own calls to taskENTER_CRITICAL() and
        taskEXIT_CRITICAL(). */
        vDemoFunction();

        /* The operation that required the critical section is complete so exit the
        critical section.  After this call to taskEXIT_CRITICAL(), the nesting depth
        will be zero, so interrupts will have been re-enabled. */
        taskEXIT_CRITICAL();
    }
}

2.2、使用说明

taskENTER_CRITICAL()
taskEXIT_CRITICAL()

task. h

void taskENTER_CRITICAL( void );

void taskEXIT_CRITICAL( void );

通过调用 taskENTER_CRITICAL() 进入临界区随后 通过调用 taskEXIT_CRITICAL() 退出临界区。

宏 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 提供了一个基本 临界区实现只需禁用中断即可使其全局运作 或在特定的中断优先级范围内运作。 请参阅 vTaskSuspendAll() RTOS API 函数获取有关在不禁用中断的情况下创建临界区的 信息。

如果所使用的 FreeRTOS 移植未使用 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量也称为 configMAX_API_CALL_INTERRUPT_PRIORITY则调用 taskENTER_CRITICAL() 将 全局禁用中断。 如果所使用的 FreeRTOS 移植 使用了 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量 则调用 taskENTER_CRITICAL() 会将中断保留在 由已禁用的 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的中断优先级一下 并启用所有更高优先级的中断。

抢占式上下文切换仅在中断内发生 在中断被禁用时不会发生。 因此可保证 调用 taskENTER_CRITICAL() 的任务维持在运行状态直到 退出临界区除非任务明确试图阻塞或让出 它不应在临界区的内部进行该操作。

对 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 的调用旨在嵌套。 因此只有在执行了一次对 taskEXIT_CRITICAL() 的调用 用于所有先前的 taskENTER_CRITICAL() 调用之后 才会退出临界区。

临界区必须保持非常短否则将影响 中断响应时间。 每次 taskENTER_CRITICAL() 调用都必须紧密配合 taskEXIT_CRITICAL() 调用。

不得从临界区调用 FreeRTOS API 函数。

taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 不得从中断服务程序 (ISR) 调用——请参阅 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR()获取中断安全等效项。

2.3、中断级进入临界段函数与退出临界段函数

函数名taskENTER_CRITICAL_FROM_ISR()-->中断进入

函数名taskEXIT_CRITICAL_FROM_ISR()-->中断退出

FreeRTOS官方示例

参数

uxSavedInterruptStatus taskEXIT_CRITICAL_FROM_ISR() 将 uxSavedInterruptStatus 作为其 唯一参数。 作为 uxSavedInterruptStatus 参数使用的值 必须是从匹配的 taskENTER_CRITICAL_FROM_ISR() 调用返回的值。

taskENTER_CRITICAL_FROM_ISR() 不采用任何 参数。

Returns:

taskENTER_CRITICAL_FROM_ISR() 返回调用宏之前的中断掩码状态 。 taskENTER_CRITICAL_FROM_ISR() 返回的值 必须作为 uxSavedInterruptStatus 参数用于匹配的 taskEXIT_CRITICAL_FROM_ISR() 调用。

taskEXIT_CRITICAL_FROM_ISR() 不返回值。


用法示例
/* A function called from an ISR. */
void vDemoFunction( void )
{
UBaseType_t uxSavedInterruptStatus;

    /* Enter the critical section.  In this example, this function is itself called from
    within a critical section, so entering this critical section will result in a nesting
    depth of 2. Save the value returned by taskENTER_CRITICAL_FROM_ISR() into a local
    stack variable so it can be passed into taskEXIT_CRITICAL_FROM_ISR(). */
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();

    /* Perform the action that is being protected by the critical section here. */

    /* Exit the critical section.  In this example, this function is itself called from a
    critical section, so interrupts will have already been disabled before a value was
    stored in uxSavedInterruptStatus, and therefore passing uxSavedInterruptStatus into
    taskEXIT_CRITICAL_FROM_ISR() will not result in interrupts being re-enabled. */
    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}

/* A task that calls vDemoFunction() from within an interrupt service routine. */
void vDemoISR( void )
{
UBaseType_t uxSavedInterruptStatus;

    /* Call taskENTER_CRITICAL_FROM_ISR() to create a critical section, saving the
    returned value into a local stack variable. */
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();


    /* Execute the code that requires the critical section here. */


    /* Calls to taskENTER_CRITICAL_FROM_ISR() can be nested so it is safe to call a
    function that includes its own calls to taskENTER_CRITICAL_FROM_ISR() and
    taskEXIT_CRITICAL_FROM_ISR(). */
    vDemoFunction();

    /* The operation that required the critical section is complete so exit the
    critical section.  Assuming interrupts were enabled on entry to this ISR, the value
    saved in uxSavedInterruptStatus will result in interrupts being re-enabled.*/
    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}

2.4、使用说明

taskENTER_CRITICAL_FROM_ISR()
taskEXIT_CRITICAL_FROM_ISR()

task. h

UBaseType_t taskENTER_CRITICAL_FROM_ISR( void );

void taskEXIT_CRITICAL_FROM_ISR( UBaseType_t uxSavedInterruptStatus );

taskENTER_CRITICAL() and taskEXIT_CRITICAL() 版本 可用于中断服务程序 (ISR)。

在 ISR 中通过调用 taskENTER_CRITICAL_FROM_ISR() 进入临界区 然后通过调用 taskEXIT_CRITICAL_FROM_ISR() 退出。

taskENTER_CRITICAL_FROM_ISR() 宏和 taskEXIT_CRITICAL_FROM_ISR() 宏提供了 基本临界区的实现只需禁用中断即可使其全局运作 可以是全局禁用也可以是禁用到特定的中断优先级。

如果使用的 FreeRTOS 移植支持中断嵌套则调用 taskENTER_CRITICAL_FROM_ISR() 将在内核配置常量 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的中断优先级或以下级别禁用中断并 启用所有其他中断优先级。 如果使用的 FreeRTOS 移植不支持中断嵌套则 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 将不起作用。

调用 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 旨在用于嵌套但宏的使用方式的语义不同于 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 等效项。

临界区必须保持非常短否则将影响 更高优先级的中断的响应时间会导致该中断嵌套。 每次 taskENTER_CRITICAL_FROM_ISR() 调用都必须紧密配合 taskEXIT_CRITICAL_FROM_ISR() 调用一起使用。

不得从临界区调用 FreeRTOS API 函数。

2.5、特点

1函数成对使用进入临界段后完成临界段代码需要退出临界段

2支持嵌套与只开关中断的最大区别

3保持临界段耗时短如果在这里运行了较长时间相当于是延时了这是实时操作系统不能接受的

4强大且稳定临界区直接屏蔽了中断系统任务调度依靠中断ISR也靠中断把中断关了临界区的代码运行就不会受到干扰了

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

3.1、挂起解释

        挂起任务调度器只导致了调度器无法进行任务调度中断不受影响可正常使用。

3.2、特点

        1与临界区不一样的是挂起任务调度器中断不受影响可正常使用

        2为了防止任务之间的资源争夺

        3挂起调度器适用于临界区位于任务与任务之间不用去延时中断又能做到临界区安全。

3.3、挂起与恢复API函数

函数名vTaskSuspendAll();

函数名xTaskResumeAll();

FreeRTOS官方用法示例

/* A function that suspends then resumes the scheduler. */
void vDemoFunction( void )
{
    /* This function suspends the scheduler.  When it is called from vTask1 the 
    scheduler is already suspended, so this call creates a nesting depth of 2. */
    vTaskSuspendAll();
        
    /* Perform an action here. */
        
    /* As calls to vTaskSuspendAll() are nested, resuming the scheduler here will 
    not cause the scheduler to re-enter the active state. */
    xTaskResumeAll();
}


void vTask1( void * pvParameters )
{
    for( ;; )
    {
        /* Perform some actions here. */
            
        /* At some point the task wants to perform an operation during which it does 
        not want to get swapped out, or it wants to access data which is also 
        accessed from another task (but not from an interrupt).  It cannot use
        taskENTER_CRITICAL()/taskEXIT_CRITICAL() as the length of the operation may
        cause interrupts to be missed. */
            

        /* Prevent the scheduler from performing a context switch. */
        vTaskSuspendAll();
            

        /* Perform the operation here.  There is no need to use critical sections as 
        the task has all the processing time other than that utilized by interrupt 
        service routines.*/           
            
            
        /* Calls to vTaskSuspendAll() can be nested so it is safe to call a (non API) 
        function which also contains calls to vTaskSuspendAll().  API functions 
        should not be called while the scheduler is suspended. */
        vDemoFunction();

            
        /* The operation is complete.  Set the scheduler back into the Active 
        state. */
        if( xTaskResumeAll() == pdTRUE )
        {
            /* A context switch occurred within xTaskResumeAll(). */
        }
        else
        {
            /* A context switch did not occur within xTaskResumeAll(). */
        }
    }
}

3.4、使用说明

vTaskSuspendAll

task. h

void vTaskSuspendAll( void );

挂起调度器。 挂起调度器会阻止上下文切换但会让中断处于启用状态。 如果调度器被挂起时中断请求切换上下文那么请求将会被挂起。而且只有在调度器恢复取消挂起时才会执行。

在 vTaskSuspendAll() 之后调用 xTaskResumeAll() 会转换调度器的状态取消其阻塞状态。

vTaskSuspendAll() 可以嵌套调用。 调用 xTaskResumeAll() 的次数必须与先前调用 vTaskSuspendAll() 的次数相同然后调度器将取消挂起状态并重新进入活动状态。

xTaskResumeAll() 只能在正在执行的任务中调用因此不能在调度器处于初始化状态时启动计划程序之前调用。

不得在调度器挂起时调用其他 FreeRTOS API 函数。

调度器挂起时禁止调用可能切换上下文的 API 函数例如 vTaskDelayUntil()、xQueueSend() 等等 。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6