【FreeRTOS】删除任务 用遥控器控制音乐

参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》
学习视频:【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 01:22】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=19&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=82
传送门


文章目录

  • 1 任务的删除
  • 2 Demo: 删除任务
  • 3 优先级与阻塞 改善播放效果
    • 3.1 其他任务
    • 3.2 改变优先级
  • 4 Tick
  • 5 修改优先级
    • 5.1 获取优先级
    • 5.2 设置优先级


1 任务的删除

删除任务时使用的函数如下:

void vTaskDelete( TaskHandle_t xTaskToDelete );

参数说明:

参数描述
pvTaskCode任务句柄,使用xTaskCreate创建任务时可以得到一个句柄。 也可传入NULL,这表示删除自己。

怎么删除任务?举个不好的例子:

  • 自杀:vTaskDelete(NULL)
  • 被杀:别的任务执行vTaskDelete(pvTaskCode),pvTaskCode是自己的句柄
  • 杀人:执行vTaskDelete(pvTaskCode),pvTaskCode是别的任务的句柄

2 Demo: 删除任务

代码为: 07_delete_task
功能为:当监测到遥控器的Power按键被按下后,删除音乐播放任务。

完整版代码如下:


while (1)
{
    /* 读取红外遥控器 */
    if (0 == IRReceiver_Read(&dev, &data))
    {		
        if (data == 0xa8) /* play */
        {
            /* 创建播放音乐的任务 */
          extern void PlayMusic(void *params);
          if (xSoundTaskHandle == NULL)
          {
                LCD_ClearLine(0, 0);
                LCD_PrintString(0, 0, "Create Task");
                ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal, &xSoundTaskHandle);
          }
        }
        
        else if (data == 0xa2) /* power */
        {
            /* 删除播放音乐的任务 */
            if (xSoundTaskHandle != NULL)
            {
                LCD_ClearLine(0, 0);
                LCD_PrintString(0, 0, "Delete Task");
                vTaskDelete(xSoundTaskHandle);
                PassiveBuzzer_Control(0); /* 停止蜂鸣器 */
                xSoundTaskHandle = NULL;
            }
        }
    }
}

现在我们只保留一个默认的任务,修改默认的任务代码,代码如下:

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
    
    uint8_t dev, data;
    int len;
    
    BaseType_t ret; // long
    TaskHandle_t xSoundTaskHandle = NULL;           // 句柄 初始值是NULL	  
    
    LCD_Init();
    LCD_Clear();

    IRReceiver_Init();
    LCD_PrintString(0, 0, "Waiting Control");

    while (1)   // 在屏幕上输出接收到的键值
    {
        
        /* 读取红外遥控器 */
        if (0 == IRReceiver_Read(&dev, &data))
        {
            if (data == 0xA8)   /*play*/
            {
                /* 创建播放音乐的任务 */
                //这里需要判断是否多次按下播放按键,判断句柄是否等于NULL
                if (xSoundTaskHandle == NULL)   /* 句柄等于NULL,才来创建这个任务 */
                {
                    LCD_PrintString(0, 0, "Create Task");
                    ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal, &xSoundTaskHandle);
                }
            }
            else if (data == 0xA2)
            {
                /* 删除播放音乐的任务 */
                if (xSoundTaskHandle != NULL)   //如果这个句柄xSoundTaskHandle != NULL,才会删除
                {
                    LCD_PrintString(0, 0, "Delete Task");
                    vTaskDelete(xSoundTaskHandle);
                    xSoundTaskHandle = NULL;    //删除完成,赋值NULL
                }
            }
        }
    }

  /* USER CODE END StartDefaultTask */
}

在这里插入图片描述
这样写代码出现了一个bug,遥控器不适配,并且删除任务的时候,蜂鸣器没有关闭,蜂鸣器一直鸣叫上一个音调~

现在来改进一下程序

清除打印信息

/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
    
    uint8_t dev, data;
    int len;
    
    BaseType_t ret; // long
    TaskHandle_t xSoundTaskHandle = NULL;           // 句柄 初始值是NULL	  
    
    LCD_Init();
    LCD_Clear();

    IRReceiver_Init();
    LCD_PrintString(0, 0, "Waiting Control");

    while (1)   // 在屏幕上输出接收到的键值
    {
        
        /* 读取红外遥控器 */
        if (0 == IRReceiver_Read(&dev, &data))
        {
            if (data == 0x22)   /*play*/
            {
                /* 创建播放音乐的任务 */
                //这里需要判断是否多次按下播放按键,判断句柄是否等于NULL
                if (xSoundTaskHandle == NULL)   /* 句柄等于NULL,才来创建这个任务 */
                {
                    LCD_ClearLine(0, 0);    //清屏
                    LCD_PrintString(0, 0, "Create Task");
                    ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal, &xSoundTaskHandle);
                }
            }
            else if (data == 0xA2)
            {
                /* 删除播放音乐的任务 */
                if (xSoundTaskHandle != NULL)   //如果这个句柄xSoundTaskHandle != NULL,才会删除
                {
                    LCD_ClearLine(0, 0);    //清屏
                    LCD_PrintString(0, 0, "Delete Task");
                    vTaskDelete(xSoundTaskHandle);
                    PassiveBuzzer_Control(0);   // Stop Buzzer 停止蜂鸣器
                    xSoundTaskHandle = NULL;    //删除完成,赋值NULL
                }
            }
            
            len = LCD_PrintString(0, 6, "Key name: ");
            LCD_PrintString(len, 6, IRReceiver_CodeToString(data));
        }
    }

  /* USER CODE END StartDefaultTask */
}

这样改进代码,播放效果好一点,能播放,能停止

但是这样频繁的创建任务和频繁的删除任务好吗???

每次创建都会动态分配内存,每次删除都会释放内存,这样操作容易造成内存的碎片,以后多次执行之后,可能就申请不到内存了

所以不能简单的清除一个任务,就不管后续的清理工作了

在这里插入图片描述

3 优先级与阻塞 改善播放效果

学习视频:【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 00:10】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=20&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=10

保留所有任务,但是会提高音乐播放任务的优先级,并且延时函数使用vTaskDelay

  • 现在先体验一把 改变优先级和使用vTaskDelay的用法

在第7个程序07_delete_task的基础上 - 创建一个新的程序 08_task_priority

3.1 其他任务

打开程序,将创建光的任务取消注释,创建色的任务也取消注释

  xLightTaskHandle = xTaskCreateStatic(
            Led_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
            "LightTask",        //光任务
            128,                //栈大小,这里提供了栈的大小(长度)
            NULL,               //无传入的参数
            osPriorityNormal,   //优先级默认
            g_pucStackOfLightTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小,最开始栈的类型不对,栈的类型uint32_t
            &g_TCBofLightTask       // 取址TCB
  );

  /* 创建任务:色 */ 
  xColorTaskHandle = xTaskCreateStatic(
            ColorLED_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
            "ColorTask",        //光任务
            128,                //栈大小,这里提供了栈的大小(长度)
            NULL,               //无传入的参数
            osPriorityNormal,   //优先级默认
            g_pucStackOfColorTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小
            &g_TCBofColorTask       // 取址TCB
  );

句柄也要保留!

static StackType_t g_pucStackOfLightTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 光任务的栈
StaticTask_t g_TCBofLightTask;                  // 光任务的TCB
static TaskHandle_t xLightTaskHandle;           // void *  在全局变量里记录句柄

static StackType_t g_pucStackOfColorTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 色任务的栈
StaticTask_t g_TCBofColorTask;                  // 色任务的TCB
static TaskHandle_t xColorTaskHandle;           // void *  在全局变量里记录句柄

3.2 改变优先级

ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal+1, &xSoundTaskHandle);

在创建播放音乐任务的时候修改优先级,注意是+1,不是-1
在这里插入图片描述

现在又出现了新的bug,按播放按键,创建一个音乐播放的任务完成后,全彩LED卡了,并且用按键删除音乐播放的操作失效了~

因为我们创建了一个高优先级的任务,里面一直在运行死循环,mdelay也是一个死循环,它不断读取时间,这个程序的优先级最高,独占了CPU,音乐播放效果非常好,没有以前的卡顿了

