第04章 任务的创建和删除
约 3214 字大约 11 分钟
2026-01-19
4.1 核心知识点
案例需求
分别使用动态和静态的方法创建三个任务:
task1:实现LED1每500ms闪烁一次。
task2:实现LED2每500ms闪烁一次。
task3:判断按键SW5是否按下,按下则删掉task1。相关宏
configSUPPORT_DYNAMIC_ALLOCATION 是否允许RTOS内核通过动态内存分配方式创建系统对象
configSUPPORT_STATIC_ALLOCATION 是否允许RTOS内核通过静态内存分配方式创建系统对象相关类型:
TaskHandle_t 是一个指向任务控制块(TCB)的指针类型,用于唯一标识一个任务实例
StaticTask_t 是静态任务控制块(TCB)的数据类型,用于存储任务的管理信息(如优先级、栈指针、状态等)
StackType_t 是任务栈内存的基本单元类型,其大小由处理器架构决定(32 位系统为 32 位)相关函数
xTaskCreate() 动态方式创建任务
xTaskCreateStatic() 静态方式创建任务
vTaskDelete() 删除任务
vTaskStartScheduler(); 启动 RTOS 调度器
vTaskDelay() 阻塞延时
taskENTER_CRITICAL() 进入临界区(进入临界区会屏蔽中断)
taskEXIT_CRITICAL() 退出临界区(退出临界区会恢复中断)动态和静态方式创建任务对比:
| 静态分配 | 动态分配 | |
|---|---|---|
| 内存来源 | 用户预分配的全局/静态变量 | FreeRTOS 堆栈 |
| 实时性 | 高(无运行时分配延迟) | 较低(依赖堆分配时间) |
| 内存碎片风险 | 无 | 可能(频繁创建/删除任务时) |
| 调试难度 | 易追踪(内存地址固定) | 需工具检测泄漏/碎片 |
| 典型用例 | 医疗设备、工业控制器 | 物联网网关、临时任务调试 |
4.2 案例1: 动态方式创建任务以及任务删除
① 需求
使用动态的方法创建三个任务:
task1:实现LED1每500ms闪烁一次
task2:实现LED2每500ms闪烁一次
task3:实现LED3每500ms闪烁一次
三个任务优先级相同② CubeMX设置

