当前位置: 首页 > news >正文

【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)

相关文章:

  • Linux中的TCP与UDP:理解两者的差异
  • 生活实用口语柯桥成人外语培训机构“客服”用英文怎么说?
  • 服务器SSH 免密码登录
  • 计算机网络(概述)
  • nginx的rev->handler的更新历程
  • 8.12 矢量图层面要素单一符号使用五(栅格数据填充)
  • 用自己的数据集训练TimeSformer并转ONNX用c++推理
  • 2024广东省职业技能大赛云计算赛项实战——容器云平台搭建
  • python watchdog 配置文件热更新
  • BP神经网络的反向传播(Back Propagation)
  • 方法区讲解
  • EasyExcel 导出批注信息
  • 【Go】十四、图形验证码、短信验证码、注册接口与redis的简单使用
  • 单片机练习题3
  • 每日优秀影视分享❗❗
  • Hibernate【inverse和cascade属性】知识要点
  • JavaScript学习总结——原型
  • java中具有继承关系的类及其对象初始化顺序
  • Laravel5.4 Queues队列学习
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 计算机在识别图像时“看到”了什么?
  • 码农张的Bug人生 - 见面之礼
  • 通过git安装npm私有模块
  • 小程序button引导用户授权
  • ​如何防止网络攻击?
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • # 数论-逆元
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (175)FPGA门控时钟技术
  • (2)Java 简介
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (3)nginx 配置(nginx.conf)
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (solr系列:一)使用tomcat部署solr服务
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (四)JPA - JQPL 实现增删改查
  • (四)事件系统
  • (一) springboot详细介绍
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • .“空心村”成因分析及解决对策122344
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .net6Api后台+uniapp导出Excel
  • .NET正则基础之——正则委托
  • .net中调用windows performance记录性能信息
  • .Net中间语言BeforeFieldInit
  • @JsonFormat与@DateTimeFormat注解的使用
  • @在php中起什么作用?
  • [ Python ]使用Charles对Python程序发出的Get与Post请求抓包-解决Python程序报错问题
  • [ 数据结构 - C++]红黑树RBTree
  • [10] CUDA程序性能的提升 与 流
  • [12] 使用 CUDA 加速排序算法