【FreeRTOS】任务管理

一、任务管理介绍

1.任务状态

1)调度器切换任务调度

2)任务是一个死循环,如果想要停止这个任务则会调用在函数最后写的delete函数进行自杀

1.就绪态

1)已经具备执行的能力,只等待调度器进行调度。

2)新创建的任务会默认设置为就绪态

2.运行态

1)表示此时任务正在运行,占用了CPU资源

2)是就绪任务优先级最高的运行

3.阻塞态

1)等待某一个时序或者中断的时候

2)等待延时,中断

3)已经从就绪列表中被删除

4.挂起态

1)任务被暂时停止,通过调用挂起函数(vTaskSuspend())可以把指定任务挂起,任务挂起后暂时不会运行,只有调用恢复函数(xTaskResume())才可以退出挂起状态

2)处于挂起态的任务对调度器而言是不可见的

5.挂起  VS  阻塞

挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到调用恢复任务的 接口;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。

2.任务状态转换

 1.运行<----->就绪

  1. 从运行态到就绪态的转换:
    当一个任务正在运行时(即处于运行态),如果发生了某个事件(如定时器超时、消息队列接收、信号量释放等),可能会导致该任务释放其占用的资源(如 CPU)并返回到就绪态。这种转换通常是由任务自身触发的,例如,当任务完成其当前工作后,它可能会释放它之前占用的资源并返回到就绪态,等待下一次调度器选择它运行。

此外,当一个更高优先级的任务准备就绪时,当前运行的任务可能会被抢占,从而导致当前任务从运行态转换到就绪态。这是基于优先级的抢占式调度的核心特性。

          2.从就绪态到运行态的转换:
当任务处于就绪态时,它已经准备好运行,但尚未获得 CPU 的使用权。从就绪态到运行态的转换是由调度器触发的。当调度器发现当前运行的任务已经完成其工作并释放了 CPU,或者有一个更高优先级的任务准备就绪时,调度器会选择一个优先级最高的就绪任务,并将其从就绪态转换到运行态。

这个过程涉及上下文切换,即保存当前运行任务的上下文(如寄存器状态、堆栈指针等),并恢复新任务的上下文。然后,新任务开始执行,从它上次停止的地方继续执行。

2.运行-->阻塞

当一个任务处于运行态时,它可能会因为等待某个资源或事件(如信号量、互斥量、消息队列中的消息等)而主动进入阻塞态。这通常发生在任务调用某些可能导致阻塞的API时,如xSemaphoreTake()xQueueReceive()等。

例如,如果一个任务尝试从一个空的消息队列中接收消息,并且该任务被配置为等待消息可用(阻塞接收),那么该任务将从运行态转换到阻塞态,并且会被加入到相应资源(在这个例子中是消息队列)的等待列表中。在阻塞态,任务会释放CPU,使得其他任务可以运行。

3.阻塞--->就绪

当任务所等待的资源或事件变得可用时,任务会从阻塞态转换到就绪态。这通常是由其他任务或中断服务例程(ISR)触发的。

例如,如果一个任务因为等待一个信号量而阻塞,当另一个任务释放该信号量时(通过调用xSemaphoreGive()),处于阻塞态的任务会被唤醒,并从信号量的等待列表中移除,转换到就绪态。此时,任务已经准备好再次运行,但是否能够立即运行取决于其优先级和其他任务的状态

4.就绪,运行,阻塞--->挂起

从运行态到挂起态

当任务处于运行态时,可以通过调用vTaskSuspend()函数来将其挂起。这将导致当前运行的任务立即释放CPU,并且其状态将从运行态转换到挂起态。挂起任务不会立即停止执行,而是在下一次上下文切换时停止。

从就绪态到挂起态

如果任务处于就绪态,同样可以通过调用vTaskSuspend()函数来将其挂起。一旦任务被挂起,它将不再参与调度器的调度,即使它的优先级比其他就绪任务高。

从阻塞态到挂起态

当任务处于阻塞态(等待某个资源或事件),并且该资源或事件尚未变得可用时,任务也可以被挂起。这通常是通过调用vTaskSuspend()函数实现的。在这种情况下,任务将保持其阻塞状态,但同时也会被挂起。一旦任务被挂起,即使资源或事件变得可用,任务也不会被唤醒。

从挂起态的恢复

要从挂起态恢复任务,可以使用vTaskResume()函数。这将把任务从挂起态转换回就绪态(如果任务原本处于就绪态或运行态被挂起),或者从阻塞态转换回阻塞态(如果任务原本因为等待资源或事件而被挂起)。当任务从挂起态恢复时,它将重新参与调度器的调度,并有可能在下一次上下文切换时获得CPU使用权。

二、常用API函数

1.任务挂起函数

1.vTaskSuspend

1)任务被挂起就不会获得CPU的使用权

2)无论任务当前处于什么状态,只要调用挂起函数就都可以进入挂起状态

3)如果这个传入的参数是NULL就表示挂起自己

2.vTaskSuspendAll 

1)这意味着一旦调用此函数,除了正在运行的任务(调用 vTaskSuspendAll() 的任务)之外,其他所有任务都将被暂停,不会获得 CPU 的使用权。