③ 代码
App_Task.h
#ifndef __APP_TASK_H__
#define __APP_TASK_H__
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Int_Key.h"
/**
* @brief 启动FreeRTOS任务管理
*
*/
void App_Task_Start(void);
#endif /* __APP_TASK_H__ */App_Task.c
#include "App_Task.h"
// 任务1 ------------------------------------
// 任务1函数的原型
void task1_callback(void *pvParameters);
// 任务1名称
#define TASK1_NAME "task1"
// 任务1堆栈大小
#define TASK1_STACK_SIZE 128
// 任务1的优先级
#define TASK1_PRIORITY 1
// 任意1的句柄
TaskHandle_t task1_handle;
// 任务2 ------------------------------------
// 任务2函数的原型
void task2_callback(void *pvParameters);
// 任务2名称
#define TASK2_NAME "task2"
// 任务2堆栈大小
#define TASK2_STACK_SIZE 128
// 任务2的优先级
#define TASK2_PRIORITY 1
// 任意2的句柄
TaskHandle_t task2_handle;
// 任务3 ------------------------------------
// 任务3函数的原型
void task3_callback(void *pvParameters);
// 任务3名称
#define TASK3_NAME "task3"
// 任务3堆栈大小
#define TASK3_STACK_SIZE 128
// 任务3的优先级
#define TASK3_PRIORITY 1
// 任意3的句柄
TaskHandle_t task3_handle;
/**
* @brief 启动 FreeRTOS 任务管理
*
*/
void App_Task_Start(void)
{
// 进入临界区
taskENTER_CRITICAL();
// 创建任务1
xTaskCreate(task1_callback, TASK1_NAME, TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &task1_handle) == pdPASS ? printf("任务1创建成功! \n") : printf("任务1创建失败! \n");
// 创建任务2
xTaskCreate(task2_callback, TASK2_NAME, TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &task2_handle) == pdPASS ? printf("任务2创建成功! \n") : printf("任务2创建失败! \n");
// 创建任务3
xTaskCreate(task3_callback, TASK3_NAME, TASK3_STACK_SIZE, NULL, TASK3_PRIORITY, &task3_handle) == pdPASS ? printf("任务3创建成功! \n") : printf("任务3创建失败! \n");
// 退出临界区
taskEXIT_CRITICAL();
// 启动任务调度器
vTaskStartScheduler();
printf("任务调度器启动... \n");
}
// 任务1函数的实现
void task1_callback(void *pvParameters)
{
printf("任务1启动... \n");
while (1)
{
// 切换LED1状态
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
// 延时500ms
HAL_Delay(500);
}
}
// 任务2函数的实现
void task2_callback(void *pvParameters)
{
printf("任务2启动... \n");
while (1)
{
// 切换LED2状态
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
// 延时500ms
HAL_Delay(500);
}
}
// 任务3函数的实现
void task3_callback(void *pvParameters)
{
printf("任务3启动... \n");
while (1)
{
// 切换LED3状态
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
// 延时500ms
HAL_Delay(500);
}
}main.c
#include "App_Task.h"
int main()
{
printf("FreeRTOS Example ...\n");
// 启动FreeRTOS任务管理
App_Task_Start();
// 启动FreeRTOS后,后面的代码将不会被执行
printf("FreeRTOS How Are You.\n");
}4.3 案例2: 任务优先级
① 需求
使用动态的方法创建三个任务:
task1:实现LED1每500ms闪烁一次,优先级为1
task2:实现LED2每500ms闪烁一次,优先级为2
task3:实现LED3每500ms闪烁一次,优先级为3② 代码
App_Task.c
#include "App_Task.h"
// 任务1 ------------------------------------
// 任务1函数的原型
void task1_callback(void *pvParameters);
// 任务1名称
#define TASK1_NAME "task1"
// 任务1堆栈大小
#define TASK1_STACK_SIZE 128
// 任务1的优先级
#define TASK1_PRIORITY 1
// 任意1的句柄
TaskHandle_t task1_handle;
// 任务2 ------------------------------------
// 任务2函数的原型
void task2_callback(void *pvParameters);
// 任务2名称
#define TASK2_NAME "task2"
// 任务2堆栈大小
#define TASK2_STACK_SIZE 128
// 任务2的优先级
#define TASK2_PRIORITY 2
// 任意2的句柄
TaskHandle_t task2_handle;
// 任务3 ------------------------------------
// 任务3函数的原型
void task3_callback(void *pvParameters);
// 任务3名称
#define TASK3_NAME "task3"
// 任务3堆栈大小
#define TASK3_STACK_SIZE 128
// 任务3的优先级
#define TASK3_PRIORITY 3
// 任意3的句柄
TaskHandle_t task3_handle;
/**
* @brief 启动 FreeRTOS 任务管理
*
*/
void App_Task_Start(void)
{
// 进入临界区
taskENTER_CRITICAL();
// 创建任务1
xTaskCreate(task1_callback, TASK1_NAME, TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &task1_handle) == pdPASS ? printf("任务1创建成功! \n") : printf("任务1创建失败! \n");
// 创建任务2
xTaskCreate(task2_callback, TASK2_NAME, TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &task2_handle) == pdPASS ? printf("任务2创建成功! \n") : printf("任务2创建失败! \n");
// 创建任务3
xTaskCreate(task3_callback, TASK3_NAME, TASK3_STACK_SIZE, NULL, TASK3_PRIORITY, &task3_handle) == pdPASS ? printf("任务3创建成功! \n") : printf("任务3创建失败! \n");
// 退出临界区
taskEXIT_CRITICAL();
// 启动任务调度器
vTaskStartScheduler();
printf("任务调度器启动... \n");
}
// 任务1函数的实现
void task1_callback(void *pvParameters)
{
printf("任务1启动... \n");
while (1)
{
// 切换LED1状态
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
// 延时500ms
HAL_Delay(500);
}
}
// 任务2函数的实现
void task2_callback(void *pvParameters)
{
printf("任务2启动... \n");
while (1)
{
// 切换LED2状态
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
// // 延时500ms
// HAL_Delay(500);
// 阻塞延时,进入阻塞状态并指定500个滴答后(500ms)恢复
vTaskDelay(500);
}
}
// 任务3函数的实现
void task3_callback(void *pvParameters)
{
printf("任务3启动... \n");
while (1)
{
// 切换LED3状态
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
// 延时500ms
// HAL_Delay(500);
// 阻塞延时,进入阻塞状态并指定500个滴答后(500ms)恢复
vTaskDelay(500);
}
}4.4 案例3: 删除任务
需求
使用动态的方法创建三个任务:
task1:实现LED1每500ms闪烁一次。
task2:实现LED2每500ms闪烁一次。
task3:判断按键SW5是否按下,按下则删掉task1。② CubeMX 设置

