学习视频是正点原子FreeRTOS,板子使用的是正点原子STM32f103ZET6精英开发板。
声明:
任务的创建和删除本质就是调用FreeRTOS的API函数
动态和静态方式创建任务区别:动态任务的任务控制块以及任务的栈空间所需的内存由FreeRTOS 管理 的堆中分配。静态任务的任务控制块以及任务的栈空间所需的内存由 用户 分配提供。
动态创建任务
动态创建任务函数
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
。
参数说明
参数 | 说明 |
pvTaskCode | 指向任务入口函数的指针。 |
pcName | 任务的描述性名称。主要是为了用于获取任务句柄。 任务名称的最大长度由 FreeRTOSConfig.h 中的 |
uxStackDepth | 要分配用于任务堆栈的堆栈。例如,如果堆栈的宽度为 16 位,uxStackDepth 为 100,则将分配 200 字节用作该任务的堆栈。 再举一例,如果堆栈的宽度为 32 位,uxStackDepth 为 400,则将分配 1600 字节用作该任务的堆栈。 堆栈深度与堆栈宽度的乘积不得超过 |
pvParameters | 作为参数传递给创建的任务的一个值。如果 pvParameters 设置为变量的地址, 则在执行创建的任务时该变量必须仍然存在——因此 传递堆栈变量的地址是无效的。 |
uxPriority | 创建任务执行的优先级 。在 最大是( |
pxCreatedTask | 用于将句柄传递至由 xTaskCreate() 函数创建的任务 。 pxCreatedTask 是可选的,可设置为 NULL。 |
实现动态创建任务流程
- 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
- 定义函数入口参数
- 编写任务函数
TCB任务控制块
任务TCB(任务控制块),这是一个关键点。它用于存储任务的状态信息,包括任务运行时的环境。任务TCB是一个相对比较大的数据结构。每个任务都有属于自己的任务控制块,类似身份证
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack;
//当前堆栈的栈顶,必须位于结构体的第一项
ListItem_t xStateListItem;
//任务的状态列表项,以引用的方式表示任务的状态
ListItem_t xEventListItem;
//事件列表项,用于将任务以引用的方式挂接到事件列表
UBaseType_t uxPriority;
//保存任务优先级,0表示最低优先级
StackType_t *pxStack;
//指向堆栈的起始位置
char pcTaskName[ configMAX_TASK_NAME_LEN ];
//任务名字
...
省略很多条件编译的成员
} tskTCB;
typedef tskTCB TCB_t;
实践代码
/*在freertosfig.h中*/
#define configSUPPORT_STATIC_ALLOCATION 1 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
/*********************************/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "FreeRTOS.h"
#include "task.h"
#define START_TASK_PRIO 1 //任务优先级为 1
#define START_TASK_STACK_SIZE 128 //开始任务堆栈定义128 字
TaskHandle_t start_task_handler; //定义任务句柄
void start_task(void * pvParameters); //前面声明
#define TASK1_PRIO 2 //任务优先级为 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1(void * pvParameters);
#define TASK2_PRIO 3 //任务优先级为 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2(void * pvParameters);
#define TASK3_PRIO 4 //任务优先级为 4
#define TASK3_STACK_SIZE 128
TaskHandle_t task3_handler;
void task3(void * pvParameters);
void freertos_demo(void)
{
xTaskCreate( (TaskFunction_t ) start_task,
(char * ) "start_task",
(uint16_t ) START_TASK_STACK_SIZE,
(void * ) NULL,
(unsigned long ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler(); //开启调度器
}
void start_task(void * pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
xTaskCreate( (TaskFunction_t ) task1,
(const char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate( (TaskFunction_t ) task2,
(const char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
xTaskCreate( (TaskFunction_t ) task3,
(const char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL); //传入NULL删除自身
taskEXIT_CRITICAL(); //退出临界区
}
//LED0闪烁500ms
void task1(void * pvParameters)
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
//LED1闪烁500ms
void task2(void * pvParameters)
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
//按下KEY0关闭LED0
void task3(void * pvParameters)
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key = key_scan(0);
if(key == KEY0_PRES)
{
vTaskDelete(task1_handler);
}
vTaskDelay(10);
}
}
特殊说明
虽然xTaskCreate()看上去很像函数,但其实是一个宏,真正被调用的函数xTaskGenericCreate(),xTaskCreate()宏定义如下所示:
#define xTaskCreate( pvTaskCode,
pcName,
usStackDepth,
pvParameters,
uxPriority,
pxCreatedTask )
xTaskGenericCreate( ( pvTaskCode ),
( pcName ),
( usStackDepth ),
( pvParameters ),
( uxPriority ),
( pxCreatedTask),
( NULL ),
( NULL ),
( NULL ) )
可以看到xTaskCreate比xTaskGenericCreate少了三个参数,在宏定义中,这三个参数被设置为NULL。这三个参数用于使用静态变量的方法分配堆栈、任务TCB空间以及设置MPU相关的参数。一般情况下,这三个参数是不使用的,所以任务创建宏xTaskCreate定义的时候,将这三个参数对用户隐藏了。接下来的章节中,为了方便,我们还是称xTaskCreate()为函数,虽然它是一个宏定义。
静态创建任务
静态创建任务函数
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 // 任务控制块指针,由用户分配
);
返回
如果 puxStackBuffer
和 pxTaskBuffer
均不为 NULL,则创建任务,并返回任务的句柄。 如果 puxStackBuffer
或 pxTaskBuffer
为 NULL,则不会创建任务,并返回 NULL。
参数说明
pxTaskCode | 指向任务入口函数的指针(即实现任务的函数名称,请参阅如下示例)。 |
pcName | 任务的描述性名称。此参数主要用于获取任务句柄。任务名称的最大长度由 FreeRTOSConfig.h 中的 configMAX_TASK_NAME_LEN 定义。 |
ulStackDepth | puxStackBuffer 参数用于将 StackType_t 变量数组传递给 xTaskCreateStatic() 。必须将 ulStackDepth 设置为数组中的索引数。 |
pvParameters | 传递给已创建任务的参数值。如果将 pvParameters 设置为变量的地址,则在创建的任务执行时变量必须仍然存在,因此传递堆栈变量的地址无效。 |
uxPriority | 所创建任务执行的优先级。最大是( configMAX_priority - 1)。 |
puxStackBuffer | 必须指向至少具有 ulStackDepth 索引的 StackType_t 数组,该数组用作任务的堆栈,因此必须是永久性的(而不是在函数的堆栈上声明)。 |
pxTaskBuffer | 必须指向 StaticTask_t 类型的变量。该变量用于保存新任务的数据结构体 (TCB) ,因此必须是持久的(而不是在函数的堆栈中声明)。 |
实现静态创建任务流程
- 需将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
- 定义空闲任务&定时器任务的任务堆栈及TCB
- 实现两个接口函数 vApplicationGetIdleTaskMemory( ) vApplicationGetTimerTaskMemory ( )
- 定义函数入口参数
- 编写任务函数
实践代码
/*在freertosfig.h文件中*/
#define configSUPPORT_STATIC_ALLOCATION 1 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
#define configMINIMAL_STACK_SIZE 128
/* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2)
/* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */
/*********************************/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "FreeRTOS.h"
#include "task.h"
#define START_TASK_PRIO 1 //任务优先级为 1
#define START_TASK_STACK_SIZE 128 //开始任务堆栈定义128 字
TaskHandle_t start_task_handler; //定义任务句柄
StackType_t start_task_stack[START_TASK_STACK_SIZE]; //定义任务堆栈
StaticTask_t start_task_tcb; //定义任务控制块
void start_task(void * pvParameters); //提前声明
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
StackType_t task1_stack[TASK1_STACK_SIZE];
StaticTask_t task1_tcb;
void task1(void * pvParameters);
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
StackType_t task2_stack[TASK2_STACK_SIZE];
StaticTask_t task2_tcb;
void task2(void * pvParameters);
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
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;
* 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;
}
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();
}
void start_task(void * pvParameters)
{
taskENTER_CRITICAL();//进入临界区
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_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_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
);
vTaskDelete(start_task_handler); //使用传入句柄删除函数
taskEXIT_CRITICAL(); //退出临界区
}
//任务一:LED0 500ms翻转一次
void task1(void * pvParameters)
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
//任务二:LED1 500ms翻转一次
void task2(void * pvParameters)
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
//任务三:判断按键KEY0,按下KEY0删除task1
void task3(void * pvParameters)
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key = key_scan(0);
if(key == KEY0_PRES)
{
vTaskDelete(task1_handler);
}
vTaskDelay(10);
}
}
特殊说明
静态创建函数的句柄是静态创建函数的返回值
任务句柄是一个指向任务控制块(Task Control Block,TCB)的指针
删除任务
void vTaskDelete (TaskHandle_t xTaskToDelete);
功能:
用于删除已被创建的任务、被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
注意:
- 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)
- 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露
删除任务流程
- 使用删除任务函数,需将宏INCLUDE_vTaskDelete 配置为 1
- 入口参数输入需要删除的任务句柄(NULL代表删除本身), 传递 NULL 将导致调用任务被删除
推荐博文
FreeRTOS学习笔记02-任务创建和删除(动态方法和静态方法)_rtos 静态创建 动态创建 任务 优缺点-CSDN博客