【FreeRTOS】使用互斥量解决优先级反转

news2024/9/25 23:18:25

目录

  • 0 前言
  • 0 引言
  • 1 互斥量_领导临时提拔你(解决优先级反转)
  • 2 怎么使用互斥量
  • 3 bug
  • 4 总结


0 前言

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

参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》


0 引言

在使用信号量的时候,不论是计数型信号量,还是二进制信号量,都可能会遇到优先级反转的问题。


1 互斥量_领导临时提拔你(解决优先级反转)

本节源码:在"21_semaphore_priority_inversion"的基础上,改出:22_mutex_priority_inversion

假设学校机房里有且仅有一台超算,一名学生为了学习,早早来到实验室机房学习了,并且按下指纹进行身份验证,日上三竿,主任带着领导来参观了,主任说你这个太吵了,先停下,超算不许使用了,最后校长来了,校长也是个科研工作者,校长也想使用超算,但是超算已经被学生按下了指纹,抢占了使用权限,没办法校长只能等待,这里就被主任卡住了,校长就非常恼火,这样就是优先级反转的例子。

对于上面的例子,校长在等待学生使用完超算,但是学生被主任卡住了,现在校长可以提高学生的优先级让学生的优先级和自己一样高,这样学生就压住了主任,学生使用完,要乖乖的,得识相,恢复自己的优先级,然后校长就可以使用超算了,这样就是优先级继承。

在时间轴上画出这个流程,如下图所示:
在这里插入图片描述


2 怎么使用互斥量

互斥量是一种特殊的信号量,和信号量的创建方法一致

static SemaphoreHandle_t g_xSemTicks;   //信号量 上次创建的信号量,现在是互斥量

g_xSemTicks = xSemaphoreCreateMutex();  //创建Mutex互斥量

修改car_game里面的代码,删除创建信号量,获取信号量的代码,只创建一个Mutex互斥量即可

在这里插入图片描述

这个互斥量的初始值是1(跳转到源码里看,会创建完后会自加1)。这样就可以直接使用了

在这里插入图片描述


3 bug

编译,烧写运行,效果不理想,任务1被卡死了

卡死在这里

在这里插入图片描述

为什么会出现这种效果呢???

在这里插入图片描述

  1. 在时间轴上,任务1先运行
  2. 任务2的优先级抢占了任务1
  3. 两秒后,任务3的优先级最高,抢占了任务2
  4. 任务3想获得互斥量,但是失败了
  5. 任务3提升任务1的优先级
  6. 任务1会继续往下走
  7. 现在就卡死了……
  8. 但是任务2的LCD任务还没有刷新完成,bInUsed没有释放,一直是1
  9. 任务3一直判断bInUsed是否为0
  10. 任务3优先级最高,一直判断,任务2一直不能给bInUsed清零,一直判断,一直卡死在这里!

那么我们需要怎么解决这个问题呢???

  • I2C的互斥访问问题
  • 之前用的是简单的全局变量保护
  • 现在可以修改成互斥量了

修改我们的程序,用法互斥量保护临界资源

  • 声明互斥量,在初始化里,创建这个互斥量


static SemaphoreHandle_t g_xI2CMutex;   // 定义一个 I2C 互斥量

void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
    
  g_xI2CMutex = xSemaphoreCreateMutex();  //创建Mutex互斥量
  • 编写函数
void GetI2C(void)
{
    /* 等待一个互斥量 */
    xSemaphoreTake(g_xI2CMutex, portMAX_DELAY); //互斥量是特殊的信号量,只需要传入句柄即可
}

void PutI2C(void)
{
    /* 释放互斥量 */
    xSemaphoreGive(g_xI2CMutex); //互斥量是特殊的信号量,只需要传入句柄即可
}



修改draw.c,先获得互斥量,再使用LCD,再释放LCD

extern void GetI2C(void);
extern void PutI2C(void);

void draw_flushArea(byte x, byte y, byte w, byte h)
{
//    while (bInUsed);
    //taskENTER_CRITICAL();
//    bInUsed = 1;
    
    GetI2C();   // 使用LCD之前,先获得互斥量。会一直等待
    LCD_FlushRegion(x, y, w, h);
    PutI2C();   // 使用LCD之后,再释放互斥量
    
//    bInUsed = 0;
    //taskEXIT_CRITICAL();
}

