一、任务管理介绍
1.任务状态
1)调度器切换任务调度
2)任务是一个死循环,如果想要停止这个任务则会调用在函数最后写的delete函数进行自杀
1.就绪态
1)已经具备执行的能力,只等待调度器进行调度。
2)新创建的任务会默认设置为就绪态
2.运行态
1)表示此时任务正在运行,占用了CPU资源
2)是就绪任务优先级最高的运行
3.阻塞态
1)等待某一个时序或者中断的时候
2)等待延时,中断
3)已经从就绪列表中被删除
4.挂起态
1)任务被暂时停止,通过调用挂起函数(vTaskSuspend())可以把指定任务挂起,任务挂起后暂时不会运行,只有调用恢复函数(xTaskResume())才可以退出挂起状态;
2)处于挂起态的任务对调度器而言是不可见的
5.挂起 VS 阻塞
挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到调用恢复任务的 接口;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。
2.任务状态转换
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
延时方式:
vTaskDelay
是一个相对延时函数,它使任务进入挂起态,直到至少等待指定的Tick Interrupt(滴答中断)次数。换句话说,它是从调用vTaskDelay
函数开始,延时指定的时间后任务才会重新进入就绪态。vTaskDelayUntil
则是一个绝对延时函数,它会使任务等待到指定的绝对时刻后才会变为就绪态。这意味着任务会等待到指定的时间点,而不是等待某个数量的Tick Interrupt。精度和任务执行:
vTaskDelayUntil
的精度通常比vTaskDelay
高,因为它基于绝对时间进行延时。- 在某些情况下,使用
vTaskDelayUntil
的任务不会丢失执行,而vTaskDelay
可能会导致任务丢失执行。例如,当高优先级的任务长时间占用CPU时,使用vTaskDelay
的任务可能会错过其预定的执行时间。使用场景:
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);//设置系统中断优先级分组4
LED_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);
}
}