当前位置: 首页 > news >正文

【STM32】FreeRTOS任务的挂起与解挂(四)

在 FreeRTOS 中,任务挂起(Suspend)与解挂(Resume) 是一种手动控制任务是否参与调度的机制。它可以临时“冻结”某个任务,使其不再被调度器运行,直到被恢复。

任务挂起与解挂的核心 API

挂起:
https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/02-Task-control/06-vTaskSuspend

解挂:
https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/02-Task-control/07-vTaskResume

函数说明
vTaskSuspend()挂起指定任务
vTaskResume()解挂(恢复)指定任务
vTaskResumeFromISR()从中断服务程序(ISR)中恢复任务
vTaskSuspend()
void vTaskSuspend(TaskHandle_t xTaskToSuspend);//作用:将指定任务挂起,使其不再参与调度。
//参数:
//`xTaskToSuspend`:任务句柄。若为 `NULL`,则挂起当前任务。
//注意:挂起任务后,直到调用 `vTaskResume()` 才会恢复运行。
vTaskResume()
void vTaskResume(TaskHandle_t xTaskToResume);//作用:恢复被挂起的任务,使其重新参与调度。
//参数:
//xTaskToResume:要解挂的任务句柄。
//注意:若任务没有被挂起,调用此函数无效。
vTaskResumeFromISR()
BaseType_t vTaskResumeFromISR(TaskHandle_t xTaskToResume);//作用:从 中断服务函数(ISR) 中恢复任务。
//返回值:若恢复的任务优先级更高,返回 pdTRUE,需在中断末尾调用 portYIELD_FROM_ISR()。

挂起与解挂的运行机制

首先,我们必须搞清楚在FreeRTOS中,任务状态是一个重要的概念,它决定了每个任务当前所处的状态以及调度器如何管理任务的执行。

请添加图片描述

FreeRTOS定义了多种任务状态,每个任务在任何时间点都处于以下状态之一:

1. 运行中 (Running):

任务正在CPU上执行。系统中同一时刻只有一个任务可以处于运行状态(在单核系统中)。

2. 就绪 (Ready):

任务已经准备好运行,等待调度器分配CPU时间。就绪状态的任务不处于任何阻塞状态并且满足优先级条件。

3. 阻塞 (Blocked):

任务因为等待某个事件而无法继续执行。常见的阻塞原因包括等待信号量、队列消息、事件组或超时事件。
阻塞任务在事件发生或超时到达时会被转移到就绪状态。

4. 挂起 (Suspended):

任务被挂起,除非被显式唤醒(通过调用 vTaskResume() 或 vTaskResumeFromISR()),否则不会被调度执行。
挂起状态与阻塞状态的区别在于挂起状态任务不会自动因为事件发生而恢复。

5. 删除 (Deleted):

任务已被删除,资源等待被系统回收。被删除的任务不再被调度执行。
调用 vTaskDelete() 会将任务置于删除状态。

说明:

  1. 挂起任务后,它将被移出就绪列表。
  2. 被挂起的任务不会运行,即使它的优先级最高。
  3. 解挂后,任务会重新加入就绪列表,调度器判断是否立即切换。

挂起任务只是暂停运行,不会释放堆栈或删除任务:❌ 不会释放资源。

状态转换的过程:

		  [就绪态]vTaskResume()┌───────┘
[挂起态] ←───── vTaskSuspend()

实际代码示例

示例一:任务的挂起与解挂:

代码说明:这段代码是基于 STM32F10x 标准外设库 和 FreeRTOS 的一个多任务模版,从FreeRTOS的创建任务、任务挂起/恢复、以及对 GPIO(LED 和按键)的控制。