同样用到I2C的设备还有MPU6050,继续修改代码

void MPU6050_Task(void *params)
{	
    int16_t AccX;
	struct mpu6050_data result;
	int ret;
//	extern volatile int bInUsed;
    extern void GetI2C(void);
    extern void PutI2C(void);
    while (1)
    {    
		/* 读数据 */
//		while (bInUsed);
//		bInUsed = 1;
        
        GetI2C();   // 使用LCD之前,先获得互斥量。会一直等待
        ret = MPU6050_ReadData(&AccX, NULL, NULL, NULL, NULL, NULL);
        PutI2C();   // 使用LCD之后,再释放互斥量
		
//		bInUsed = 0;
		
        if (0 == ret)
		{
			/* 解析数据 */
			MPU6050_ParseData(AccX, 0, 0, 0, 0, 0, &result);

			/* 写队列 */
			xQueueSend(g_xQueueMPU6050, &result, 0);
		}
		
		/* delay */
		vTaskDelay(50);
	}
}

4 总结

  • 观察现象,第一辆车先跑1S,然后第二辆车跑一会,接着第一辆车和第二辆车一起跑,第一辆车先跑到终点,同时第二辆车停止,第三辆车开始运行,直到到达终点后,第二辆车继续运行到终点。

  • 任务1使用了vTaskDelay,进入了阻塞,任务2就可以继续跑,但是优先级没有任务1高(任务1已经优先级继承了),所以看起来第一辆车和第二辆车有一段是并行同时在跑,其实是交叉运行的任务。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是无效营养?无效营养对健康和身材的影响

在追求健康饮食和匀称身材的道路上,我们往往被各种营养概念包围,一不小心就会陷入“无效营养”的陷阱。那么,什么是无效营养?简单来说,无效营养‌:指的是摄入的营养物质不能被身体利用,或者摄入…

数据库与缓存一致性的解决方案

数据库和缓存的数据一致性问题一直是老生常谈的话题了,它不仅在面试中十分常见,而且在实际开发中也是需要加以考量的因素。借着难得的空暇时光(其实是晚上不太想写代码),笔者今天想和大家简单讨论一下,数据…

Vue实现步骤条(el-step)+Popover弹出框

1、实现效果 hover到每一个步骤条上时,如果当前有未完成情况(unFinishedMe不为空),就使用popover显示出来,如果没有hover时就不显示 2、实现思路 循环app信息列表显示多个进度条 使用el-steps 循环步骤列表&#xf…

LeetCode的高频SQL50题(基础版)学习笔记

题目在此网站 https://leetcode.cn/ 查询 # Write your MySQL query statement below select product_id from products where low_fats like Y and recyclable like Y;# Write your MySQL query statement below select name from customer where referee_id !2 or referee_i…

Java笔试面试题AI答之面向对象(7)

文章目录 37. Java成员变量与局部变量的区别有哪些?38. Java 创建一个对象用什么运算符? 对象实体与对象引用有何不同?对象实体与对象引用的不同示例 39. 类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么?40. Java构造方法有…

服了!DELETE 同一行记录也会造成死锁---图文解析

服了!DELETE 同一行记录也会造成死锁!! 作者:转转技术团队 链接:https://juejin.cn/post/7387227689319563290 来源:稀土掘金 MySQL 锁回顾 共享锁 使用共享锁(Shared Lock)时&am…

模糊视频一键变清晰,从此告别模糊不清的画质

话不多说,咱们直入主题。你是不是有比较模糊的视频,比如老视频,老电影和监控视频,对了,还有日本土特产(懂的都懂),模糊的视频看起是不是很不舒服,长期久了还会影响视力影…

这些可视化Python库非常强!

介绍的大体流程是:库名、类型、github star、功能、使用方法、案例、学习资料。 第一部分:数据可视化 pyecharts 类型:可视化图表设计 GitHub Star :5985 功能: 简洁的 API 设计,使用如丝滑般流畅&am…

LLM(二):Prompt

