1 任务创建和删除的API函数
任务的创建和删除本质就是调用F热额RTOS的API函数
动态创建任务:任务的任务管理控制块以及任务栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配(申请的空间是FreeRTOS管理的)
静态创建任务:任务的任务管理控制块以及任务栈空间所需的内存,需用户分配提供(申请的空间是不归FreeRTOS管理的,独立的)
API函数 | 描述 |
xTaskCreat() | 动态方式创建任务 |
xTaskCreatStatic() | 静态方式创建任务 |
vTaskDelete() | 删除任务 |
1.1 动态创建任务函数
BaseType_t xTaskCreate
( TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务名字,最大长度configMAX_TASK_NAME_LEN */
const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈大小,注意字为单位 */
void * const pvParameters, /* 传递给任务函数的参数 */
UBaseType_t uxPriority, /* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */
TaskHandle_t * const pxCreatedTask /* 任务句柄,就是任务的任务控制块 */
)
返回值 | 描述 |
pdPASS | 任务创建成功 |
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 任务创建失败 |
实现动态创建任务流程:
1)将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
2)定义函数入口参数
3)编写任务函数
使用只需要这三步,此函数创建的任务会立刻进入就绪状态,由任务调度器运行。
动态创建任务函数内部实现:
1)申请堆栈内存&任务控制块内存
2)TCB任务控制块结构体成员赋值
3)添加新任务到就绪列表中
1.2 任务控制块结构体成员介绍
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为TCB的第一个成员 */
ListItem_t xStateListItem; /* 任务状态列表项 */
ListItem_t xEventListItem; /* 任务事件列表项 */
UBaseType_t uxPriority; /* 任务优先级,数值越大,优先级越大 */
StackType_t * pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */
…
省略很多条件编译的成员
} tskTCB;
任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关
注意:每个任务都有属于自己的任务控制块,类似于身份证
1.3 静态创建任务函数
TaskHandle_t xTaskCreateStatic
(
TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务函数名 */
const uint32_t ulStackDepth, /* 任务堆栈大小注意字为单位 */
void * const pvParameters, /* 传递的任务函数参数 */
UBaseType_t uxPriority, /* 任务优先级 */
StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
StaticTask_t * const pxTaskBuffer /* 任务控制块指针,由用户分配 */
);
返回值 | 描述 |
NULL | 用户没有提供相应的内存,创建任务失败 |
其他值 | 任务句柄,任务创建成功 |
静态创建任务使用流程;
1)将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
2)定义空闲任务&定时器任务的任务堆栈及TCB
3)实现两个接口函数
-
vApplicationGetIdleTaskMemory( ) 空闲任务内存赋值
-
vApplicationGetTimerTaskMemory ( ) 软件定时器内存赋值
4)定义函数入口参数
5)编写任务函数
使用只需要以上五个步骤,此函数创建的任务会立刻进入就绪状态,由任务调度器调度运行。
静态创建内部实现:
1)TCB结构体成员赋值
2)添加新任务到就绪列表中
1.4 任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete); //xTaskToDelete指的是待删除任务的任务句柄
任务删除函数用于删除已经被成功创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中删除。
注意;
- 当传入的参数为NULL,代表删除任务自身(当前正在运行的任务)
- 空闲任务会负责释放被删除任务中由系统分配的内存(指的是传入参数是NULL的时候,即删除任务本身的时候由空闲任务负责释放内存,当删除任务不是正在运行的任务的时候,是由vTaskDelete()函数释放内存的),但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄漏。
删除任务流程:
1)使用删除任务函数,将宏INCLUDE_vTaskDelete配置为1
2)入口参数输入需要删除的任务句柄(NULL代表删除本身)
内部实现过程:
1)获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除任务本身。
2)将被删除任务移除所在列表:将该任务在所在列表中移除,包括:阻塞、挂起、就绪、事件等列表。
3)判断所需要删除的任务:
- 删除任务本身,需先添加到等待删除列表,内存释放将在空闲任务执行
- 删除其他任务,释放内存,任务数量
4)更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务,然后可以及时更新超时时间。
2 任务创建和删除(动态方法)
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate( (TaskFunction_t ) start_task, //指向任务函数的指针
(char * ) "start_task", //任务名
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE, //堆栈大小,宏定义
(void * ) NULL, //入口参数,一般不设置
(UBaseType_t ) START_TASK_PRIO, //任务优先级,宏定义
(TaskHandle_t * ) &start_task_handler); //任务句柄,即任务控制块
vTaskStartScheduler(); //开启任务调度器,当创建完任务之后,该函数必须开启要不然任务无法调度
}
//定义开始任务函数
//start_task:用来创建其他三个任务,并且只创建一次,创建完成后需要删除掉start_task
void start_task( void * pvParameters )
{
//关闭中断
taskENTER_CRITICAL(); //进入临界区
//创建任务1
xTaskCreate( (TaskFunction_t ) task1, //指向任务函数的指针
(char * ) "task1", //任务名
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE, //堆栈大小,宏定义
(void * ) NULL, //入口参数,一般不设置
(UBaseType_t ) TASK1_PRIO, //任务优先级,宏定义
(TaskHandle_t * ) &task1_handler); //任务句柄,即任务控制块
//创建任务2
xTaskCreate( (TaskFunction_t ) task2, //指向任务函数的指针
(char * ) "task2", //任务名
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE, //堆栈大小,宏定义
(void * ) NULL, //入口参数,一般不设置
(UBaseType_t ) TASK2_PRIO, //任务优先级,宏定义
(TaskHandle_t * ) &task2_handler); //任务句柄,即任务控制块
//创建任务3
xTaskCreate( (TaskFunction_t ) task3, //指向任务函数的指针
(char * ) "task3", //任务名
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE, //堆栈大小,宏定义
(void * ) NULL, //入口参数,一般不设置
(UBaseType_t ) TASK3_PRIO, //任务优先级,宏定义
(TaskHandle_t * ) &task3_handler); //任务句柄,即任务控制块
//创建完三个任务,需要删掉start_task()函数
vTaskDelete(NULL);
taskEXTI_CRITICAL(); //退出临界区
}
//定义task1的任务函数
//task1:用来实现LED0每500ms翻转一次
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE(); //LED0翻转函数
vTaskDelay(500); //系统自带的延时函数
}
}
//定义task2的任务函数
//task2:用来实现LED1每500ms翻转一次
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE(); //LED1翻转函数
vTaskDelay(500); //系统自带的延时函数
}
}
//定义task3的任务函数
//task3:用来判断按键KEY0,按下KEYO删除task1
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key_scan(0); //不支持连按
if(key == KEY0_PRES)
{
if(task1_handler != NULL) //避免重复删除带来的错误
{
printf("删除task1!!!\r\n");
vTaskDelete(task1_handler);
task1_handler =NULL;
}
}
vTaskDelay(10);
}
}
如果上面这段程序中没有临界函数:
taskENTER_CRITICAL(); //进入临界区
taskEXTI_CRITICAL(); //退出临界区
则运行程序结果会如下图所示:
原因如下:
首先创建开始任务,开始任务优先级是最低的,开始任务一创建好之后,就开启任务调度,在开始任务中又创建了三个任务,首先创建task1之后,由于task1的优先级比start_task优先级高,因此会先执行task1的任务,然后进入阻塞之后(延时500ms)task1释放CPU使用权给start_task继续执行,这时候开始创建task2,由于task2优先级也比start_task优先级高,因此会执行task2的任务,然后进入阻塞之后(延时500ms)task2释放CPU使用权给start_task继续执行,这时候开始创建task3,由于task3优先级也比start_task优先级高,因此会执行task3的任务,然后进入阻塞之后(延时10ms)task3释放CPU使用权给start_task继续执行,vTaskDelte()函数。
如果需要一开始高优先级先执行,可以刚进入start_task()的时候关闭任务调度器。任务切换其实是在中断中执行的,FreeRTOS提供了API函数:进入临界区,就是用来关闭中断。进入临界区函数底层其实操作的是basepri寄存器。
因此在start_task中进入临界区,然后等创建完三个任务,退出临界区,才会使得优先级高的先运行,即运行task3。
3 任务创建和删除(静态方法)
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t start_task_handler;
StackType_t start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t start_task_tcb;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task1_handler;
StackType_t task1_stack[TASK1_STACK_SIZE];
StaticTask_t task1_tcb;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task2_handler;
StackType_t task2_stack[TASK2_STACK_SIZE];
StaticTask_t task2_tcb;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128 //单位是字,因此需要*4变成字节
TaskHandle_t task3_handler;
StackType_t task3_stack[TASK3_STACK_SIZE];
StaticTask_t task3_tcb;
void task3( void * pvParameters );
//空闲任务配置
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
//软件定时器任务配置
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
/******************************************************************************************************/
//空闲任务内存分配
//任务控制块内存、任务堆栈内存、任务堆栈的大小
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, //空闲任务任务控制块,用来保存任务的一些特征信息
StackType_t ** ppxIdleTaskStackBuffer, //空闲任务任务堆栈
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack; //自己分配一个数组,或者通过malloc函数申请
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
//软件定时器内存分配
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
uint32_t * pulTimerTaskStackSize )
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
start_task_handler = xTaskCreateStatic( (TaskFunction_t) start_task,
(char * ) "start_task",
(uint32_t ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) start_task_stack,
(StaticTask_t *) &start_task_tcb );
vTaskStartScheduler(); //开启任务调度器,当创建完任务之后,该函数必须开启要不然任务无法调度
}
//定义开始任务函数
//start_task:用来创建其他三个任务,并且只创建一次,创建完成后需要删除掉start_task
void start_task( void * pvParameters )
{
//关闭中断
taskENTER_CRITICAL(); //进入临界区
//创建task1
task1_handler = xTaskCreateStatic( (TaskFunction_t) task1,
(char * ) "task1",
(uint32_t ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(StackType_t * ) task1_stack,
(StaticTask_t *) &task1_tcb );
//创建task2
task2_handler = xTaskCreateStatic( (TaskFunction_t) task2,
(char * ) "task2",
(uint32_t ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(StackType_t * ) task2_stack,
(StaticTask_t *) &task2_tcb );
//创建task3
task3_handler = xTaskCreateStatic( (TaskFunction_t) task3,
(char * ) "task3",
(uint32_t ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(StackType_t * ) task3_stack,
(StaticTask_t *) &task3_tcb );
//创建完三个任务,需要删掉start_task()函数
vTaskDelete(start_task_handler);
taskEXTI_CRITICAL(); //退出临界区
}
//定义task1的任务函数
//task1:用来实现LED0每500ms翻转一次
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE(); //LED0翻转函数
vTaskDelay(500); //系统自带的延时函数
}
}
//定义task2的任务函数
//task2:用来实现LED1每500ms翻转一次
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE(); //LED1翻转函数
vTaskDelay(500); //系统自带的延时函数
}
}
//定义task3的任务函数
//task3:用来判断按键KEY0,按下KEYO删除task1
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key_scan(0); //不支持连按
if(key == KEY0_PRES)
{
if(task1_handler != NULL) //避免重复删除带来的错误
{
printf("删除task1!!!\r\n");
vTaskDelete(task1_handler);
task1_handler =NULL;
}
}
vTaskDelay(10);
}
}