FreeRTOS入门教程(任务状态)

举报
yd_274589494 发表于 2023/10/23 21:05:11 2023/10/23
【摘要】 @TOC 前言本篇文章将为大家讲解FreeRTOS中的任务状态,在FreeRTOS任务是有非常多种状态的,了解了任务的这些状态有助于我们理解任务是如何运行和停止的。 一、简单实验在将这个之前我们先来做一个实验来观察任务是怎么样运行的:代码:下面的代码先定义了三个标志位,这三个标志位标志着哪一个任务在运行。static int Task1Flag = 0;static int Task2Fla...

@TOC


前言

本篇文章将为大家讲解FreeRTOS中的任务状态,在FreeRTOS任务是有非常多种状态的,了解了任务的这些状态有助于我们理解任务是如何运行和停止的。

一、简单实验

在将这个之前我们先来做一个实验来观察任务是怎么样运行的:
代码:

下面的代码先定义了三个标志位,这三个标志位标志着哪一个任务在运行。

static int Task1Flag = 0;
static int Task2Flag = 0;
static int Task3Flag = 0;

//任务执行函数
void Task1(void * param)
{
	static int i = 0;
	while (1)
	{
		Task1Flag = 1;
		Task2Flag = 0;
		Task3Flag = 0;
	}
}

//任务执行函数
void Task2(void * param)
{
	while (1)
	{
		Task1Flag = 0;
		Task2Flag = 1;
		Task3Flag = 0;		
	}
}

//任务执行函数
void Task3(void * param)
{
	while (1)
	{
		Task1Flag = 0;
		Task2Flag = 0;
		Task3Flag = 1;		
	}
}

打开keil中的模拟串口:
在这里插入图片描述

打开keil中的逻辑分析仪:
在这里插入图片描述
将定义的三个变量加入逻辑分析仪中:
在这里插入图片描述
将状态都设置为bit:
在这里插入图片描述
设置好后全速运行代码:
我们可以观察到其实这三个任务并不是在同一时刻执行的,而是分开执行,只是执行时间较短我们无法分辨出到底是谁在执行。
在这里插入图片描述
通过观察每个任务的执行时间可以得知每个任务运行的时间大概是1ms左右,这是什么原因呢?

在FreeRTOS中存在一个Tick中断每当发生一次中断时就会判断是否需要进行任务的切换,那么这个Tick中断又是多少ms发生一次呢?

在FreeRTOSConfig这个文件中我们可以找到答案:
在这里插入图片描述
configTICK_RATE_HZ指定了内核时钟节拍的频率,以赫兹(Hz)为单位。

在FreeRTOS中,内核时钟节拍是一种时间标准,它用于测量任务运行时间、等待时间、计时器等功能的时间。内核时钟节拍的频率可以通过configTICK_RATE_HZ来设置,其默认值是1000,表示每秒进行1000次时钟节拍,即时钟节拍的周期为1毫秒。

还有一个问题就是为什么是任务3先执行呢?
在创建任务函数内部可以看到这样一个函数,这个函数会将创建好的任务添加进入就绪链表,后创建的任务在链表的最前面,所以后创建的任务将会被先取出来执行。
在这里插入图片描述

二、任务状态概念讲解

这里使用百问网的一张任务转换图片来讲解:
在这里插入图片描述
在FreeRTOS中,每个任务都有一个状态,表示当前任务的情况。FreeRTOS使用一些特定的宏定义来表示不同的任务状态,这些宏定义包括:

eRunning:表示任务正在运行。

eReady:表示任务处于就绪状态,等待调度器将其调度执行。

eBlocked:表示任务处于阻塞状态,即等待某些事件的发生,例如等待信号量、消息队列、定时器超时等。

eSuspended:表示任务处于暂停状态,即该任务已经被暂停,不参与调度,但它的状态和资源保留,能够在需要时恢复运行。

eDeleted:表示任务已被删除,对应的控制块和栈空间已被释放。

