• 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;		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    打开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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3.vTaskDelay和vTaskDelayUntil的区别

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

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

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

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

    总结

    本篇文章就讲解到这里。

  • 相关阅读:
    1317. 将整数转换为两个无零整数的和
    ABP微服务系列学习-搭建自己的微服务结构(三)
    正点原子LORA模块ATK-01固件升级与断电不丢失设置参数
    为React Ant-Design Table增加字段设置 | 京东云技术团队
    【网络教程】IPtables官方教程--学习笔记4
    golang设计模式——访问者模式
    slambook2+ubuntu20.04(第九章-第十章)
    训练好的深度学习模型,多种部署方式
    fetch网络请求详解
    _Linux理解软硬链接
  • 原文地址:https://blog.csdn.net/m0_49476241/article/details/130451344