在这里插入图片描述

将mdelay函数换成vTaskDelay,主动放弃CPU,允许其他任务执行

/* USER CODE END PT */
/* Function definition -------------------------------------------------------*/
/* USER CODE BEGIN FD */
/**
  * @Function name  MUSIC_Begin
  * @Introduce  		开始播放音乐						
  * @Return 				NULL
  */
void MUSIC_Analysis(void)
{
	uint16_t MusicBeatNum = ((((sizeof(Music_Lone_Brave))/2)/3)-1);
	
	uint16_t MusicSpeed = Music_Lone_Brave[0][2];
	for(uint16_t i = 1;i<=MusicBeatNum;i++){
		//BSP_Buzzer_SetFrequency(Tone_Index[Music_Lone_Brave[i][0]][Music_Lone_Brave[i][1]]);
		PassiveBuzzer_Set_Freq_Duty(Tone_Index[Music_Lone_Brave[i][0]][Music_Lone_Brave[i][1]], 50);    // 设置蜂鸣器频率,占空比50
		// HAL_Delay(MusicSpeed/Music_Lone_Brave[i][2]);
		// mdelay(MusicSpeed/Music_Lone_Brave[i][2]);  //Delay ms
        vTaskDelay(MusicSpeed/Music_Lone_Brave[i][2]);  //主动放弃CPU
	}![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/77a8a7dca2214120aa410c3f11b53278.gif)

}

现在可以同时运行这三个任务了,RGB灯缓慢变化,PC13间隔闪烁,遥控器也是可以创建播放任务和删除播放任务的~
非常nice!

在这里插入图片描述

在这里插入图片描述

4 Tick

对于同优先级的任务,它们“轮流”执行。怎么轮流?你执行一会,我执行一会。

"一会"怎么定义?

人有心跳,心跳间隔基本恒定。

FreeRTOS中也有心跳,它使用定时器产生固定间隔的中断。这叫Tick、滴答,比如每10ms发生一次时钟中断。

如下图所示:

  • 假设t1、t2、t3发生时钟中断
  • 两次中断之间的时间被称为时间片(time slice、tick period)
  • 时间片的长度由configTICK_RATE_HZ 决定,假设configTICK_RATE_HZ为100,那么时间片长度就是10ms
    在这里插入图片描述

相同优先级的任务怎么切换呢?请看下图:

  • 任务2从t1执行到t2
  • 在t2发生tick中断,进入tick中断处理函数:
    • 选择下一个要运行的任务
    • 执行完中断处理函数后,切换到新的任务:任务1
  • 任务1从t2执行到t3
  • 从图中可以看出,任务运行的时间并不是严格从t1,t2,t3哪里开始

在这里插入图片描述

有了Tick的概念后,我们就可以使用Tick来衡量时间了,比如:

vTaskDelay(2);  // 等待2个Tick,假设configTICK_RATE_HZ=100, Tick周期时10ms, 等待20ms

// 还可以使用pdMS_TO_TICKS宏把ms转换为tick
vTaskDelay(pdMS_TO_TICKS(100));	 // 等待100ms

注意,基于Tick实现的延时并不精确,比如vTaskDelay(2)的本意是延迟2个Tick周期,有可能经过1个Tick多一点就返回了。 如下图:

在这里插入图片描述

使用vTaskDelay函数时,建议以ms为单位,使用pdMS_TO_TICKS把时间转换为Tick。

这样的代码就与configTICK_RATE_HZ无关,即使配置项configTICK_RATE_HZ改变了,我们也不用去修改代码。

5 修改优先级

5.1 获取优先级

使用uxTaskPriorityGet来获得任务的优先级:

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。

5.2 设置优先级

使用vTaskPrioritySet 来设置任务的优先级:

void vTaskPrioritySet( TaskHandle_t xTask,
                       UBaseType_t uxNewPriority );

使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。

使用vTaskPrioritySet 来设置任务的优先级

void vTaskPrioritySet( TaskHandle_t xTask,
                       UBaseType_t uxNewPriority );