任务状态之间的转换是由FreeRTOS内核自动管理的。任务常常在下列几种情况下会发生状态变化:

创建任务时,任务状态由“未开始”变为“就绪”。

调度器根据任务优先级选取该任务并将其状态变为“运行中”。

任务等待某个事件(如信号量)时,任务状态变为“阻塞”。

任务等待其他任务释放资源时,任务状态可能会转变为“挂起”。

任务自己调用删除函数删除自己时,任务状态变为“已删除”。

三、vTaskDelay和vTaskDelayUntil

1.vTaskDelay

vTaskDelay函数用于使当前任务暂停一段时间之后再继续执行。它的参数是一个整数,表示需要延迟的系统节拍数。例如,在默认的配置下,内核节拍周期为1毫秒,因此vTaskDelay(100)即为使当前任务暂停100毫秒。

需要注意的是,vTaskDelay会引起任务阻塞,同时该延迟时间不是绝对准确的。在等待期间FreeRTOS会尝试进行其他任务的调度,因此实际的延迟时间可能会比指定的时间长。

代码示例:

void Task1(void * param)
{
	static int i = 0;
	while (1)
	{
		printf("Task1\r\n");
		vTaskDelay(1000);
	}
}

//任务执行函数
void Task2(void * param)
{
	while (1)
	{
		printf("Task2\r\n");
		vTaskDelay(2000);
	}
}

//任务执行函数
void Task3(void * param)
{
	while (1)
	{
		printf("Task3\r\n");
		vTaskDelay(3000);
	}
}

观察串口的打印结果:
在这里插入图片描述

2.vTaskDelayUntil

vTaskDelayUntil 是一个精确的定时函数,它使任务等待到特定的时间点才重新变为就绪状态。

调用 vTaskDelayUntil 时,需要提供一个时间戳(以 TickType_t 类型表示),任务将休眠,直到系统时钟达到或超过该时间戳。

这使得任务可以以精确的时间间隔执行,非常适合实时性要求高的应用。

// 定义一个任务,该任务会每隔1秒输出一次消息
void Task1(void* pvParameters)
{
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = pdMS_TO_TICKS(1000); // 1秒的时间间隔

    // 获取当前时间作为初始时间
    xLastWakeTime = xTaskGetTickCount();

    while (1)
    {
        // 执行任务1的操作,例如输出消息
        printf("Task1 is running...\n");

        // 等待到达下一个时间间隔
        vTaskDelayUntil(&xLastWakeTime, xFrequency);
    }
}

int main(void)
{
    // 初始化FreeRTOS内核和硬件

    // 创建任务1
    xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动调度器
    vTaskStartScheduler();

    // 此处不会执行,因为调度器会接管控制权
    return 0;
}

3.vTaskDelay和vTaskDelayUntil的区别

vTaskDelay函数和vTaskDelayUntil函数都用于在FreeRTOS中实现任务的时间延迟,但它们的方式不同。

vTaskDelay函数通过传递一个相对延迟的节拍数来工作。任务会阻塞指定的节拍数,然后继续执行。这意味着vTaskDelay的延迟时间是相对于当前任务的执行时间而言的,实际的延迟时间可能会受到任务切换和系统负载的影响。因此,无法保证精确的延迟时间,可能会有一定的误差。

vTaskDelayUntil函数通过传递一个绝对时间点(以节拍数表示)来工作。任务会等待直到当前时间达到或超过传递的绝对时间点,然后继续执行。这意味着vTaskDelayUntil提供了更精确的延迟控制,可以实现准确的定时任务。您可以根据需要计算下一个执行时间点,并将其传递给vTaskDelayUntil函数,任务将在该时间点进行阻塞,确保精确的延迟时间。

vTaskDelay用于相对延迟,而vTaskDelayUntil用于绝对时间点延迟,使得在实现定时任务时更加方便和精确。

总结

本篇文章就讲解到这里。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。