【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 Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

MySQL逻辑删除与唯一索引冲突解决方案

《MySQL逻辑删除与唯一索引冲突解决方案》本文探讨MySQL逻辑删除与唯一索引冲突问题,提出四种解决方案:复合索引+时间戳、修改唯一字段、历史表、业务层校验,推荐方案1和方案3,适用于不同场景,感兴... 目录问题背景问题复现解决方案解决方案1.复合唯一索引 + 时间戳删除字段解决方案2:删除后修改唯一字

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

python删除xml中的w:ascii属性的步骤

《python删除xml中的w:ascii属性的步骤》使用xml.etree.ElementTree删除WordXML中w:ascii属性,需注册命名空间并定位rFonts元素,通过del操作删除属... 可以使用python的XML.etree.ElementTree模块通过以下步骤删除XML中的w:as

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景