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

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

    }   

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苹果电脑虚拟机运行Windows Mac环境安装Win PD19虚拟机 parallels desktop19虚拟机安装教程免费密钥激活

在如今多元的数字时代&#xff0c;我们经常需要在不同的操作系统环境下进行工作和学习。而对于 Mac 用户来说&#xff0c;有时候需要在自己的电脑上安装 Windows 操作系统&#xff0c;以体验更多软件及功能&#xff0c;而在 Mac 安装 Windows 虚拟机是常用的一种操作。下面就来…

Python28-5 k-means算法

k-means 算法介绍 k-means 算法是一种经典的聚类算法&#xff0c;其目的是将数据集分成 ( k ) 个不同的簇&#xff0c;每个簇内的数据点尽可能接近。算法的基本思想是通过反复迭代优化簇中心的位置&#xff0c;使得每个簇内的点与簇中心的距离之和最小。k-means 算法的具体步骤…

【FFmpeg】avformat_find_stream_info函数

【FFmpeg】avformat_find_stream_info 1.avformat_find_stream_info1.1 初始化解析器&#xff08;av_parser_init&#xff09;1.2 查找探测解码器&#xff08;find_probe_decoder&#xff09;1.3 尝试打开解码器&#xff08;avcodec_open2&#xff09;1.4 读取帧&#xff08;re…

嵌入式Linux之Uboot简介和移植

uboot简介 uboot 的全称是 Universal Boot Loader&#xff0c;uboot 是一个遵循 GPL 协议的开源软件&#xff0c;uboot是一个裸机代码&#xff0c;可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高级功能。 也就是说&#xff0c;可以在没有系统的情况…

创建kobject

1、kobject介绍 kobject的全称是kernel object&#xff0c;即内核对象。每一个kobject都会对应系统/sys/下的一个目录。 2、相关结构体和api介绍 2.1 struct kobject // include/linux/kobject.h 2.2 kobject_create_and_add kobject_create_and_addkobject_createkobj…

开源自动化热键映射工具autohotkey十大用法及精选脚本

AutoHotkey&#xff08;AHK&#xff09;是一款功能强大的热键脚本语言工具&#xff0c;它允许用户通过编写脚本来自动化键盘、鼠标等设备的操作&#xff0c;从而极大地提高工作效率。以下是AutoHotkey的十大经典用法&#xff0c;这些用法不仅解放了用户的双手&#xff0c;还展示…

字节码编程ASM之插桩方法调用记录

写在前面 源码 。 正式开始之前&#xff0c;先分享一个让人”悲伤“的真实的故事。 那是一个风和日丽的周六的下午&#xff0c;俺正在开开心心的打着羽毛球&#xff0c;突然接到了来自于最不想联系的那个人&#xff08;没错&#xff0c;这个人就是我的领导&#xff01;&#x…

QT Creator生成uml类图

先说方法&#xff0c;使用Doxygen工具&#xff0c;笔者用的虚拟机linux系统下的qt5.7&#xff0c;没找到自带的uml生成类的工具。 1、Doxygen 安装 在 Ubuntu 系统中&#xff0c;执行下面命令安装 doxygen 和 graphviz 软件包。 sudo apt install graphviz # 用于生成代码…

等保2.0 实施方案之信息软件验证要求

一、等保2.0背景及意义 随着信息技术的快速发展和网络安全威胁的不断演变&#xff0c;网络安全已成为国家安全、社会稳定和经济发展的重要保障。等保2.0&#xff08;即《信息安全技术 网络安全等级保护基本要求》2.0版本&#xff09;作为网络安全等级保护制度的最新标准&#x…

Gradle学习-5 发布二进制插件

注&#xff1a;以下示例基于Gradle8.0 1、发布插件 复制一分 buildSrc&#xff0c;执行命令行&#xff0c;生成一个新目录 leon-gradle-plugin cp -rf buildSrc leon-gradle-plugin在 leon-gradle-plugin 目录下的 build.gradle 中引入maven plugins{// 引用 Groovy 插件&…

