当前位置: 首页> 娱乐> 八卦 > STM32F1+HAL库+FreeTOTS学习4——任务挂起与恢复

STM32F1+HAL库+FreeTOTS学习4——任务挂起与恢复

时间:2025/7/11 8:43:01来源:https://blog.csdn.net/weixin_67907028/article/details/140161506 浏览次数:0次

STM32F1+HAL库+FreeTOTS学习4——任务挂起与恢复

  • 任务挂起和恢复的API介绍
  • 代码实现

上一期我们学习了FreeRTOS中任务创建的两种方法,这一期我们学习任务的挂起和恢复。

任务挂起和恢复的API介绍

在 :STM32F1+HAL库+FreeTOTS学习1——FreeRTOS入门 的学习中,我们有了解到FreeRTOS的状态分为就绪、阻塞、挂起和运行四种状态,那如何将处于就绪态的任务挂起呢?我们有对应的函数:

  1. 挂起任务函数
void vTaskSuspend(TaskHandle_t xTaskToSuspend);/*
参数是需要挂起的任务句柄,当传入的参数是NULL时,代表任务自身挂起。
*/

使用前需要将宏 : INCLUDE_vTaskSuspend 配置为1,且无论优先级如何,被挂起的任务都不会被执行,知道任务恢复。

内部实现如下:
在这里插入图片描述

  1. 任务恢复函数(任务中恢复)
void vTaskResume(TaskHandle_t xTaskToResume);/*
传入参数是需要恢复的任务句柄。
*/

使用前需要将宏 : INCLUDE_vTaskSuspend 配置为1 ,且无论该任务被挂起多少次,只需要调用一次vTaskResume函数即可恢复该任务,且该任务立即进入就绪态。

内部实现如下:
在这里插入图片描述

  1. 任务恢复函数(中断中恢复)
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
/*
参数依旧是需要恢复的任务句柄,但是该函数存在返回值
当返回值为 pdTRUE时,表示改任务恢复后需要进行任务切换, 需要调用portYIELD_FROM_ISR函数切换到更高优先级的任务。
当返回值为 pdFALSE 时,表示任务恢复后不需要进行任务切换。
*/
  • 使用前需要将宏 : INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 定义为1
  • 该函数专门用在中断服务函数中,用于解挂(恢复)被挂起的任务
  • 中断服务程序中要调用freeRTOS的API函数则该中断的优先级不能高于FreeRTOS所管理的最高优先级:简单理解就是中断的优先级必须在 5 到 15之间 。
  • 并且中断的优先级分组必须是4位全用做抢占优先级,无子优先级。

代码实现

再看代码实现之前,我们先明确以下实现的功能,不然有的时候看的云里雾里的:

  • 创建四任务,一个开始任务,三个运行任务。
  • 再开始任务中创建三个运行任务,创建完成后删除开始任务。
  • 任务1控制LED0闪烁,1s闪一次
  • 任务2负责串口打印,打印系统运行时间。
  • 按键3负责按键扫描,按键0按下挂起任务1,按键1按下恢复任务1,打印相关信息。
  • WK_UP按键用作外部中断,在中断里面恢复任务1,并打印相关信息

在这里插入图片描述

以上就是需要实现的内容,由于本期内容使用到按键,所有这里展示以下一些按键的CubeMX配置:

  • 按键部分:这里只展示按键0,按键1和0一样即可
    在这里插入图片描述
  • WK_UP按键:配置外部输入,优先级为5(必须要设置优先级再5到15之前,且优先级分组为4)
    在这里插入图片描述
    在这里插入图片描述

其他部分的配置这里就不展示了,在直接在配好的工程上该即可。下面来看代码

  1. main.c :须在main函数里面调用 freertos_demo()函数进入FreRTOS系统。
freertos_demo();
  1. freertos_demo.c