2)中断服务例程(ISR)不受 vTaskSuspendAll() 的影响。即使所有任务都被挂起,中断仍然可以正常响应和处理。

3)使用 xTaskResumeAll() 来恢复所有被 vTaskSuspendAll() 暂停的任务的调度。

4)如果系统配置了时间片轮转(task time slicing),vTaskSuspendAll() 也会暂停时间片的切换。

5)这个函数可以嵌套使用,但是如果调用多少次vTaskSuspendAll()就要调用多少次xTaskResumeAll()来恢复【如果你调用了vTaskSuspendAll() n 次,你就需要调用xTaskResumeAll() n 次来恢复所有任务的调度。

2.任务挂起恢复 

1.vTaskResume

1)任务的恢复就让挂起的任务重新回到就绪状态

2)此函数不能用于中断函数中恢复任务挂起

2.xTaskResumeFromISR

1)在中断里面对任务挂起的恢复操作

2)

 3.任务删除

1.vTaskDelete

1)如果要删除自己,就传入“NULL”

2)如果要删除其他用户则传入对应的句柄即可

3)要使用这个函数,则要先将这个“INCLUDE_vTaskDelete”置为1

4)我们要在删除任务之前,先将这个任务拥有的这个内存先删除,在删除这个任务。要不然这个任务所占的空间不会自动删除,容易造成内存泄漏

4.任务延时函数

1.vTaskDelay

2.vTaskDelayUntil

3.  vTaskDelay    和    vTaskDelayUntil

  1. 延时方式

    • vTaskDelay是一个相对延时函数,它使任务进入挂起态,直到至少等待指定的Tick Interrupt(滴答中断)次数。换句话说,它是从调用vTaskDelay函数开始,延时指定的时间后任务才会重新进入就绪态。
    • vTaskDelayUntil则是一个绝对延时函数,它会使任务等待到指定的绝对时刻后才会变为就绪态。这意味着任务会等待到指定的时间点,而不是等待某个数量的Tick Interrupt。
  2. 精度和任务执行

    • vTaskDelayUntil的精度通常比vTaskDelay高,因为它基于绝对时间进行延时。
    • 在某些情况下,使用vTaskDelayUntil的任务不会丢失执行,而vTaskDelay可能会导致任务丢失执行。例如,当高优先级的任务长时间占用CPU时,使用vTaskDelay的任务可能会错过其预定的执行时间。
  3. 使用场景

    • vTaskDelay适用于需要相对延时的场景,例如,当你想要任务在执行某个操作后等待一段时间再继续执行时。
    • vTaskDelayUntil则适用于需要任务在特定时间点执行的场景,例如,定时任务或需要在特定时间间隔内执行的任务。

三、任务的设计要点

1.中断服务函数

1)在中断函数中不能执行挂起任务

2)不允许执行任何会阻塞运行的API接口函数

3)保持精简短小

4)只能做标记,标记触发其他函数

5)中断过长,会影响其他函数的运行

2.普通任务

1)因为任务是死循环的,所以这里一定要有阻塞的机会,才可以跳转到其他任务

2)将紧急任务的优先级设计较高

3.空闲任务

1)任务创建的时候自动创建空闲任务

2)空闲任务中也不要编写会阻塞运行的API函数

3)主要用于释放资源

4.任务执行时间

1)时间---->任务执行的周期时间,任务需要的时间长短

2)一般来说:处理时间更短的任务优先级要设置的越(因为这样对运行时间长的任务来说根本不值得一提)

