FreeRTOS入门教程(任务状态)

简介: FreeRTOS入门教程(任务状态)

前言

本篇文章将为大家讲解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用于绝对时间点延迟,使得在实现定时任务时更加方便和精确。

总结

本篇文章就讲解到这里。


相关文章
|
数据可视化 中间件 API
FreeRTOS记录(一、熟悉开发环境以及CubeMX下FreeRTOS配置)
熟悉 在 STM32 CubeMX 下面的 FreeRTOS 使用
1259 1
FreeRTOS记录(一、熟悉开发环境以及CubeMX下FreeRTOS配置)
|
5月前
|
消息中间件 存储 数据采集
FreeRTOS入门教程(任务通知)
FreeRTOS入门教程(任务通知)
48 0
|
5月前
|
数据采集 API 数据处理
FreeRTOS入门教程(软件定时器)
FreeRTOS入门教程(软件定时器)
56 0
|
5月前
|
存储 安全
FreeRTOS入门教程(队列的概念及相关函数介绍)
FreeRTOS入门教程(队列的概念及相关函数介绍)
48 0
|
5月前
|
存储 消息中间件 API
FreeRTOS深入教程(软件定时器源码分析)
FreeRTOS深入教程(软件定时器源码分析)
98 0
|
5月前
|
存储
FreeRTOS入门教程(事件组概念和函数使用)
FreeRTOS入门教程(事件组概念和函数使用)
42 0
|
5月前
|
消息中间件 算法 调度
FreeRTOS入门教程(同步与互斥)
FreeRTOS入门教程(同步与互斥)
98 0
|
5月前
|
API
FreeRTOS入门教程(队列详细使用示例)
FreeRTOS入门教程(队列详细使用示例)
51 0
|
5月前
|
算法 调度
FreeRTOS入门教程(互斥锁的概念和函数使用)
FreeRTOS入门教程(互斥锁的概念和函数使用)
92 0
|
5月前
|
API 调度
FreeRTOS深入教程(中断管理)
FreeRTOS深入教程(中断管理)
111 0