#include "freertos_demo.h"
#include "main.h"
/*FreeRTOS*********************************************************************************************/#include "key.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      1                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      1                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *//* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_PRIO      1                   /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
void task3(void *pvParameters);             /* 任务函数 *//******************************************************************************************************//* LCD刷屏时使用的颜色 *//*** @brief       FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{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();		//开启任务调度
}/*** @brief       start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
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();            /* 退出临界区,重新开启中断,开启任务调度 */
}/*** @brief       task1* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{while(1){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);  printf("task1 start\r\n");/* LED0闪烁 */vTaskDelay(1000);                                               /* 延时1000ticks */}
}/*** @brief       task2* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{float float_num = 0.0;while(1){float_num += 0.01f;                         /* 更新数值 */printf("float_num: %0.4f\r\n", float_num);  /* 打印数值 */printf("task2 start\r\n");vTaskDelay(1000);                           /* 延时1000ticks */}
}/*** @brief       task2* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task3(void *pvParameters)
{vTaskDelay(100);while(1){		Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task);         //扫描Key0状态Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task);         //扫描Key1状态Key_One_Scan(Key_Name_Key2,Key2_Up_Task,Key2_Down_Task);         //扫描Key2状态}
}
  1. key.c (这里面用来实现软件方式按键扫描)
/* USER CODE BEGIN 2 */#include "freertos_demo.h"
#include "key.h"
#include "usart.h"
void Key0_Down_Task(void)
{vTaskSuspend(Task1Task_Handler);printf("任务1挂起\r\n");
}
void Key0_Up_Task(void)
{}
void Key1_Down_Task(void)
{printf("任务1解挂\r\n");
}
void Key1_Up_Task(void)
{vTaskResume(Task1Task_Handler);  
}
void Key2_Down_Task(void)
{}
void Key2_Up_Task(void)
{}
void WKUP_Down_Task(void)
{}
void WWKUP_Up_Task(void)
{}void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{static uint8_t Key_Val[Key_Name_Max];    //按键值的存放位置static uint8_t Key_Flag[Key_Name_Max];   //KEY0~2为0时表示按下,为1表示松开,WKUP反之Key_Val[KeyName] = Key_Val[KeyName] <<1;  //每次扫描完,将上一次扫描的结果左移保存switch(KeyName){case Key_Name_Key0:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin));    //读取Key0按键值break;case Key_Name_Key1:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin));   //读取Key1按键值break;case Key_Name_Key2:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin));   //读取Key2按键值break;
//        case Key_Name_WKUP:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin));   //读取WKUP按键值
//            break; default:break;}
//    if(KeyName == Key_Name_WKUP)     //WKUP的电路图与其他按键不同,所以需要特殊处理
//    {
//        //WKUP特殊情况
//        //当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xff
//        if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1)
//        {
//            (*OnKeyOneDown)();
//           Key_Flag[KeyName] = 0;
//        }
//        //当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00
//        if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0)
//        {
//            (*OnKeyOneUp)();
//           Key_Flag[KeyName] = 1;
//        } 
//    }
//    else                               //Key0~2按键逻辑判断
//    {//Key0~2常规判断//当按键标志为1(松开)是,判断是否按下if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 1){(*OnKeyOneDown)();Key_Flag[KeyName] = 0;}//当按键标志位为0(按下),判断按键是否松开if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 0){(*OnKeyOneUp)();Key_Flag[KeyName] = 1;}  }//}
/* USER CODE END 2 */
  1. key.h (lkey.c搭配使用)
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* USER CODE BEGIN Private defines */
typedef enum{Key_Name_Key0 = 0,Key_Name_Key1,Key_Name_Key2,Key_Name_WKUP,Key_Name_Max}EnumKeyOneName;/* USER CODE END Private defines */void MX_GPIO_Init(void);/* USER CODE BEGIN Prototypes */
void Key0_Down_Task(void);void Key0_Up_Task(void);void Key1_Down_Task(void);void Key1_Up_Task(void);void Key2_Down_Task(void);void Key2_Up_Task(void);void WKUP_Down_Task(void);void WWKUP_Up_Task(void);void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void)); 
/* USER CODE END Prototypes */#ifdef __cplusplus
}
#endif /*__ GPIO_H__ */

值得注意的是,按键扫描部分代码取自 : STM32框架之按键扫描新思路 并稍作修改,具体实现原理可以自行了解。

  1. gpio.c (这里用来完成中断方式的恢复任务。)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{BaseType_t xYieldRequired;if(GPIO_Pin == WK_UP_Pin )       //判断中断源为WKUP中断{xYieldRequired = 	xTaskResumeFromISR(Task1Task_Handler);if(xYieldRequired == pdTRUE){portYIELD_FROM_ISR(xYieldRequired);printf("任务1解挂中断方式\r\n");}}   }
关键字:STM32F1+HAL库+FreeTOTS学习4——任务挂起与恢复

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: