利用STM32CubeMX和Keil模拟器,3天入门FreeRTOS(5.3) ——递归锁

news2024/11/24 9:07:06

前言

(1)FreeRTOS是我一天过完的,由此回忆并且记录一下。个人认为,如果只是入门,利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后,再去学习网上的一些其他课程也许会简单很多。
(2)本系列课程是使用的keil软件仿真平台,所以对于没有开发板的同学也可也进行学习。
(3)叠甲,再次强调,本系列课程仅仅用于入门。学习完之后建议还要再去寻找其他课程加深理解。
(4)本系列博客对应代码仓库:gitee仓库

前期准备

(1)复制上一篇博客的工程下来

在这里插入图片描述

实战

使用STM32CubeMX创建递归锁

(1)按照如下步骤

在这里插入图片描述

使用keil端创建递归锁

(1)创建递归锁(按Ctrl+F搜索add mutexes

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
	// 普通互斥量
	KeilMutexHandle = xSemaphoreCreateMutex();
	// 递归锁
	KeilRecursiveMutexHandle = xSemaphoreCreateRecursiveMutex();
  /* USER CODE END RTOS_MUTEX */

应用代码

代码

(1)补充如下代码(按Ctrl+F搜索Header_StartCubemxTask

/* USER CODE END Header_StartCubemxTask */
#define Test_RecursiveMutex  1 //如果为1,测试递归锁。为0测试普通互斥量的死锁问题
void Test_DeadLock(void)
{
#if Test_RecursiveMutex
	xSemaphoreTakeRecursive(KeilRecursiveMutexHandle, portMAX_DELAY);
#else
	xSemaphoreTake(KeilMutexHandle, portMAX_DELAY);
#endif /* Test_RecursiveMutex */
	printf("Test_RecursiveMutex\r\n");
#if Test_RecursiveMutex
	xSemaphoreGiveRecursive(KeilRecursiveMutexHandle);
#else
	xSemaphoreGive(KeilMutexHandle);
#endif /* Test_RecursiveMutex */
}

void StartCubemxTask(void *argument)
{
  /* USER CODE BEGIN StartCubemxTask */
  /* Infinite loop */
  for(;;)
  {
#if Test_RecursiveMutex
		xSemaphoreTakeRecursive(KeilRecursiveMutexHandle, portMAX_DELAY);
#else
		xSemaphoreTake(KeilMutexHandle, portMAX_DELAY);
#endif /* Test_RecursiveMutex */
		// 这里是刻意采用阻塞延时,目的是增加低优先级任务执行时间
		HAL_Delay(10);
		Task_H = 0;
		Task_M = 0;
		Task_L = 1;
		//测试互斥量死锁问题
		Test_DeadLock();
#if Test_RecursiveMutex
		xSemaphoreGiveRecursive(KeilRecursiveMutexHandle);
#else
		xSemaphoreGive(KeilMutexHandle);
#endif /* Test_RecursiveMutex */
  }
  /* USER CODE END StartCubemxTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
void StartKeilTask_M(void *argument)
{
	vTaskDelay(50/portTICK_PERIOD_MS);	
	BaseType_t xStatus;	
	while(1)
	{		
		Task_H = 0;
		Task_M = 1;
		Task_L = 0;

	}
}
void StartKeilTask_H(void *argument)
{
	vTaskDelay(100/portTICK_PERIOD_MS);		
	while(1)
	{
#if Test_RecursiveMutex
		xSemaphoreTakeRecursive(KeilRecursiveMutexHandle, portMAX_DELAY);
#else
		xSemaphoreTake(KeilMutexHandle, portMAX_DELAY);
#endif /* Test_RecursiveMutex */
		Task_H = 1;
		Task_M = 0;
		Task_L = 0;
#if Test_RecursiveMutex
		xSemaphoreGiveRecursive(KeilRecursiveMutexHandle);
#else
		xSemaphoreGive(KeilMutexHandle);
#endif /* Test_RecursiveMutex */
	}
}
/* USER CODE END Application */

使用存在的小问题—FreeRTOS的堆空间配置

(1)FreeRTOS创建任务时默认的任务栈大小为128字,在32位系统中即为128*4=512Byte,再加上TCB块占用84Byte,一共596Byte。而大小为3072Byte的堆允许创建3个这样的任务,占用约1800Byte。堆中剩余的部分则存放了系统内核、信号量、队列、任务通知等数据。
(2)而这里如果模拟运行,会发现第三个任务无法成功创建。

在这里插入图片描述

(3)
<1>此时我们可以看一下打印的错误数据,会发现是返回的errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。而这个错误表示内存分配失败。
<2>当时我任务是因为STM32F103C8T6RAM空间不足导致的问题。之后我更换为STM32F103RCT6,依旧存在这个问题。
<3>后面查阅资料后才发现,我们可以通过FreeRTOSConfig.hconfigTOTAL_HEAP_SIZE 这个宏来设置RAM空间分配给 FreeRTOS 的堆。于是我将默认的3072修改为3500,程序就成功运行起来了。

在这里插入图片描述

//修改前
#define configTOTAL_HEAP_SIZE                    ((size_t)3072)
// 修改后
#define configTOTAL_HEAP_SIZE                    ((size_t)3500)

(4)注意,如果是使用的STM32CubeMX,是按照如下步骤修改

在这里插入图片描述

FreeRTOS堆分配技巧

(1) 在前面我们介绍静态创建任务的时候曾经讲解过,如何分配任务栈空间。而如果是动态创建任务,那么就需要在FreeRTOS的堆上使用类似malloc函数进行申请空间。这里正好遇到了这个问题,那么我正好做补充,如何分配FreeRTOS的堆空间。
(2)介绍静态创建任务的时候,我们介绍了uxTaskGetStackHighWaterMark()函数,可以通过这个函数知道当前任务剩余的栈空间。而FreeRTOS的堆空间剩余,官方也提供了两个API接口。
(3)注意
<1>xPortGetFreeHeapSize()在使用heap_3.c时不能被调用。
<2>xPortGetMinimumEverFreeHeapSize()只能在使用heap_4.cheap_5.c时生效。
(4)FreeRTOS一般默认使用的是heap_4.c

size_t xPortGetFreeHeapSize( void );	         //获取当前未分配的内存堆大小
size_t xPortGetMinimumEverFreeHeapSize( void );	 //获取未分配的内存堆历史最小值

(5)如果需要修改FreeRTOS内存管理策略,进入FreeRTOSConfig.hCtrl+F搜索USE_FreeRTOS_HEAP将其设置为自己想要的内存管理策略。

/* 像如下写法,就是使用的heap_3.c */
//#define USE_FreeRTOS_HEAP_1
//#define USE_FreeRTOS_HEAP_2
#define USE_FreeRTOS_HEAP_3
//#define USE_FreeRTOS_HEAP_4
//#define USE_FreeRTOS_HEAP_5

在这里插入图片描述

(6)如果是使用的STM32CubeMX,按照如下方法调整FreeRTOS内存管理策略。

在这里插入图片描述

仿真结果

(1)将Test_RecursiveMutex置为0,进行死锁实验,会发现互斥量永远被低优先级任务持有并且卡死。导致高优先级任务永远无法被执行。

在这里插入图片描述

(2)将Test_RecursiveMutex置为1,利用递归锁解决死锁问题。高优先级任务想要获取锁的时候,发现锁被低优先级任务持有,进行优先级继承。

在这里插入图片描述

理论

(1)在前文我们介绍了互斥量的知识,我们知道,互斥量是谁上锁谁解锁。我们知道,互斥量是谁上锁设谁解锁。但是互斥量有一个问题,就是只能上锁一次。
(2)这样存在一个问题,假如我们在临界区中,再一次想办法获取互斥量会怎么样呢?从上面的实验结果我们能够发现,会发生死锁。也就是高优先级任务永远无法获取到互斥量,也就永远无法被执行。
注意:之所以说永远,是因为我这里Take设置的等待时间是portMAX_DELAY。如果是其他值,例如10s,那么结果就是10s内高优先级任务无法被执行。

xSemaphoreTake(KeilMutexHandle, portMAX_DELAY);
//临界区
xSemaphoreGive(KeilMutexHandle);

(3)为了解决这个问题,FreeRTOS提出了一个新的处理办法——递归锁。递归锁可以解决在临界区中,依然需要进行获取锁的情况。(我暂时想不到有啥实际例子)
(4)需要注意,递归锁需要注意两点:
<1>谁上锁谁解锁。
<2>递归锁上锁了几次,那么就需要解锁几次。

API函数介绍

创建

(1)动态创建递归锁

/**
 * @brief  动态创建递归锁
 *
 * @param  无
 *
 * @return  如果成功创建了递归互斥锁,那么将返回创建的互斥锁的句柄。否则返回 NULL。
 */
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );

(2)静态创建递归锁

/**
 * @brief  静态创建递归锁
 *
 * @param  无
 *
 * @return 如果已成功创建递归互斥锁,则返回创建的创建的互斥锁的句柄。 如果由于pxMutexBuffer为NULL而导致 递归互斥锁未创建,则返回 NULL。
 */
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic(StaticSemaphore_t *pxMutexBuffer );

/* --- 使用方法 --- */
 SemaphoreHandle_t xSemaphore = NULL;
 StaticSemaphore_t xMutexBuffer;

 void vATask( void * pvParameters )
 {
    /* 创建一个不使用动态内存分配的递归锁。 
     * 递归锁的数据结构会被保存到xMutexBuffer变量中
    */
    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );

    /* pxMutexBuffer不是NULL,所以句柄不应该是NULL */
    configASSERT( xSemaphore );

    /* 任务代码的其余部分放在这里 */
 }

获取

(1)互斥量和信号量的获取函数都是xSemaphoreTake(),而递归锁的获取函数为xSemaphoreTakeRecursive()

/**
 * @brief  获取递归锁
 *
 * @param  xMutex        正在获得的互斥锁的句柄
 *        -xTicksToWait  等待递归锁变为可用的时间(以滴答为单位)
 *
 * @return 如果获得信号量,则返回 pdTRUE。 如果 xTicksToWait 过期,信号量不可用,则返回 pdFALSE。
 */
xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,TickType_t xTicksToWait );

释放

(1)互斥量和信号量的释放函数都是xSemaphoreGive(),而递归锁的是xSemaphoreGiveRecursive()

/**
 * @brief  释放递归锁
 *
 * @param  xMutex   正在释放或“给出”的互斥锁的句柄
 *
 * @return  如果成功给出信号量,则返回 pdTRUE
 */
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex );

参考

(1)韦东山freeRTOS快速入门:10-3_互斥量的缺陷和递归锁
(2)FreeRTOS官方手册:信号量/互斥锁
(3)C站:STM32内存结构介绍,FreeRTOS内存分配技巧,Stack_Size和Heap_Size大小设置
(4)FreeRTOS官方手册:内存管理

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

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

相关文章

物联网协议Coap之C#基于Mozi的CoapClient调用解析

目录 前言 一、CoapClient相关类介绍 1、CoapClient类图 2、CoapClient的设计与实现 3、SendMessage解析 二、Client调用分析 1、创建CoapClient对象 2、实际发送请求 3、Server端请求响应 4、控制器寻址 总结 前言 在之前的博客内容中&#xff0c;关于在ASP.Net Co…

循序渐进,学会用pyecharts绘制桑基图

循序渐进&#xff0c;学会用pyecharts绘制桑基图 桑基图介绍 桑基图是比较冷门的可视化图形&#xff0c;知道的人不多&#xff0c;但它的可视化效果很惊艳&#xff0c;以后肯定会有越来越多的人使用&#xff0c;我平时使用桑基图&#xff0c;主要是用其绘制可视化图形做PPT。…

签到业务流程

1.技术选型 Redis主写入查询&#xff0c;Mysql辅助查询&#xff0c;传统签到多数都是直接采用mysql为存储DB,在大数据的情况下数据库的压力较大.查询速率也会随着数据量增大而增加.所以在需求定稿以后查阅了很多签到实现方式,发现用redis做签到会有很大的优势.本功能主要用到r…

JVM系列——基础知识

Java运行区域 程序计数器&#xff08;Program Counter Register&#xff09; 程序计数器是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里[1]&#xff0c;字节码解释器工作时就是通过改变这个计数器的值来选取下一…

STM正点mini-跑马灯

一.库函数版 1.硬件连接 &#xff27;&#xff30;&#xff29;&#xff2f;的输出方式&#xff1a;推挽输出 &#xff29;&#xff2f;口输出为高电平时&#xff0c;P-MOS置高&#xff0c;输出为&#xff11;&#xff0c;LED对应引脚处为高电平&#xff0c;而二极管正&#…

[Tomcat] [从安装到关闭] MAC部署方式

安装Tomcat 官网下载&#xff1a;Apache Tomcat - Apache Tomcat 9 Software Downloads 配置Tomcat 1、输入cd空格&#xff0c;打开Tomca目录&#xff0c;把bin文件夹直接拖拉到终端 2、授权bin目录下的所有操作&#xff1a;终端输入[sudo chmod 755 *.sh]&#xff0c;回车 …

HCS-华为云Stack-FusionSphere

HCS-华为云Stack-FusionSphere FusionSphere是华为面向多行业客户推出的云操作系统解决方案。 FusionSphere基于开放的OpenStack架构&#xff0c;并针对企业云计算数据中心场景进行设计和优化&#xff0c;提供了强大的虚拟化功能和资源池管理能力、丰富的云基础服务组件和工具…

C++类和对象——深拷贝与浅拷贝详解

目录 1.深拷贝和浅拷贝是什么 2.案例分析 完整代码 1.深拷贝和浅拷贝是什么 看不懂没关系&#xff0c;下面有案例分析 2.案例分析 浅拷贝可能会导致堆区的内存重复释放 一个名为person的类里面有年龄和指向身高的指针这两个成员。 当我们执行到person p2&#xff08;p1&am…

翻译: GPT-4 with Vision 升级 Streamlit 应用程序的 7 种方式一

随着 OpenAI 在多模态方面的最新进展&#xff0c;想象一下将这种能力与视觉理解相结合。 现在&#xff0c;您可以在 Streamlit 应用程序中使用 GPT-4 和 Vision&#xff0c;以&#xff1a; 从草图和静态图像构建 Streamlit 应用程序。帮助你优化应用的用户体验&#xff0c;包…

深度学习-使用Labelimg数据标注

数据标注是计算机视觉和机器学习项目中至关重要的一步&#xff0c;而使用工具进行标注是提高效率的关键。本文介绍了LabelImg&#xff0c;一款常用的开源图像标注工具。用户可以在图像中方便而准确地标注目标区域&#xff0c;为训练机器学习模型提供高质量的标注数据。LabelImg…

【VB测绘程序设计】案例8——IF选择结构练习排序(附源代码)

【VB测绘程序设计】案例6——IF选择结构练习排序(附源代码) 文章目录 前言一、界面显示二、程序说明三、程序代码四、数据演示总结前言 本文主要掌握Val()函数转换,inputBox函数、IF条件句的练习,输入3个数,按大到小排序并打印。 一、界面显示 二、程序说明 利用inpu…

node.js Redis SETNX命令实现分布式锁解决超卖/定时任务重复执行问题

Redis SETNX 特性 当然&#xff0c;让我们通过一个简单的例子&#xff0c;使用 Redis CLI&#xff08;命令行界面&#xff09;来模拟获取锁和释放锁的过程。 在此示例中&#xff0c;我将使用键“lock:tcaccount_[pk]”和“status:tcaccount_[pk]”分别表示锁定键和状态键。 获…

搞定App关键词和评论

从关键词优化的三大基本概念走起&#xff01; 关联性 优化师一般如何选择关联性高的关键词呢&#xff1f; 主要思路如下&#xff1a;品牌词-关联词-竞品词-竞品关键词&#xff0c;优先级从前到后依次降低&#xff0c;通过ASO优化工具筛选出合适的关键词。做ASO有一个好处就是…

vusui css 使用,简单明了 适合后端人员 已解决

vusui-cssopen in new window 免除开发者繁复的手写 CSS 样式&#xff0c;让 WEB 前端开发更简单、灵活、便捷&#xff01;如果喜欢就点个 ★Staropen in new window 吧。 移动设备优先&#xff1a; vusui-css 包含了贯穿于整个库的移动设备优先的样式。浏览器支持&#xff1a…

vue3使用最新的属性defineModel实现父子组件数据响应式绑定

子父之间使用v-model双向绑定数据&#xff0c;子组件每次都要写emit和props觉得麻烦&#xff1f;以前&#xff0c;为了使组件支持与v-model双向绑定&#xff0c;它需要&#xff08;1&#xff09;声明prop&#xff0c;&#xff08;2&#xff09;在打算更新prop时发出相应的updat…

用C语言实现贪吃蛇游戏!!!

前言 大家好呀&#xff0c;我是Humble&#xff0c;不知不觉在CSND分享自己学过的C语言知识已经有三个多月了&#xff0c;从开始的C语言常见语法概念说到C语言的数据结构今天用C语言实现贪吃蛇已经有30余篇博客的内容&#xff0c;也希望这些内容可以帮助到各位正在阅读的小伙伴…

Linux cat,tac,more,head,tail命令 查看文本

目录 一. cat 和 tac命令二. head 和 tail 命令三. more命令 一. cat 和 tac命令 cat&#xff1a;用来打开文本文件&#xff0c;从上到下的顺序显示文件内容。tac&#xff1a;用法和cat相同&#xff0c;只不过是从下到上逆序的方式显示文件内容。当文件的内容有很多的时候&…

canvas绘制旋转的大风车

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

vue中的Mutations

目录 一&#xff1a;介绍 二&#xff1a;例子 一&#xff1a;介绍 Vuex 中的 mutation 非常类似于事件&#xff1a; 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的函数&#xff0c;并且它会接受 sta…

操作系统-线程的实现方式和多线程模型(用户级线程 内核级线程 多线程模型的情况)和线程的状态,转换,组织,控制

文章目录 线程的实现方式和多线程模型总览线程的实现方式用户级线程内核级线程多线程模型一对一多对一多对多 小结 线程的状态,转换,组织,控制总览 线程的状态与转换线程的组织与控制 线程的实现方式和多线程模型 总览 线程的实现方式 用户级线程 程序自己通过自己设计的线程…