③ 代码
App_Task.c
#include "App_Task.h"
// 任务1 ------------------------------------
// 任务1函数的原型
void task1_callback(void *pvParameters);
// 任务1名称
#define TASK1_NAME "task1"
// 任务1堆栈大小
#define TASK1_STACK_SIZE 128
// 任务1的优先级
#define TASK1_PRIORITY 1
// 任意1的句柄
TaskHandle_t task1_handle;
// 任务2 ------------------------------------
// 任务2函数的原型
void task2_callback(void *pvParameters);
// 任务2名称
#define TASK2_NAME "task2"
// 任务2堆栈大小
#define TASK2_STACK_SIZE 128
// 任务2的优先级
#define TASK2_PRIORITY 2
// 任意2的句柄
TaskHandle_t task2_handle;
// 任务3 ------------------------------------
// 任务3函数的原型
void task3_callback(void *pvParameters);
// 任务3名称
#define TASK3_NAME "task3"
// 任务3堆栈大小
#define TASK3_STACK_SIZE 128
// 任务3的优先级
#define TASK3_PRIORITY 3
// 任意3的句柄
TaskHandle_t task3_handle;
/**
* @brief 启动 FreeRTOS 任务管理
*
*/
void App_Task_Start(void)
{
// 进入临界区
taskENTER_CRITICAL();
// 创建任务1
xTaskCreate(task1_callback, TASK1_NAME, TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &task1_handle) == pdPASS ? printf("任务1创建成功! \n") : printf("任务1创建失败! \n");
// 创建任务2
xTaskCreate(task2_callback, TASK2_NAME, TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &task2_handle) == pdPASS ? printf("任务2创建成功! \n") : printf("任务2创建失败! \n");
// 创建任务3
xTaskCreate(task3_callback, TASK3_NAME, TASK3_STACK_SIZE, NULL, TASK3_PRIORITY, &task3_handle) == pdPASS ? printf("任务3创建成功! \n") : printf("任务3创建失败! \n");
// 退出临界区
taskEXIT_CRITICAL();
// 启动任务调度器
vTaskStartScheduler();
printf("任务调度器启动... \n");
}
// 任务1函数的实现
void task1_callback(void *pvParameters)
{
printf("任务1启动... \n");
while (1)
{
// 切换LED1状态
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
// 延时500ms
HAL_Delay(500);
}
}
// 任务2函数的实现
void task2_callback(void *pvParameters)
{
printf("任务2启动... \n");
while (1)
{
// 切换LED2状态
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
// // 延时500ms
// HAL_Delay(500);
// 阻塞延时,进入阻塞状态并指定500个滴答后(500ms)恢复
vTaskDelay(500);
}
}
// 任务3函数的实现
void task3_callback(void *pvParameters)
{
printf("任务3(按键轮询)启动... \n");
while (1)
{
// 如果检测到SW3被触发
if (Int_Key_IsSW3Pressed())
{
printf("检测到SW3按键被触发! \n");
// 删除任务1(删除已经被删掉的任务,程序会卡死)
if (task1_handle != NULL)
{
vTaskDelete(task1_handle);
task1_handle = NULL;
}
printf("任务1被删除! \n");
}
// 阻塞延时,进入阻塞状态并指定100个滴答后(100ms)恢复
vTaskDelay(100);
}
}Int_Key.h
#ifndef __INT_KEY_H__
#define __INT_KEY_H__
#include "gpio.h"
/**
* @brief 检测按键SW3是否被触发
*
* @return uint8_t 1 表示按下,0 表示未按下
*/
uint8_t Int_Key_IsSW3Pressed(void);
#endif /* __INT_KEY_H__ */Int_Key.c
#include "Int_Key.h"
/**
* @brief 检测按键SW3是否被触发, 需要轮询调用
*
* @return uint8_t 1 表示按下,0 表示未按下
*/
uint8_t Int_Key_IsSW3Pressed(void)
{
// 如果检测到按键对应的引脚是低电平
if (HAL_GPIO_ReadPin(KEY_SW3_GPIO_Port, KEY_SW3_Pin) == GPIO_PIN_RESET)
{
// 延时10ms用于消抖
HAL_Delay(10);
// 再次检测按键对应的引脚是否是低电平
if (HAL_GPIO_ReadPin(KEY_SW3_GPIO_Port, KEY_SW3_Pin) == GPIO_PIN_RESET)
{
// 等待按键释放(如果按键对应引脚是低电平就一直等待),变为高电平,触发
while (HAL_GPIO_ReadPin(KEY_SW3_GPIO_Port, KEY_SW3_Pin) == GPIO_PIN_RESET)
;
return 1;
}
}
return 0;
}4.5 案例4: 静态方式创建新任务
App_Task.c
#include "App_Task.h"
// 任务1 ------------------------------------
// 任务1函数的原型
void task1_callback(void *pvParameters);
// 任务1名称
#define TASK1_NAME "task1"
// 任务1堆栈大小
#define TASK1_STACK_SIZE 128
// 任务1的优先级
#define TASK1_PRIORITY 1
// 任务1的句柄
TaskHandle_t task1_handle;
// 定义任务1的栈空间(全局数组会在静态区分配空间)
StackType_t task1_stack[TASK1_STACK_SIZE];
// 定义任务1的控制块(全局变量会在静态区分配空间)
StaticTask_t task1_tcb;
// 任务2 ------------------------------------
// 任务2函数的原型
void task2_callback(void *pvParameters);
// 任务2名称
#define TASK2_NAME "task2"
// 任务2堆栈大小
#define TASK2_STACK_SIZE 128
// 任务2的优先级
#define TASK2_PRIORITY 2
// 任务2的句柄
TaskHandle_t task2_handle;
// 定义任务2的栈空间(全局数组会在静态区分配空间)
StackType_t task2_stack[TASK2_STACK_SIZE];
// 定义任务2的控制块(全局变量会在静态区分配空间)
StaticTask_t task2_tcb;
// 任务3 ------------------------------------
// 任务3函数的原型
void task3_callback(void *pvParameters);
// 任务3名称
#define TASK3_NAME "task3"
// 任务3堆栈大小
#define TASK3_STACK_SIZE 128
// 任务3的优先级
#define TASK3_PRIORITY 3
// 任务3的句柄
TaskHandle_t task3_handle;
// 定义任务3的栈空间(全局数组会在静态区分配空间)
StackType_t task3_stack[TASK3_STACK_SIZE];
// 定义任务3的控制块(全局变量会在静态区分配空间)
StaticTask_t task3_tcb;
/**
* @brief 启动 FreeRTOS 任务管理
*
*/
void App_Task_Start(void)
{
// 进入临界区
taskENTER_CRITICAL();
// 使用静态方式创建任务1
task1_handle = xTaskCreateStatic(task1_callback, TASK1_NAME, TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, task1_stack, &task1_tcb);
task1_handle != NULL ? printf("任务1创建成功! \n") : printf("任务1创建失败! \n");
// 使用静态方式创建任务2
task2_handle = xTaskCreateStatic(task2_callback, TASK2_NAME, TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, task2_stack, &task2_tcb);
task2_handle != NULL ? printf("任务2创建成功! \n") : printf("任务2创建失败! \n");
// 使用静态方式创建任务3
task3_handle = xTaskCreateStatic(task3_callback, TASK3_NAME, TASK3_STACK_SIZE, NULL, TASK3_PRIORITY, task3_stack, &task3_tcb);
task3_handle != NULL ? printf("任务3创建成功! \n") : printf("任务3创建失败! \n");
// 退出临界区
taskEXIT_CRITICAL();
// 启动任务调度器
vTaskStartScheduler();
printf("任务调度器启动... \n");
}
// 实现vApplicationGetIdleTaskMemory函数,为空闲任务在静态区分配空间
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{
// 定义StaticTask_t类型静态局部变量作为空闲任务的TCK控制块
static StaticTask_t idle_task_tcb;
// 定义静态局部数组作为空闲任务的栈空间
static StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
// 设置空闲任务的TCK指向静态局部变量
*ppxIdleTaskTCBBuffer = &idle_task_tcb;
// 设置空闲任务的栈空间指向静态局部数组
*ppxIdleTaskStackBuffer = idle_task_stack;
// 设置空闲任务栈空间长度
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
// 任务1函数的实现
void task1_callback(void *pvParameters)
{
printf("任务1启动... \n");
while (1)
{
// 切换LED1状态
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
// 延时500ms
HAL_Delay(500);
}
}
// 任务2函数的实现
void task2_callback(void *pvParameters)
{
printf("任务2启动... \n");
while (1)
{
// 切换LED2状态
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
// // 延时500ms
// HAL_Delay(500);
// 阻塞延时,进入阻塞状态并指定500个滴答后(500ms)恢复
vTaskDelay(500);
}
}
// 任务3函数的实现
void task3_callback(void *pvParameters)
{
printf("任务3(按键轮询)启动... \n");
while (1)
{
// 如果检测到SW3被触发
if (Int_Key_IsSW3Pressed())
{
printf("检测到SW3按键被触发! \n");
// 删除任务1(删除已经被删掉的任务,程序会卡死)
if (task1_handle != NULL)
{
vTaskDelete(task1_handle);
task1_handle = NULL;
}
printf("任务1被删除! \n");
}
// 阻塞延时,进入阻塞状态并指定100个滴答后(100ms)恢复
vTaskDelay(100);
}
}FreeRTOSConfig.js
添加如下配置:
// 允许静态分配方式
#define configSUPPORT_STATIC_ALLOCATION 1