函数说明:
✅ 系统初始化				初始化串口、GPIO、按键等外设
✅ 创建任务					创建 start_task 用于初始化系统任务
✅ 动态创建任务				start_task 中动态创建 led0_task 和 led1_task
✅ 任务控制					led0_task 通过按键挂起/恢复 led1_task
✅ LED 控制					led1_task 控制 GPIOC.7 周期性闪烁
/********************************************************************************* @file    Project/STM32F10x_StdPeriph_Template/main.c * @author  MCD Application Team* @version V3.5.0* @date    08-April-2011* @brief   Main program body******************************************************************************* @attention** THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.** <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>*******************************************************************************/  /* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h" 
#include <stdio.h>
#include <stdbool.h>
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"		
#include "gpio.h"//------------------------- start_task --------------------------------------------//
void 			start_task(void *pvParameters);			//任务函数入口
TaskHandle_t StartTask_Handler;							//任务句柄	_任务身份证_每个任务都有独立的任务
#define 	START_STK_SIZE  64							//任务堆栈大小
#define 	START_TASK_PRO	1							//任务优先级
//-----------------------------------------------------------------------void 	led0_task(void *pvParameters);					//任务函数入口
TaskHandle_t LED0_Task_Handler;							//任务句柄
//-----------------------------------------------------------------------void 	led1_task(void *pvParameters);					//任务函数入口
TaskHandle_t LED1_Task_Handler;							//任务句柄int main(void)
{NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );	//RTOS需要将中断优先级分组分配到第四组usart_init(115200);									// 初始化串口MX_GPIO_Init();										// 初始化 GPIOMX_Key_Init();										// 初始化按键printf("Create Task! \r\n");//------------------------- start_task --------------------------------------------//// 创建启动任务xTaskCreate((TaskFunction_t) 	start_task,						        //任务函数入口(const char *	 )  "start_task",				//任务函数名称(uint16_t      ) 	START_STK_SIZE,				//任务堆栈大小(void *    	   )  NULL,							//任务参数入口(UBaseType_t 	 )  START_TASK_PRO,				//任务优先级(TaskHandle_t *) 	&StartTask_Handler );       //任务句柄vTaskStartScheduler(); 	 // 启动调度器(永不返回)while(1)	// 不会执行到这里{}}// -------------------------- 启动任务 start_task -------------------------- 
void start_task(void *pvParameters) 	//任务函数入口
{printf("start Task Run! \r\n");taskENTER_CRITICAL();		//进入临界区,因为创建的任务不能被打断,所以开启临界区之后,中断会被屏蔽	
//------------------------- 创建 LED0 任务 --------------------------------------------//xTaskCreate((TaskFunction_t) 	led0_task,							//任务函数入口(const char *	 )  "led0_task",			//任务函数名称(uint16_t      ) 	128,					//任务堆栈大小(void *    	   )  NULL,						//任务参数入口(UBaseType_t 	 )  2,						//任务优先级(TaskHandle_t *) 	&LED0_Task_Handler );   //任务句柄
//------------------------- 创建 LED1 任务 --------------------------------------------//xTaskCreate((TaskFunction_t) 	led1_task,							//任务函数入口(const char *	 )  "led1_task",			//任务函数名称(uint16_t      ) 	128,					//任务堆栈大小(void *    	   )  NULL,						//任务参数入口(UBaseType_t 	 )  3,						//任务优先级(TaskHandle_t *) 	&LED1_Task_Handler );   //任务句柄vTaskDelete(StartTask_Handler);		//删除start_task 任务句柄taskEXIT_CRITICAL();	//退出临界区//临界区保护:防止在任务创建期间被调度打断。//任务自删除:start_task 完成任务后自我销毁,节省资源。
}void led0_task(void *pvParameters)
{printf("led0_task Run! \r\n");while(1){//按键PA0按下if(GPIO_ReadInputDataBit( GPIOA, GPIO_Pin_0 ) == Bit_RESET){vTaskSuspend(LED1_Task_Handler);		//将 LED1_Task 挂起printf("led1_task is Suspend! \r\n");}if(GPIO_ReadInputDataBit( GPIOC, GPIO_Pin_1 ) == Bit_RESET){vTaskResume(LED1_Task_Handler);			//将 LED1_Task 解挂printf("led1_task is Resume! \r\n");}vTaskDelay(10); // 延迟 10 tick,避免按键抖动}
}void led1_task(void *pvParameters)
{printf("led1_task Run! \r\n");while(1){GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_RESET); // 开灯vTaskDelay(500);GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_SET); 	 // 关灯vTaskDelay(500);vTaskDelay(10);}//控制 LED(GPIOC.7)每 0.5 秒闪烁一次//被 led0_task 动态挂起与恢复
}
/*//这部分注释是潜在的 led_blue_task:说明你可以在任务运行时继续扩展更多功能,如蓝灯控制等等,while(1) //锁死当前线程,但并不影响其他线程的运行{for(int i = 0;i<5;i++){GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_RESET);  //绿灯开启vTaskDelay(500);GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_SET); 		//绿灯关闭vTaskDelay(500);printf("start_task Run Count; %d \r\n", i);}vTaskDelay(10);taskENTER_CRITICAL();		//进入临界区,因为创建的任务不能被打断,所以开启临界区之后,中断会被屏蔽//------------------------- led_B_task --------------------------------------------////初始化函数xTaskCreate((TaskFunction_t) 	led_blue_task,						//任务函数入口(const char *	 )  "led_blue_task",					//任务函数名称(uint16_t      ) 	32,												//任务堆栈大小(void *    	   )  NULL,											//任务参数入口(UBaseType_t 	 )  2,												//任务优先级_值越大优先级越高(TaskHandle_t *) 	LED_BLUE_TASK_Handler );  //蓝灯任务句柄vTaskDelete(StartTask_Handler);		//删除start_task 任务句柄taskEXIT_CRITICAL();		//退出临界区}
*//******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
示例二:挂起和恢复其他任务:
TaskHandle_t xTaskHandle;void vControlledTask(void *pvParameters)
{for (;;){printf("任务运行中...\n");vTaskDelay(pdMS_TO_TICKS(1000));}
}void vControllerTask(void *pvParameters)
{// 挂起任务vTaskSuspend(xTaskHandle);vTaskDelay(pdMS_TO_TICKS(5000));// 解挂任务vTaskResume(xTaskHandle);// 控制任务自身结束vTaskDelete(NULL);
}void main_app()
{xTaskCreate(vControlledTask, "TaskA", 128, NULL, 2, &xTaskHandle);xTaskCreate(vControllerTask, "Controller", 128, NULL, 3, NULL);vTaskStartScheduler();
}
示例三:任务自挂起