一,什么是Prompt 在人工智能领域,Prompt指的是用户给大型语言模型发出的指令。作用是引导模型生成符合预期主题或内容的文本,从而控制生成结果的方向和内容。 大模型是根据用户提出的问题来输出下文,所以用户提出的问题的质量也…

三种智能指针

智能指针 new和delete 1:new初始化 new未初始化值 int *p new int;//p值未定义string *str new string;//为空串&#xff0c;调用string默认构造函数new 初始化值 int *p new int(100);string *str new string(6,a);//aaaaaavector类型指针 vector<int> *p new v…

pikepdf:一个实用的PDF文件处理Python库

我是东哥&#xff0c;今天给大家介绍一个实用的Python库——pikepdf&#xff0c;它能让你像操作文本文件一样轻松地处理PDF&#xff0c;无论是读取、修改还是保存&#xff0c;都能迎刃而解。 基本介绍 pikepdf是一个基于Python的库&#xff0c;它允许开发者轻松地读取、写入和…

第四课,接收键盘输入

一&#xff0c;关于基本框架中头文件的作用 头文件就是一个工具箱&#xff0c;C中有很多工具&#xff0c;我们最熟悉的cout就是其中之一 引入头文件&#xff1a;如果你想在你的代码中使用工具箱里的工具&#xff0c;C会很大方的让你用&#xff0c;但前提是你必须在本页代码的最…

为什么 CNC 加工会产生毛刺?

在现代机械加工领域&#xff0c;CNC(计算机数控)加工以其高精度、高效率的特点被广泛应用。然而&#xff0c;在 CNC 加工过程中&#xff0c;毛刺的产生常常是一个令人困扰的问题。时利和将解析为什么 CNC 加工会产生毛刺呢? 一、刀具磨损 刀具在长时间的使用过程中会逐渐磨损。…

如何一步快速去除黑神话悟空图片上的文字?一招教会你

设计师朋友们&#xff0c;如果老板让你用去除海报上的文字&#xff0c;你会怎么做&#xff1f; 用PS的内容识别填充&#xff0c;图片就会变模糊再精修简直太麻烦啦&#xff01; 还好我最近找到一个图片处理神器&#xff0c;一键就能P去图片的文字&#xff01;简单又高效&…

day04-面向对象-常用API时间Arrays

一、常用API 1.1 StringBuilder类 StringBuilder类代表可变的字符串对象&#xff0c;理解为一个操作字符串的容器相对于String来说,比本来的操作效率更高 ​ StringBuilder常用方法public StringBuilder(): 构建一个空的可变字符串对象public StringBuilder(String str): 构建…

vue3中vite基于vite-plugin-html的多入口打包

先看打包效果 1、安装vite-plugin-html 2、配置多个入口 每个入口都要有模板(index.html、App.vue、main.js复制一份&#xff0c;根据实际需求调整三个文件) 3、配置vite.config.js 4、代码片段 import { createHtmlPlugin } from vite-plugin-htmlconst htmlParams {minif…

关于springboot的Rest请求映射处理的源码分析(一)

我们在开发中很常见的一种方式是通过请求的类型&#xff0c;也就是restful风格来区别我们的请求接口。 通过请求类型的不同(GET POST PUT DELETE) 来区分即便是请求路径相同的请求。 但是他的底层是如何支持的呢&#xff0c;明明我请求路径都是/user。就因为类型不同就能区分到…

网络层 III(划分子网和构造超网)【★★★★★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、网络层转发分组的过程 分组转发都是基于目的主机所在网络的&#xff0c;这是因为互联网上的网络数远小于主机数&#xff0c;这样可以极大地压缩转发表的大小。…

【Python报错】AttributeError`:`‘NoneType‘ object has no attribute ‘XXXX‘`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言 在Python编程中&#xff0c;AttributeError是一个常见的错误类型&#xff0c;它表示尝试访问的对象没有该属性。本文将探讨…

深度强化学习算法(六)(附带MATLAB程序)

深度强化学习&#xff08;Deep Reinforcement Learning, DRL&#xff09;结合了深度学习和强化学习的优点&#xff0c;能够处理具有高维状态和动作空间的复杂任务。它的核心思想是利用深度神经网络来逼近强化学习中的策略函数和价值函数&#xff0c;从而提高学习能力和决策效率…