四、例子

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define LED1_TASK_PRIO		2
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);//任务优先级
#define LED2_TASK_PRIO		3
//任务堆栈大小	
#define LED2_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);//任务优先级
#define KEY_TASK_PRIO		4
//任务堆栈大小	
#define KEY_STK_SIZE 		50  
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4LED_Init();KEY_Init();USART1_Init(115200);//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建LED1任务xTaskCreate((TaskFunction_t )led1_task,     (const char*    )"led1_task",   (uint16_t       )LED1_STK_SIZE, (void*          )NULL,(UBaseType_t    )LED1_TASK_PRIO,(TaskHandle_t*  )&LED1Task_Handler); //创建LED2任务xTaskCreate((TaskFunction_t )led2_task,     (const char*    )"led2_task",   (uint16_t       )LED2_STK_SIZE, (void*          )NULL,(UBaseType_t    )LED2_TASK_PRIO,(TaskHandle_t*  )&LED2Task_Handler);//创建KEY任务xTaskCreate((TaskFunction_t )key_task,     (const char*    )"key_task",   (uint16_t       )KEY_STK_SIZE, (void*          )NULL,(UBaseType_t    )KEY_TASK_PRIO,(TaskHandle_t*  )&KEYTask_Handler);vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
} //LED1任务函数
void led1_task(void *pvParameters)
{while(1){LED1=0;vTaskDelay(200);LED1=1;vTaskDelay(800);}
}//LED2任务函数
void led2_task(void *pvParameters)
{while(1){LED2=0;vTaskDelay(800);LED2=1;vTaskDelay(200);}
}//KEY任务函数
void key_task(void *pvParameters)
{u8 key=0;while(1){key=KEY_Scan(0);if(key==KEY_UP_PRESS){printf("挂起LED任务!\n");vTaskSuspend(LED2Task_Handler);/* 挂起LED任务 */printf("挂起LED任务成功!\n");}else if(key==KEY1_PRESS){printf("恢复LED任务!\n");vTaskResume(LED2Task_Handler);/* 恢复LED任务!*/printf("恢复LED任务成功!\n");}//将本任务进行挂起vTaskDelay(20);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/491296.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux-并发通信

一.linux-tcp通信框架 1.基础框架 1.1 tcp 服务器框架 1.套接字 #include <sys/socket.h> int socket(int domain, int type, int protocol);
 返回的文件描述符可以指向当前的socket&#xff0c;后续通过对文件描述符的访问就可以配置这个socket 成功时返回文件…

测试C#使用PuppeteerSharp将网页生成PDF文件

微信公众号“DotNet开发跳槽”、“dotNET跨平台”、“DotNet”发布了几篇将网页生成图片或pdf文件的文章&#xff08;参考文献2-5&#xff09;&#xff0c;其中介绍了使用puppeteer-sharp、Select.HtmlToPdf、iTextSharp等多种方式实现html转图片或pdf&#xff0c;正好最近有类…

ABAP - Function ALV 06 红绿黄灯

实际开发中会有一些需求要求添加红绿黄灯列&#xff0c;例如ALVZ中交货单是否已完成拣配、未拣配、未完成拣配等状态进行红绿灯的显示&#xff0c;以求达到ALV简洁明了的目的&#xff0c;显示效果如下图所示。 完成这一功能只需要在结构定义时添加一个字段然后赋值即可。字段类…

STM32 系统滴答时钟启动过程 SysTick_Config

STM32 系统滴答时钟启动过程 SysTick_Config 1. 系统滴答时钟1.1 简介1.2 配置1.3 启动和更新 1. 系统滴答时钟 1.1 简介 SysTick&#xff1a;系统滴答时钟&#xff0c;属于Cortex-M4内核中的一个外设&#xff0c;24bit向下递减计数。 Systick定时器常用来做延时&#xff0c;…

排列组合简单详解(附10题)(会员版)

非会员,不用注册会员也能看! https://blog.csdn.net/Runcode8/article/details/136274861https://blog.csdn.net/Runcode8/article/details/136274861 一、认识C,P,A: A.排列 A(x,y)=(x!)/[(x-y)!]=x(x-1)...(x-y+1) P.排列 P(x,y)=A(x,y) C.组合 C(x,…

NXP实战笔记(九):S32K3xx基于RTD-SDK在S32DS上配置 CRCIRQPower

目录 1、CRC概述 1.1、CRC配置 1.2、代码示例 2、INTCTRL 3、Power 1、CRC概述 硬件CRC产生16或者32bit的&#xff0c;S32K3提供了可编程多项式与其他参数需求。 CRC图示如下 1.1、CRC配置 暂时DMA不怎么会用&#xff0c;所以没有启用DMA CRC的选择 这点需要十分注意&…

Vue | (六)使用Vue脚手架(下)| 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue 中的自定义事件&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;全局事件总线&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;消息订阅与发布&#x1f407;使用方法…

RK3568平台开发系列讲解(Linux系统篇)字符设备驱动:主设备和次设备

🚀返回专栏总目录 文章目录 一、主设备和次设备的概念二、设备号的分配和释放沉淀、分享、成长,让自己和他人都能有所收获!😄 字符设备通过字符(一个接一个的字符)以流方式向用户程序传递数据,就像串行端口那样。字符设备驱动通过/dev目录下的特殊文件公开设备的属性和…

知乎万赞:为什么我不建议你转行学python?_为什么不建议学python

写在前面 本文的目的很简单&#xff0c;一句话&#xff1a; 用最少的时间&#xff0c;最高效率&#xff0c;让你清楚&#xff1a;想要拿到python offer&#xff0c;你需要做什么&#xff1f;你该怎么做&#xff1f;如果你不具备这些条件&#xff0c;我不建议你转行学python&a…

linux系统git常规操作

Git命令常规操作 常用命令说明常用操作示意图文件的状态变化周期 添加文件跟踪文件会添加到.git的隐藏目录由工作区提交到本地仓库查看git的状态提交后的git目录状态 删除文件重命名暂存区数据查看历史记录还原历史数据还原未来数据标签使用对比数据 常用命令说明 命令命令说明…

C++ //练习 8.9 使用你为8.1.2节(第281页)第一个练习所编写的函数打印一个istringstream对象的内容。

C Primer&#xff08;第5版&#xff09; 练习 8.9 练习 8.9 使用你为8.1.2节&#xff08;第281页&#xff09;第一个练习所编写的函数打印一个istringstream对象的内容。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*****…

8.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-游戏底层功能对接类GameProc的实现

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;通过逆向分析确定游戏明文接收数据过程 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;bcf7559184863febdcad819e48aaa…