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

2024-06-23 10:28

本文主要是介绍【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; // longTaskHandle_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*/{/* 创建播放音乐的任务 *///这里需要判断是否多次按下播放按键,判断句柄是否等于NULLif (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; // longTaskHandle_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*/{/* 创建播放音乐的任务 *///这里需要判断是否多次按下播放按键,判断句柄是否等于NULLif (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 msvTaskDelay(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)

这篇关于【FreeRTOS】删除任务 用遥控器控制音乐的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1086894

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的

如何恢复回收站中已删除/清空的文件

回收站清空后如何恢复已删除的文件?是否可以恢复永久删除的文件?或者最糟糕的是,如果文件直接被删除怎么办?本文将向您展示清空回收站后恢复已删除数据的最佳方法。 回收站清空后如何恢复已删除的文件? “回收站清空后我还能恢复已删除的文件吗?” 答案是肯定的,但是在这种情况下您将需要一个  回收站恢复工具 来从回收站中检索文件: 错误/永久删除回收站或任何数字存储设备中的文件 直接删除的文件/

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品

FreeRTOS学习笔记(六)队列

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、队列的基本内容1.1 队列的引入1.2 FreeRTOS 队列的功能与作用1.3 队列的结构体1.4 队列的使用流程 二、相关API详解2.1 xQueueCreate2.2 xQueueSend2.3 xQueueReceive2.4 xQueueSendFromISR2.5 xQueueRecei

PostgreSQL中的多版本并发控制(MVCC)深入解析

引言 PostgreSQL作为一款强大的开源关系数据库管理系统,以其高性能、高可靠性和丰富的功能特性而广受欢迎。在并发控制方面,PostgreSQL采用了多版本并发控制(MVCC)机制,该机制为数据库提供了高效的数据访问和更新能力,同时保证了数据的一致性和隔离性。本文将深入解析PostgreSQL中的MVCC功能,探讨其工作原理、使用场景,并通过具体SQL示例来展示其在实际应用中的表现。 一、