使用参数xTask来指定任务,设置为NULL表示设置自己的优先级;

参数uxNewPriority表示新的优先级,取值范围是0~(configMAX_PRIORITIES – 1)

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

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

相关文章

有哪些零售O2O应用模式?如何构建O2O闭环生态系统?

在零售业的演变历程中&#xff0c;O2O模式的兴起标志着一个新时代的开始。这种模式以其创新性&#xff0c;将线上的便捷与线下的实体体验完美融合&#xff0c;为消费者带来了前所未有的购物便利和体验丰富性。随着技术的不断进步和消费者需求的日益多样化&#xff0c;O2O模式已…

202484读书笔记|《长安诗选》——你心中的一团锦绣,终有脱口而出的一日,大鹏终有直击云天的一日

202484读书笔记|《长安诗选》——你心中的一团锦绣&#xff0c;终有脱口而出的一日&#xff0c;大鹏终有直击云天的一日 一 诗梦长安三万里二 诗话长安二 谪仙风华四 盛唐群星 《长安诗选》追光动画 韩潇&#xff0c;电影《长安三万里》同名诗集&#xff0c;很给力的电影&#…

在线教育系统源码入门:教育培训小程序开发全流程

本篇文章&#xff0c;笔者将详细介绍在线教育系统源码的入门知识&#xff0c;并带领大家了解教育培训小程序的开发全流程。 一、在线教育系统的基本概念 一个完整的在线教育系统应具备以下几个模块&#xff1a; 用户管理 课程管理 教学互动 支付模块 数据统计 二、开发工…

C/C++ 数组负数下标

一 概述 在 C 中&#xff0c;数组是一块连续的内存空间&#xff0c;数组的下标通常用来定位这段内存中的特定元素。下标通常从 0 开始&#xff0c;最大到数组长度减 1。例如&#xff0c;一个有 10 个元素的数组&#xff0c;其有效下标范围是从 0 到 9。 当你尝试使用负数下标来…

嵌入式通信协议----Wi-Fi协议详解(二)(基于STM32+有人物联网WIFI模块)

四、有人WIFI模块 1.模块介绍 Wi-Fi 模块用于实现串口到 Wi-Fi 数据包的双向透明转发&#xff0c;模块内部完成协议转换&#xff0c;通 过该模块&#xff0c;客户可以将物理设备连接到 Wi-Fi 网络上&#xff0c;从而实现物联网的控制与管理。 2.模块参数 Wi-Fi 模块的…

Windows应急响应靶机 - Web2

一、靶机介绍 应急响应靶机训练-Web2 前景需要&#xff1a;小李在某单位驻场值守&#xff0c;深夜12点&#xff0c;甲方已经回家了&#xff0c;小李刚偷偷摸鱼后&#xff0c;发现安全设备有告警&#xff0c;于是立刻停掉了机器开始排查。 这是他的服务器系统&#xff0c;请你…

规模弹性: 管理谷歌的TPUv4机器学习超级计算机(二)

本文为翻译文章&#xff0c;原文为&#xff1a; Resiliency at Scale: Managing Google’sTPUv4 Machine Learning Supercomputer。 由于字数过长&#xff0c;文章分为两期发布&#xff0c;本片涵盖原文后半部分4&#xff5e;9节&#xff0c;前三章节请参考文章&#xff1a;规…

【数据结构】顺序表实操——通讯录项目

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

堆排序的实现原理

一、什么是堆排序&#xff1f; 堆排序就是将待排序元素以一种特定树的结构组合在一起&#xff0c;这种结构被称为堆。 堆又分为大根堆和小根堆&#xff0c;所谓大根堆即为所有的父节点均大于子节点&#xff0c;但兄弟节点之间却没有什么严格的限制&#xff0c;小根堆恰恰相反&a…

使用Scala爬取安居客房产信息并存入CSV文件

使用Scala爬取安居客房产信息并存入CSV文件 本篇博客中&#xff0c;我们将介绍如何使用Scala语言编写一个简单的程序&#xff0c;来爬取安居客&#xff08;Anjuke&#xff09;网站上的房产信息&#xff0c;并将这些信息存储到CSV文件中。这个示例将涵盖HTTP请求、HTML解析、数…

