第05章 任务的挂起与恢复
约 1523 字大约 5 分钟
2026-01-19
5.1 核心知识点
相关宏:
INCLUDE_vTaskSuspend 是否启用任务挂起和恢复功能
configUSE_TRACE_FACILITY 是否启用内核跟踪和调试功能,扩展任务状态信息采集
configUSE_STATS_FORMATTING_FUNCTIONS 允许使用格式化函数(如 vTaskList())将调试信息转换为可读文本相关函数:
vTaskSuspend() 挂起任务, 类似暂停,可恢复
vTaskResume() 恢复被挂起的任务
xTaskResumeFromISR() 在中断服务程序中恢复被挂起的任务
vTaskSuspendAll() 挂起调度器;挂起调度器后无法切换任务,将只执行当前任务(注意不是最高优先级任务)
xTaskResumeAll() 恢复调度器
vTaskList() 获取任务状态vTaskList() 的结果:

'B' - 已阻塞
'R' - 准备就绪
'D' - 已删除(等待清理)
'S' - 已挂起
'X' - 运行态5.2 案例
① 需求
创建三个任务:
task1:实现LED1每500ms闪烁一次, 优先级1
task2:实现LED2每500ms闪烁一次,优先级4(用于证明调度器挂起后,执行的是当前任务不是优先级最高的任务)
task3:判断按键按下逻辑,优先级3
SW3按下,挂起task1,SW4按下在任务中恢复task1
SW5按下,挂起调度器,SW6按下恢复调度器② 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 4
// 任务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");
vTaskStartScheduler();
}
// 任务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");
// 定义字符串
char task_info[256];
while (1)
{
// 检测按键是否按下
switch (Int_Key_IsDetect())
{
case 3:
// 按键SW3被触发,挂起任务1
printf("SW3被触发,挂起任务1 ... \n");
vTaskSuspend(task1_handle);
break;
case 4:
// 按键SW4被触发,恢复任务1
printf("SW4被触发,恢复任务1 ... \n");
vTaskResume(task1_handle);
break;
case 5:
// 按键SW5被触发,挂起调度器
printf("SW5按键被触发! 挂起调度器 ... \n");
vTaskSuspendAll();
break;
case 6:
// 按键SW6被触发,恢复调度器
printf("SW6按键被触发! 恢复调度器 ... \n");
xTaskResumeAll();
break;
default:
break;
}
// 获取任务状态信息并打印
vTaskList(task_info);
printf("任务状态信息:\n%s \n", task_info);
printf("任务状态信息长度:%d \n", strlen(task_info));
// 阻塞延时,进入阻塞状态并指定500个滴答后(500ms)恢复
vTaskDelay(500);
}
}Int_Key.h
#ifndef __INT_KEY_H__
#define __INT_KEY_H__
#include "gpio.h"
/**
* @brief 检测按键否被触发
*
* @return uint8_t 0 表示没有按键被按下;3表示Sw3被按下;4表示Sw4被按下;5表示Sw5被按下;6表示Sw6被按下
*/
uint8_t Int_Key_IsDetect(void);
#endif /* __INT_KEY_H__ */Int_Key.c
#include "Int_Key.h"
/**
* @brief 检测按键否被触发
*
* @return uint8_t 0 表示没有按键被按下;3表示Sw3被按下;4表示Sw4被按下;5表示Sw5被按下;6表示Sw6被按下
*/
uint8_t Int_Key_IsDetect(void)
{
// 检测SW3 如果检测到按键对应的引脚是低电平
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 3;
}
}
// 检测SW4 如果检测到按键对应的引脚是低电平
if (HAL_GPIO_ReadPin(KEY_SW4_GPIO_Port, KEY_SW4_Pin) == GPIO_PIN_RESET)
{
// 延时10ms用于消抖
HAL_Delay(10);
// 再次检测按键对应的引脚是否是低电平
if (HAL_GPIO_ReadPin(KEY_SW4_GPIO_Port, KEY_SW4_Pin) == GPIO_PIN_RESET)
{
// 等待按键释放(如果按键对应引脚是低电平就一直等待),变为高电平,触发
while (HAL_GPIO_ReadPin(KEY_SW4_GPIO_Port, KEY_SW4_Pin) == GPIO_PIN_RESET)
;
return 4;
}
}
// 检测SW5 如果检测到按键对应的引脚是高电平
if (HAL_GPIO_ReadPin(KEY_SW5_GPIO_Port, KEY_SW5_Pin) == GPIO_PIN_SET)
{
// 延时10ms用于消抖
HAL_Delay(10);
// 再次检测按键对应的引脚是否是高电平
if (HAL_GPIO_ReadPin(KEY_SW5_GPIO_Port, KEY_SW5_Pin) == GPIO_PIN_SET)
{
// 等待按键释放(如果按键对应引脚是高电平就一直等待),变为低电平,触发
while (HAL_GPIO_ReadPin(KEY_SW5_GPIO_Port, KEY_SW5_Pin) == GPIO_PIN_SET)
;
return 5;
}
}
// 检测SW6 如果检测到按键对应的引脚是高电平
if (HAL_GPIO_ReadPin(KEY_SW6_GPIO_Port, KEY_SW6_Pin) == GPIO_PIN_SET)
{
// 延时10ms用于消抖
HAL_Delay(10);
// 再次检测按键对应的引脚是否是高电平
if (HAL_GPIO_ReadPin(KEY_SW6_GPIO_Port, KEY_SW6_Pin) == GPIO_PIN_SET)
{
// 等待按键释放(如果按键对应引脚是高电平就一直等待),变为低电平,触发
while (HAL_GPIO_ReadPin(KEY_SW6_GPIO_Port, KEY_SW6_Pin) == GPIO_PIN_SET)
;
return 6;
}
}
return 0;
}FreeRTOSConfig.h
添加配置
// 开启追踪工具
#define configUSE_TRACE_FACILITY 1
// 开启格式化函数
#define configUSE_STATS_FORMATTING_FUNCTIONS 1