目录
一、任务创建和删除的API函数
任务的创建和删除本质就是调用 FreeRTOS 的 API 函数
API函数 | 描述 |
xTaskCreate() | 动态方式创建任务 |
xTaskCreateStatic() | 静态方式创建任务 |
xTaskDelete() | 删除任务 |
动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从 FreeRTOS 管理的堆中分配
静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供
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、添加新任务到就绪列表中
任务控制块结构体成员介绍
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.2、静态创建任务函数
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.3、任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete);
形参 | 描述 |
xTaskToDelete | 待删除任务的任务句柄 |
用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除
1、当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)
2、当要删除任务自身时,空闲任务会负责释放被删除任务中由系统分配的内存(动态创建任务),但是由用户在任务删除前申请的内存(静态创建任务),则需要由用户在任务被删除前提前释放,否则将导致内存泄漏
删除任务流程
1、使用删除任务函数,需将宏 INCLUDE_vTaskDelete 配置为 1
2、入口参数输入需要删除的任务句柄(NULL代表删除本身)
删除任务函数内部实现
1、获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
2、将被删除任务,移除所在列表:将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表
3、判断所需要删除的任务:删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行。删除其他任务,释放内存,任务数量
4、更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
二、任务创建和删除(动态方法)
将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下
start_task:用来创建其他三个任务
task1:实现 LED0 每 500ms 闪烁一次
task2:实现 LED1 每 500ms 闪烁一次
task3:判断按键 KEY0 是否按下,按下则删掉 task1,判断按键 KEY1 是否按下,按下则删掉 task2
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "freertos_demo.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
freertos_demo();
}
freertos_demo.c
#include "freertos_demo.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4 /* 任务优先级 */
#define TASK3_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task3Task_Handler; /* 任务句柄 */
void task3(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/* FreeRTOS例程入口函数 */
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Task Create & Del", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 110, 115, 314, BLACK);
lcd_draw_rectangle(125, 110, 234, 314, BLACK);
lcd_draw_line(5, 130, 115, 130, BLACK);
lcd_draw_line(125, 130, 234, 130, BLACK);
lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);
lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);
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();
}
/* start_task */
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t)task1, /* 任务函数 */
(const char *)"task1", /* 任务名称 */
(uint16_t)TASK1_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK1_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task1Task_Handler); /* 任务句柄 */
/* 创建任务2 */
xTaskCreate((TaskFunction_t)task2, /* 任务函数 */
(const char *)"task2", /* 任务名称 */
(uint16_t)TASK2_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK2_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task2Task_Handler); /* 任务句柄 */
/* 创建任务3 */
xTaskCreate((TaskFunction_t)task3, /* 任务函数 */
(const char *)"task3", /* 任务名称 */
(uint16_t)TASK3_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK3_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task3Task_Handler); /* 任务句柄 */
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* task1 */
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while (1)
{
lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);
lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* task2 */
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
while (1)
{
lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);
lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* task3 */
void task3(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES: /* 删除任务1 */
{
if (Task1Task_Handler != NULL)
{
vTaskDelete(Task1Task_Handler);
Task1Task_Handler = NULL;
}
break;
}
case KEY1_PRES: /* 删除任务2 */
{
if (Task2Task_Handler != NULL)
{
vTaskDelete(Task2Task_Handler);
Task2Task_Handler = NULL;
}
break;
}
default:
{
break;
}
}
vTaskDelay(10);
}
}
freertos_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LCD/lcd.h"
#include "FreeRTOS.h"
#include "task.h"
void freertos_demo(void);
#endif
三、任务创建和删除(静态方法)
将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下
start_task:用来创建其他三个任务
task1:实现 LED0 每 500ms 闪烁一次
task2:实现 LED1 每 500ms 闪烁一次
task3:判断按键 KEY0 是否按下,按下则删掉 task1,判断按键 KEY1 是否按下,按下则删掉 task2
main.c、freertos_demo.h 和动态创建一样
freertos_demo.c
#include "freertos_demo.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; /* 空闲任务任务堆栈 */
static StaticTask_t IdleTaskTCB; /* 空闲任务控制块 */
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; /* 定时器服务任务堆栈 */
static StaticTask_t TimerTaskTCB; /* 定时器服务任务控制块 */
/**
* @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;
}
/**
* @brief 获取定时器服务任务的任务堆栈和任务控制块内存
* @param ppxTimerTaskTCBBuffer:任务控制块内存
ppxTimerTaskStackBuffer:任务堆栈内存
pulTimerTaskStackSize:任务堆栈大小
* @retval 无
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &TimerTaskTCB;
*ppxTimerTaskStackBuffer = TimerTaskStack;
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/* 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); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
StackType_t Task1TaskStack[TASK1_STK_SIZE]; /* 任务堆栈 */
StaticTask_t Task1TaskTCB; /* 任务控制块 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
StackType_t Task2TaskStack[TASK2_STK_SIZE]; /* 任务堆栈 */
StaticTask_t Task2TaskTCB; /* 任务控制块 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4 /* 任务优先级 */
#define TASK3_STK_SIZE 128 /* 任务堆栈大小 */
StackType_t Task3TaskStack[TASK3_STK_SIZE]; /* 任务堆栈 */
StaticTask_t Task3TaskTCB; /* 任务控制块 */
TaskHandle_t Task3Task_Handler; /* 任务句柄 */
void task3(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/* FreeRTOS例程入口函数 */
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Task Create & Del", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 110, 115, 314, BLACK);
lcd_draw_rectangle(125, 110, 234, 314, BLACK);
lcd_draw_line(5, 130, 115, 130, BLACK);
lcd_draw_line(125, 130, 234, 130, BLACK);
lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);
lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);
StartTask_Handler = xTaskCreateStatic((TaskFunction_t)start_task, /* 任务函数 */
(const char *)"start_task", /* 任务名称 */
(uint32_t)START_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传递给任务函数的参数 */
(UBaseType_t)START_TASK_PRIO, /* 任务优先级 */
(StackType_t *)StartTaskStack, /* 任务堆栈 */
(StaticTask_t *)&StartTaskTCB); /* 任务控制块 */
vTaskStartScheduler();
}
/* start_task */
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(); /* 退出临界区 */
}
/* task1 */
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while (1)
{
lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);
lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* task2 */
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
while (1)
{
lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);
lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* task3 */
void task3(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES: /* 删除任务1 */
{
if (Task1Task_Handler != NULL)
{
vTaskDelete(Task1Task_Handler);
Task1Task_Handler = NULL;
}
break;
}
case KEY1_PRES: /* 删除任务2 */
{
if (Task2Task_Handler != NULL)
{
vTaskDelete(Task2Task_Handler);
Task2Task_Handler = NULL;
}
break;
}
default:
{
break;
}
}
vTaskDelay(10);
}
}