【FreeRTOS(三)】任务状态

任务状态

freeRTOS任务的状态有四种:运行、就绪、阻塞、挂起

  • 运行态(runnnig):当任务正在运行,此时的状态被称为运行态,即CPU的使用权被这个任务占用;
  • 挂起态(suspended):任务被暂时停止,通过调用挂起函数(vTaskSuspend())可以把指定任务挂起,任务挂起后暂时不会运行,只有调用恢复函数(xTaskResume())才可以退出挂起状态;
  • 阻塞态(blocked):任务在等待信号量、消息队列、事件标准组、系统延时时,被称为阻塞态,如果等待的事件到了,就会自动退出阻塞态,准备运行;
  • 就绪态(ready):任务已经具备了运行条件(没有被挂起或阻塞),但是又更高优先级或同优先级的任务正在运行,所以需要等待的状态。

在这里插入图片描述

一般任务创建完成后,进入就绪态;处于就绪态的任务,如果没有更高优先级或同优先级的任务正在运行,它会自动进入运行态;如果有更高优先级的任务要运行,或者同优先级的任务要轮流运行,它会从运行态返回到就绪态;如果任务需要等待某个事件,或者任务自己进入了系统延时,则会进入阻塞态;当等待的事件达到后,任务又会进入就绪态。

注意事项:在阻塞状态中,任务可以等待两种类型的事件:

  • 时间相关的事件。
    • 可以是等待一段时间,比如等3秒钟。使用vTaskDelay函数实现,等待的时间到了任务就会变为就绪态
    • 也可以是等到某个绝对的时间点。使用vTaskDelayUntil函数实现,等到了某个时间点任务就会进入就绪态
  • 同步事件,由其他任务或者中断产生,比如任务A给任务B发送数据,或者任务A等待用户按下按键。同步事件的来源很多,如下:
    • 队列(queue)
    • 二进制信号量(binary semaphores)
    • 互斥量(mutexes)
    • 递归互斥量、递归锁(recursive muxtexes)
    • 事件组(event groups)
    • 任务通知(task notifications)

任务挂起 vTaskSuspend

挂起任务,当挂起一个任务时,不管优先级是多少,不需要占用任何微控制器处理器时间。调用vTaskSuspend不会累积——即:在统一任务中调用vTaskSuspend两次,但只需调用一次vTaskResume来是挂起的任务就绪。

void vTaskSuspend( xTaskHandle pxTaskToSuspend);
parameter description
pxTaskToSuspend 处理需要挂起的任务。传递NULL将挂起调用此函数的任务。
return

取消任务挂起 vTaskResume

取消挂起的任务。 必须是调用 vTaskSuspend后挂起的任务,才有可能通过调用 vTaskResume重新运行。

void vTaskResume(xTaskHandle pxTaskToResume);
parameter description
pxTaskToResume 就绪的任务的句柄
return

挂起任务调度器 vTaskSuspendAll

挂起任务调度器,挂起任务调度器可以防止发生上下文切换,但保留启用中断的状态。如果一个中断在调度程序挂起时请求上下文切换,那么该请求被保持为挂起状态,并且只有在任务调度器恢复(非挂起)时才执行。注意一下:

  • 在挂起任务调度器时,其他FreeRTOS API函数是不能被调用的,自己的函数可以被调用。
  • 注意系统看门狗喂狗
void vTaskSuspendAll(void);
parameter description
None
return

取消挂起任务调度器 xTaskResumeAll

在使用 vTaskSuspendAll 挂起任务调度器后,使用此函数可恢复任务调度器。

BaseType_t xTaskResumeAll(void);
parameter description
None
return 是否恢复

代码示例:任务挂起、取消任务挂起

创建两个优先级为1的任务,再将task1挂起,延时3秒后取消挂起

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

void task1(void *pvParam)
{
    while (1)
    {
        printf("task1!\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void task2(void *pvParam)
{
    while (1)
    {
        printf("task2!\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;

    xTaskCreate(task1, "task1", 4096, NULL, 1, pxTask);
    xTaskCreate(task2, "task2", 4096, NULL, 1, NULL);

    vTaskSuspend(pxTask);
   
    vTaskDelay(3000 / portTICK_PERIOD_MS);

    vTaskResume(pxTask);

}

代码示例:挂起任务调度器、取消挂起任务调度器

创建两个优先级为1的任务,进入task1,准备for循环时,挂起任务调度器,防止其他任务干扰,待for循环结束时,取消挂起任务调度器

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

void task1(void *pvParam)
{

    printf("Test begin!\n");
    vTaskSuspendAll();

    for(int i = 0; i < 9999; i++)
    {
        for(int j = 0; j < 9999; j++)
        {
            ;
        }
    }

    xTaskResumeAll();
    printf("Test end\n");

    vTaskDelete(NULL);
}

void task2(void *pvParam)
{
    while (1)
    {
        printf("task2!\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
    xTaskCreate(task2, "task2", 4096, NULL, 1, NULL);
}

版权声明:本文为K3169原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>