掌握 Nuxt 3 中的状态管理:实践指南

title: 掌握 Nuxt 3 中的状态管理&#xff1a;实践指南 date: 2024/6/22 updated: 2024/6/22 author: cmdragon excerpt: 摘要&#xff1a;该文指南详述了Nuxt 3的概况与安装&#xff0c;聚焦于在Nuxt 3框架下运用Vuex进行高效的状态管理&#xff0c;涵盖基础配置、模块化实…

以太坊==给合约转入/查询合约剩余/合约转给某账户/结构体/MAP

转入 必须要定义该函数&#xff0c;或者定义fallback // 接收以太币 receive() external payable {} // Corrected Line // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;contract SimpleStorage {uint256 private storedData;// 事件&#xff0c;用于通知数据变更e…

使用 GitOps 进行防灾 MinIO

想象一下&#xff0c;您已经花费了无数小时来完善 Docker Swarm 设置&#xff0c;精心设计每项服务&#xff0c;并调整 CI/CD 管道以实现无缝自动化。现在&#xff0c;想象一下这个经过微调的系统被重置为原点&#xff0c;不是因为严重的故障或安全漏洞&#xff0c;而是因为数据…

并行计算之SIMD与SPMD

SIMD (Single Instruction Multiple Data) SIMD&#xff0c;也就是单指令多数据计算&#xff0c;一条指令可以处理多个数据。通过向量寄存器存储多个数据元素&#xff0c;并使用单条指令同时对这些数据元素进行处理&#xff0c;从而提高了计算效率。 代码示例&#xff1a; fl…

数据库设计概述-数据库设计内容、数据库设计方法(基于E-R模型的规范设计方法)

一、引言 如何利用关系数据库理论设计一个满足应用系统需求的数据库 二、数据库设计内容 1、数据库设计是基于应用系统需求分析中对数据的需求&#xff0c;解决数据的抽象、数据的表达和数据的存储结构等问题 2、其目标是设计出一个满足应用要求、简洁、高效、规范合理的数…

昇思25天学习打卡营第4天|数据变换(Transforms)

一、简介&#xff1a; 数据变换是指将已有的数据转换成可以提供给模型直接训练和验证的数据格式&#xff0c;在深度学习中一般被称为数据预处理&#xff0c;之前在昇思25天学习打卡营第3天|数据集Dataset-CSDN博客 介绍数据集的时候已经有了一个简单的使用&#xff0c;下面将具…

mac赛车竞速游戏:弯道卡丁车车手 for Mac 中文版下载

《弯道卡丁车车手》是一款刺激的卡丁车竞速游戏&#xff0c;玩家扮演的是赛道上的卡丁车车手&#xff0c;需要在曲线崎岖的赛道上驾驶卡丁车&#xff0c;与其他车手展开激烈的竞速比赛。 游戏中有多种赛道可以选择&#xff0c;每个赛道都有不同的难度和特点&#xff0c;玩家需…

双例集合(三)——双例集合的实现类之TreeMap容器类

Map接口有两个实现类&#xff0c;一个是HashMap容器类&#xff0c;另一个是TreeMap容器类。TreeMap容器类的使用在API上于HashMap容器类没有太大的区别。它们的区别主要体现在两个方面&#xff0c;一个是底层实现方式上&#xff0c;HashMap是基于Hash算法来实现的吗&#xff0c…

Apple - Advanced Memory Management Programming Guide 内存管理

翻译整理自&#xff1a;Advanced Memory Management Programming Guide&#xff08;Updated: 2012-07-17 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html#//apple_ref/doc/uid/10000011i 文章目录 一、关于…

算法题--华为od机试考试(整数对最小和、素数之积、找城市)

目录 整数对最小和 题目描述 注意 输出描述 示例1 输入 输出 说明 解析 答案 素数之积 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入 输出 说明 解析 找城市 题目描述 输入 输出 示例1 输入 输出 示例2 输入 输出 说明 解析…