一.静态创建任务
1.创建空闲任务
空闲任务:是由启用freertos任务调度器时内核自动生成的,确保系统内至少存在一个任务正在运行。
空闲任务的优先级永远是最低的,确保不会影响到当前系统其他的任务的运行,空闲任务一直处于就绪态,或者运行态两种之一。当系统的其它任务转为“就绪态”,则会“抢占”空闲任务进行执行。
空闲任务在删除任务函数结束之后,会自动释放掉删除任务分配的内存空间。解释:当其他的运行的任务执行到vTaskDelay();这个函数的时候,空闲任务就可以切进来,释放掉删除任务分配的内存空间了。
当存在和空闲任务都为0优先级的任务,空闲任务会礼让,让别的0级任务进行执行。
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; /* 空闲任务任务堆栈 */
static StaticTask_t IdleTaskTCB; /* 空闲任务控制块 */
/**
* @brief 获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
实现此函数即可。
* @param ppxIdleTaskTCBBuffer:任务控制块内存
ppxIdleTaskStackBuffer:任务堆栈内存
pulIdleTaskStackSize:任务堆栈大小
* @retval 无
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &IdleTaskTCB;
*ppxIdleTaskStackBuffer = IdleTaskStack;
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
2.系统设置
需要在 FreeRTOSconfig.h文件中
#define configSUPPORT_STATIC_ALLOCATION 1
/* 1: 支持静态申请内存, 默认: 0 */
3.创建任务
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
StackType_t StartTaskStack[START_STK_SIZE]; /* 任务堆栈 */
StaticTask_t StartTaskTCB; /* 任务控制块 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
任务优先级:在freertos里面优先级有五种0-4,但一般选择都是分组4,总共为2^ 4-1,因为这样不用去考虑子优先级,方便了很多,只需要通过抢占优先级的大小直接判断进行任务的抢占,freertos任务的优先级数值越小,任务的优先级越低。
任务堆栈:分配给当前任务的运行空间,因为是静态分配所以由用户自行设置,每个任务的堆栈都是自行分离的,数据也是隔离的,当新建一个freertos任务的时候,将分配空间的首地址传入到TCB,当任务删除的时候,堆栈也会被空闲任务释放掉。堆栈大小需要根据程序任务的实际情况进行分配,当出现堆栈溢出的时候,会触发堆栈溢出监测机制,系统会进行异常处理,存储相关的信息进行调试。
任务控制块(TCB):里面存放该任务对应的所有信息(堆栈指针,任务名称,任务形参等),任务控制块相当于是任务的身份证。当产生中断的时候,TCB会保存当时中断,寄存器以及指针等各种信息,当中断结束,再次执行任务的时候,会直接从TCB里面存储的信息直接开始执行。
任务句柄:这是一个二级指针,指向的是对应创建的TCB的首地址,如果是操作自己,则传入的可以是NULL。
任务函数:必须是一个无返回值且可以无限循环的函数。
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
Task1Task_Handler = xTaskCreateStatic((TaskFunction_t )task1, /* 任务函数 */
(const char* )"task1", /* 任务名称 */
(uint32_t )TASK1_STK_SIZE,/* 任务堆栈大小 */
(void* )NULL, /* 传递给任务函数的参数 */
(UBaseType_t )TASK1_PRIO, /* 任务优先级 */
(StackType_t* )Task1TaskStack,/* 任务堆栈 */
(StaticTask_t* )&Task1TaskTCB);/* 任务控制块 */
/* 创建任务2 */
Task2Task_Handler = xTaskCreateStatic((TaskFunction_t )task2, /* 任务函数 */
(const char* )"task2", /* 任务名称 */
(uint32_t )TASK2_STK_SIZE,/* 任务堆栈大小 */
(void* )NULL, /* 传递给任务函数的参数 */
(UBaseType_t )TASK2_PRIO, /* 任务优先级 */
(StackType_t* )Task2TaskStack,/* 任务堆栈 */
(StaticTask_t* )&Task2TaskTCB);/* 任务控制块 */
/* 创建任务3 */
Task3Task_Handler = xTaskCreateStatic((TaskFunction_t )task3, /* 任务函数 */
(const char* )"task3", /* 任务名称 */
(uint32_t )TASK3_STK_SIZE,/* 任务堆栈大小 */
(void* )NULL, /* 传递给任务函数的参数 */
(UBaseType_t )TASK3_PRIO, /* 任务优先级 */
(StackType_t* )Task3TaskStack,/* 任务堆栈 */
(StaticTask_t* )&Task3TaskTCB);/* 任务控制块 */
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
4.任务调度
vTaskStartScheduler();
freertos无非是四种状态,运行态,就绪态,阻塞态,挂起态
运行态:正在执行的任务,stm32一次只能执行一个任务,因为只有一个cpu。
就绪态:任务可以被执行,准备好被执行,但当前CPU在执行别的任务,此任务还未被执行。
阻塞态:任务因为延时或者中断,导致进入阻塞状态。
挂起态:暂停当前函数的运行。要调用函数vTaskSuspend()进入挂起状态,调用vTaskResume()进行解挂,转入就绪态。
freertos的两种调度方式:
·抢占式调度:高优先级的任务可以抢占低优先级的任务,被抢占的任务会进入就绪态
·时间片调度:当多个任务的优先级相同的时候,任务调度器会根据系统时钟节拍进行切换任务,每个时间片执行一个任务。当时间片结束时切换任务,但当运行任务的期间,该任务被中断或者延时进入阻塞状态,则会直接执行下一个任务。
5.临界区
必须完整运行,不可以被打断的代码。每次有且仅有一个进程进入到临界区,当任务进入后,别的进程需要等待该进程出临界区才可以继续进入一个,进入临界区的进程必须在有限的时间退出,当进程不可以进入自己的临界区,需要让出CPU,避免出现"忙等"的现象。
二.动态创建任务
1.系统配置
#define configSUPPORT_STATIC_ALLOCATION 0 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
区别:因为是动态创建任务,所以堆栈空间是由freertos内核分配堆栈空间的,所以只需要设置任务句柄去指向TCB任务控制块的首地址。
xTaskCreate((TaskFunction_t )task1, /* 任务函数 */
(const char* )"task1", /* 任务名称 */
(uint16_t )TASK1_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )TASK1_PRIO, /* 任务优先级 */
(TaskHandle_t* )&Task1Task_Handler); /* 任务句柄 */
其余配置和静态分配任务一样。
三.删除任务
#define INCLUDE_vTaskDelete 1 /* 删除任务 */
删除任务只需要删除对应任务的任务句柄,上文中提到TCB是一个一级指针且分配了内存空间,里面存放了每个任务的指针位置,寄存器等所有信息。任务句柄是二级指针,指向TCB首地址。所以通过任务句柄可以找到对应的任务。
vTaskDelete(task)
当你需要在任务中删除别的任务,直接task == 对应任务的任务句柄,删除自己本身,task == NULL,当执行vTaskDelete(),删除的是任务本身,释放对应的堆栈空间,需要在执行空闲任务中释放。
注意的是:不管删除的任务处于什么状态,删除函数执行后,会直接从队列中消失。