Freertos学习03-Task状态

发布时间 2023-06-26 23:19:07作者: seekwhale13

一、前言

  FreeRTOS是一个流行的实时操作系统,它支持多任务处理。在FreeRTOS中,任务有不同的状态,这些状态反映了任务在系统中的行为。

二、状态特点

任务可以存在于以下状态中:

  • 运行
    当任务实际执行时,它被称为处于运行状态。 任务当前正在使用处理器。 如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。

  • 准备就绪
    准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。

  • 阻塞
    如果任务当前正在等待时间或外部事件,则该任务被认为处于阻塞状态。 例如,如果一个任务调用vTaskDelay(),它将被阻塞(被置于阻塞状态), 直到延迟结束-一个时间事件。 任务也可以通过阻塞来等待队列、信号量、事件组、通知或信号量 事件。 处于阻塞状态的任务通常有一个"超时"期, 超时后任务将被超时,并被解除阻塞, 即使该任务所等待的事件没有发生。

“阻塞”状态下的任务不使用任何处理时间,不能 被选择进入运行状态。

  • 挂起
    与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。 相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态。
    image
      以上几种状态中需要注意挂起态和阻塞态,函数可以通过调用vTaskDelay()函数来进入阻塞态,挂起态则通过vTaskSuspend()进入挂起态

三、实例测试

1.相关函数

1)挂起函数vTaskSuspend(),这个函数可以将任务从就绪、运行、以及阻塞态置入挂起态,输入参数为任务的句柄,将函数写在任务函数的内部可以将自己挂起,此时输入参数无须句柄,为NULL。
image

2)挂起所有任务函数vTaskSuspendAll(),这个函数的工作原理是系统维护一个全局变量uxSchedulerSuspended的计数值,对于FreeRTOS来说,task的切换是在中断中发生的,如果中断进来,中断中会对uxSchedulerSuspended这个变量的值进行判断,从而决定是否需要进行Task切换。当其大于0的时候禁止调度,等于0的时候表示允许调度。
  如果调度器挂起话,当前正在执行的Task会一直继续执行,内核不再调度(意味着当前任务不会被切换出去),直到该任务调用了xTaskResumeAll()函数。
注意在使用vTaskSuspendAll()挂起其余任务区间,不允许调用其他FreeRTOS API 函数。
image
3)恢复函数vTaskResume(),挂起函数相对,恢复函数可以将处于挂起状态的函数恢复至就绪态,注意只能是就绪态。使用时只需要传入句柄即可。
image

4)恢复所有函数xTaskResumeAll(),可与vTaskSuspendAll()配合使用,将所有函数的挂起态解除。
image

2.代码测试

测试1:该例子在任务创建之后先将task1挂起,经短暂延迟恢复,所以可以观察到有一段只有task2输出,task1无输出:

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"

void mytask1(void *pvParam)
{
	while (1)
	{
		printf("111-task1 \n");
		vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时
	}
}

void mytask2(void *pvParam)
{
	while (1)
	{
		printf("222-task2 \n");
		vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时
	}
}

void app_main(void)
{
	TaskHandle_t xHandle = NULL; // 任务句柄

	xTaskCreate(mytask1, "mytask1", 1024, NULL, 1, &xHandle); // 传入参数
	xTaskCreate(mytask2, "mytask2", 1024, NULL, 2, NULL);	  // 传入参数
	vTaskDelay(2000 / portTICK_PERIOD_MS);					  // 延时
	vTaskSuspend(xHandle);									  // 挂起
	vTaskDelay(2000 / portTICK_PERIOD_MS);					  // 延时
	vTaskResume(xHandle);									  // 恢复
}

image
测试2:测试vTaskSuspendAll()xTaskResumeAll(),有时我们需要保证某个任务运行期间不被打扰,例如对都某时间点的各传感器数据进行采样,因为要保证传感器数据尽量在同一时刻采集,所以此时我们可以停止其他任务调度,等待采集结束后再恢复。

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"

void mytask1(void *pvParam)
{

	printf("task1-begin \n");
	vTaskSuspendAll(); // 挂起所有任务
	for (int i = 0; i < 9999; i++)
	{
		for (int j = 0; j < 9999; j++)
		{
			; // 模拟不可中断任务
		}
	}

	xTaskResumeAll(); // 恢复所有任务
	printf("task1-over \n");
	vTaskDelete(NULL);
}

void mytask2(void *pvParam)
{
	while (1)
	{
		printf("222-task2 \n");
		vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时
	}
}

void app_main(void)
{
	TaskHandle_t xHandle = NULL; // 任务句柄

	xTaskCreate(mytask1, "mytask1", 2048, NULL, 1, &xHandle); // 传入参数
	xTaskCreate(mytask2, "mytask2", 2048, NULL, 2, NULL);	  // 传入参数
}

测试结果:符合预期。
image

THE END!