【热部署】✈️Springboot 项目的热部署实现方式

目录 &#x1f378;前言 &#x1f37b;一、热部署和手动重启 &#x1f37a;二、热部署的实现 2.1 手动启动热部署 2.2 自动检测热部署 2.3 关闭热部署 &#x1f49e;️三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;书接上文&#xff0c;通过Springboot 中的 actu…

解析Kotlin中扩展函数与扩展属性【笔记摘要】

1.扩展函数 1.1 作用域&#xff1a;扩展函数写的位置不同&#xff0c;作用域就也不同 扩展函数可以写成顶层函数&#xff08;Top-level Function&#xff09;&#xff0c;此时它只属于它所在的 package。这样你就能在任何类里使用它&#xff1a; package com.rengwuxianfun …

zabbix“专家坐诊”第244期问答

问题一 Q&#xff1a;请教一下&#xff0c;我的zabbix6.0配置的基于snmptrap上报的日志提取关键字推送告警&#xff0c;正则表达式能否帮忙看看怎么弄&#xff1f;我这配置的提示一直不正确&#xff1f; A&#xff1a;具体看一下这里的信息。 Q&#xff1a;这个我是直接复制的…

如何计算弧线弹道的落地位置

1&#xff09;如何计算弧线弹道的落地位置 2&#xff09;Unity 2021 IL2CPP下使用Protobuf-net序列化报异常 3&#xff09;编译问题&#xff0c;用Mono可以&#xff0c;但用IL2CPP就报错 4&#xff09;Wwise的Bank在安卓上LoadBank之后&#xff0c;播放没有声音 这是第393篇UWA…

ssm旅游信息分享网站-计算机毕业设计源码92194

目录 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 旅游信息分享网站分析 2.1 可行性分析 2.2 系统功能分析 2.3 系统用例分析 2.4 系统流程分析 2.5本章小结 3 旅游信息分享网站总体设计 3.1 系统功能模块设计 3.2 数据库设计 3.4本章小结 4 旅游信…

从全连接到卷积

一、全连接到卷积 1、卷积具有两个原则&#xff1a; 平移不变性&#xff1a;无论作用在哪个部分&#xff0c;它都要有相同的作用&#xff0c;而不会随着位置的改变而改变 局部性&#xff1a;卷积核作用处&#xff0c;作用域应该是核作用点的周围一小部分而不作用于更大的部分 …

一篇文章搞懂弹性云服务器和轻量云服务器的区别

前言 在众多的云服务器类型中&#xff0c;弹性云服务器和轻量云服务器因其各自的特点和优势&#xff0c;受到了广大用户的青睐。那么&#xff0c;这两者之间到底有哪些区别呢&#xff1f;本文将为您详细解析。 弹性云服务器&#xff1a;灵活多变的计算资源池 弹性云服务器&…

【计算机体系结构】缓存的false sharing

在介绍缓存的false sharing之前&#xff0c;本文先介绍一下多核系统中缓存一致性是如何维护的。 目前主流的多核系统中的缓存一致性协议是MESI协议及其衍生协议。 MESI协议 MESI协议的4种状态 MESI协议有4种状态。MESI是4种状态的首字母缩写&#xff0c;缓存行的4种状态分别…

CoAtNet(NeurIPS 2023, Google)论文解读

paper&#xff1a;CoAtNet: Marrying Convolution and Attention for All Data Sizes third-party implementation&#xff1a;https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/maxxvit.py 背景 自AlexNet以来&#xff0c;ConvNets一直是计算机…

【接口自动化测试】第四节.实现项目核心业务的单接口自动化测试

文章目录 前言一、登录单接口自动化测试 1.1 登录单接口文档信息 1.2 登录成功 1.3 登录失败&#xff08;用户名为空&#xff09;二、数据驱动的实现 2.1 json文件实现数据驱动三、课程添加单接口自动化测试 3.1 课程添加单接口文档信息 3.2 课程…