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

news2024/10/6 20:35:49

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");
		 }

    }   

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1890549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Webpack: 核心流程之Init、Make、Seal

概述 在前文中&#xff0c;我们了解了 Webpack 的基本应用、性能优化、Loader 与 Plugin 组件开发方方面面的知识&#xff0c;相信学习过这些内容之后&#xff0c;你已经对 Webpack 有相当深入的理解了&#xff0c;可以开始从更底层的视角&#xff0c;自底向上重新审视 Webpac…

R可视化数据必要格式——长格式

一、引言 我们在对数据进行可视化时遇到最头疼、最常见的问题是什么&#xff1f;数据问题。 因为我们往往不会从零自己编程进行可视化&#xff0c;往往是现有模板或积累&#xff0c;而正确的数据格式对应正确的图形包要求&#xff0c;一定会正确出图&#xff0c;所以只有一个问…

uniapp H5页面设置跨域请求

记录一下本地服务在uniapp H5页面访问请求报跨域的错误 这是我在本地起的服务端口号为8088 ip大家可打开cmd 输入ipconfig 查看 第一种方法 在源码视图中配置 "devServer": {"https": false, // 是否启用 https 协议&#xff0c;默认false"port&q…

AI与大模型工程师证书研修班报名啦!

人工智能大模型是指拥有超大规模参数&#xff08;通常在十亿个以上&#xff09;、超强计算资源的机器学习模型&#xff0c;能够处理海量数据&#xff0c;完成各种复杂任务&#xff0c;如自然语言处理、图像识别等。计算机硬件性能不断提升&#xff0c;深度学习算法快速优化&…

【笔记】太久不用redis忘记怎么后台登陆了

&#xff01;首先启动虚拟机linux的centos7 2.启动finalshell 我的redis启动在根目录用 redis-server redis.conf --启动 systemctl status redis --查看redis状态 是否active redis-cli -h centos的ip地址 -p 你要用的redis端口号&#xff08;默认为6379&#xff09; -a 你…

基于xilinx FPGA的GTX/GTH/GTY位置信息查看方式(如X0Y0在bank几)

目录 1 概述2 参考文档3 查看方式4查询总结&#xff1a; 1 概述 本文用于介绍如何查看xilinx fpga GTX得位置信息&#xff08;如X0Y0在哪个BANK/Quad&#xff09;。 2 参考文档 《ug476_7Series_Transceivers》 《pg156-ultrascale-pcie-gen3-en-us-4.4》 3 查看方式 通过…

动态住宅代理IP的优势是什么?什么地方用到?

在大数据时代的背景下&#xff0c;代理IP成为了很多企业顺利开展的重要工具。代理IP地址可以分为住宅代理IP地址和数据中心代理IP地址。选择住宅代理IP的好处是可以实现真正的高匿名性&#xff0c;而使用数据中心代理IP可能会暴露自己使用代理的情况。 住宅代理IP是指互联网服务…

NSSCTF-Web题目22(弱比较、数组绕过)

目录 [鹤城杯 2021]Middle magic 1、题目 2、知识点 3、思路 [WUSTCTF 2020]朴实无华 4、题目 5、知识点 6、思路 [鹤城杯 2021]Middle magic 1、题目 2、知识点 代码审计&#xff0c;弱比较、数组绕过 3、思路 打开题目&#xff0c;出现源代码&#xff0c;我们进行审…

探讨命令模式及其应用

目录 命令模式命令模式结构命令模式适用场景命令模式优缺点练手题目题目描述输入描述输出描述题解 命令模式 命令模式是一种行为设计模式&#xff0c; 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其…

昂科烧录器支持MindMotion灵动微电子的32位微控制器MM32L052NT

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中MindMotion灵动微电子的32位微控制器MM32L052NT已经被昂科的通用烧录平台AP8000所支持。 MM32L052NT使用高性能的ARM Cortex-M0为内核的32位微控制器&#xff0c;最高工作频率…

助力游戏实现应用内运营闭环,融云游戏社交方案升级!

通信能力在所有应用场景都是必备组件&#xff0c;这源于社交属性带给应用的增长神话。 在游戏场景&#xff0c;玩家从少数核心向大众用户泛化扩展的过程&#xff0c;就是游戏深度融合社交能力的过程。 从单机到联机&#xff0c;游戏乐趣的升级 1996 年&#xff0c;游戏界顶流…

cesium楼层分户分析

文章目录 1. 区域绘制2. 户型切分3. 楼房分层4. 编辑房户信息5. 查看房户信息6. 数据库6.1. 楼栋数据库6.2. 单位数据库 7. 房户数据库 1. 区域绘制 点击绘制图形&#xff0c;激活画笔&#xff0c;右键结束绘制。 输入框可以更换地址前缀。 分户坐标是由绘制的多个点组成的&…

自动化任务工具 -- zTasker v1.94 绿色版

软件简介 zTasker 是一款功能强大的自动化任务管理软件&#xff0c;以其简洁易用、一键式操作而著称。软件体积小巧&#xff0c;启动迅速&#xff0c;提供了超过100种任务类型和30多种定时/条件执行方法&#xff0c;能够满足用户在自动化方面的多样化需求。 zTasker 支持定时任…

js学习--制作选项卡

选项卡制作 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>.text_one {width: 11.4%;height: 200px…

gdbserver 指南

文章目录 1. 前言2. gdbserver 远程调试2.1 准备工作2.1.1 准备 客户端 gdb 程序2.1.2 准备 服务端 gdbserver2.1.3 准备 被调试程序 2.2 调试2.2.1 通过网络远程调试2.2.1.1 通过 gdbserver 直接启动程序调试2.2.1.2 通过 gdbserver 挂接到已运行程序调试 2.2.2 通过串口远程调…

浏览器自动填充登录用户名和密码,如何清除

文章目录 刷新网页的时候浏览器会自动填充用户名和密码刷新之后效果图解决方案完整的login.vue代码核心代码原理(添加 readonly 和监听 focus 事件) 刷新网页的时候浏览器会自动填充用户名和密码 刷新之后效果图 解决方案 完整的login.vue代码 <template><div class…

linux系统主机查看系统日志(可查看被执行命令和远程登录等)

linux系统中&#xff0c;在/var/log/messages日志文件中&#xff0c;会记录显示系统所有运行的相关日志 如果被黑客攻击进来&#xff0c;可以在此日志做排查&#xff01; 例如&#xff1a;记录远程登录日志 记录执行命令日志

问题集锦2

1.商品详情和切换数据&#xff0c;要做好来源区分 今天&#xff0c;商品切换 我可以不可以&#xff0c;不选择传递空&#xff0c;导致if没有走到 2.查价格&#xff0c;用用户传递的

Rocky Linux 9 系统OpenSSH CVE-2024-6387 漏洞修复

Rocky Linux 9系统 OpenSSH CVE-2024-6387 漏洞修复 1、漏洞修复2、修复思路3、修复方案3.1、方案一3.2、方案二 4、总结5、参考 1、漏洞修复 CVE-2024-6387&#xff1a;regreSSHion&#xff1a;OpenSSH 服务器中的远程代码执行&#xff08;RCE&#xff09;&#xff0c;至少在…

如何选择适合自己的虚拟化技术?

虚拟化技术已成为现代数据中心和云计算环境的核心组成部分。本文将帮助您了解如何选择适合自己需求的虚拟化技术&#xff0c;以实现更高的效率、资源利用率和灵活性。 理解虚拟化技术 首先&#xff0c;让我们了解虚拟化技术的基本概念。虚拟化允许将一个物理服务器划分为多个虚…