【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

相关文章

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Git中恢复已删除分支的几种方法

《Git中恢复已删除分支的几种方法》:本文主要介绍在Git中恢复已删除分支的几种方法,包括查找提交记录、恢复分支、推送恢复的分支等步骤,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录1. 恢复本地删除的分支场景方法2. 恢复远程删除的分支场景方法3. 恢复未推送的本地删除分支场景方法4. 恢复

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

Python Invoke自动化任务库的使用

《PythonInvoke自动化任务库的使用》Invoke是一个强大的Python库,用于编写自动化脚本,本文就来介绍一下PythonInvoke自动化任务库的使用,具有一定的参考价值,感兴趣的可以... 目录什么是 Invoke?如何安装 Invoke?Invoke 基础1. 运行测试2. 构建文档3.

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

Redis过期键删除策略解读

《Redis过期键删除策略解读》Redis通过惰性删除策略和定期删除策略来管理过期键,惰性删除策略在键被访问时检查是否过期并删除,节省CPU开销但可能导致过期键滞留,定期删除策略定期扫描并删除过期键,... 目录1.Redis使用两种不同的策略来删除过期键,分别是惰性删除策略和定期删除策略1.1惰性删除策略

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C

MySQL中删除重复数据SQL的三种写法

《MySQL中删除重复数据SQL的三种写法》:本文主要介绍MySQL中删除重复数据SQL的三种写法,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下... 目录方法一:使用 left join + 子查询删除重复数据(推荐)方法二:创建临时表(需分多步执行,逻辑清晰,但会

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五