⚠️ 注意:如果任务挂起自己,必须由其他任务或中断来恢复它!自己是无法为自己解挂的。

void vSelfSuspendingTask(void *pvParameters)
{for (;;){printf("执行一次后挂起...\n");// 挂起自己vTaskSuspend(NULL);}
}
示例四:中断中恢复任务
void EXTI0_IRQHandler(void)
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;vTaskResumeFromISR(xTaskHandle);  // 恢复高优先级任务portYIELD_FROM_ISR(xHigherPriorityTaskWoken);  // 若有必要切换上下文
}

FreeRTOS任务的挂起与解挂可用于控制执行时机,比如等待外部事件、同步信号等;当被挂起的任务即使有更高优先级也不会抢占;这可以与中断配合使用, 能够在中断中恢复任务(vTaskResumeFromISR())。

vTaskSuspend() 可将任务挂起,暂停其调度执行,vTaskResume()vTaskResumeFromISR() 可将其恢复。这种机制适合对任务执行时机进行精确控制,但在多数场景下使用信号量/事件组更优雅。

以上,便是 FreeRTOS任务的挂起与解挂。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

http://www.lryc.cn/news/599522.html

相关文章:

  • ServletRegistrationBean相关知识点
  • IIC协议详解与项目注意事项
  • Ant Design 结合 React 参考 Vben 逻辑实现的描述列表组件封装实践
  • C#模拟pacs系统接收并解析影像设备数据(DICOM文件解析)
  • 【unitrix】 6.16 非负整数类型( TUnsigned )特质(t_unsingned.rs)
  • Docker镜像导入解析:docker import vs docker load
  • 2025最新蜘蛛池在百度SEO中的应用
  • vue2+node+express+MongoDB项目安装启动启动
  • ELK Stack技术栈
  • 前端基础知识Vue系列 - 27(Vue项目中如何解决跨域)
  • 养老服务行业怎么解决采购管理难题?
  • 配置Mac/Linux终端启动执行脚本
  • 数据赋能(332)——安全与合规——保密管理
  • 代码随想录day45dp12
  • 读书笔记8:供应链思维下的企业战略与数字生态
  • OpenCV(04)梯度处理,边缘检测,绘制轮廓,凸包特征检测,轮廓特征查找
  • MCP协议详细教程
  • The Magic Mask for Android:解锁无限可能的安卓自定义套件
  • 【面试场景题】外卖点餐系统设计思路
  • [MMU]四级页表查找(table walk)的流程
  • SQL性能优化
  • 【LeetCode Solutions】LeetCode 热题 100 题解(16 ~ 20)
  • 系统编程——文件IO
  • SpringBoot整合Fastexcel/EasyExcel导出Excel导出多个图片
  • 面向对象编程实战:Python打造你的数码宠物世界
  • Java NIO FileChannel在大文件传输中的性能优化实践指南
  • 盟接之桥说制造:构建以预防为核心的供应链客诉管理体系
  • GitHub git push 推送大文件
  • 【第四章:大模型(LLM)】01.Embedding is all you need-(6)从 Word2Vec 到推荐/广告系统,再到大语言模型(LLM)
  • Three.js 控制器和交互设计:OrbitControls + Raycaster 实战