FreeRTOS(计数信号量)

news2024/11/24 8:46:10

 资料来源于硬件家园:资料汇总 - FreeRTOS实时操作系统课程(多任务管理)

目录

一、计数信号量的定义与应用

1、计数信号量的定义

2、计数信号量的应用

二、计数信号量的运作机制

1、任务间计数信号量的实现

三、计数信号量常用的API函数

 1、计数信号量典型流程与API函数

2、计数信号量创建与删除

3、任务中计数信号量释放

4、中断中计数信号量释放

5、计数信号量获取

四、计数信号量编程

1、启用计数信号量

 2、创建计数信号量

 3、重要代码


一、计数信号量的定义与应用

1、计数信号量的定义

①取值只有0与1两种状态的信号量称之为二值信号量;取值大于1的信号量称之为计数信号量

②计数信号量是一种长度大于1,消息大小为0的特殊消息队列。  

③计数信号量的取值也可以为1,但通常大于1,如果取值为1,相当于只有0与1两种状态,用二值信号量即可。

④创建计数信号量时,系统会为创建的计数信号量分配内存,计数信号量创建完成后的示意图如下:

图片

2、计数信号量的应用

在嵌入式操作系统中,计数信号量是资源管理的重要手段,主要用于任务与任务间。

应用场景:

计数信号量允许多个任务对其进行操作,但限制了任务的数量。比如有一个停车场,里面只有50个车位,那么只能停50辆车,相当于我们的信号量有50 个。假如一开始停车场的车位还有50个,那么每进去一辆车就要消耗一个停车位,车位的数量就要减1,相应地,我们的信号量在使用之后也需要减 1。当停车场停满了50 辆车时,此时的停车位数量为 0,再来的车就不能停进去了,否则将没法停车了,也相当于我们的信号量为0,后面的任务对这个停车场资源的访问也无法进行。当有车从停车场离开时,车位又空余出来了,那么后面的车就能停进去了。信号量操作也是一样的,当我们释放了这个资源,后面的任务才能对这个资源进行访问。

二、计数信号量的运作机制

1、任务间计数信号量的实现

任务间信号量的实现是指各个任务之间使用信号量实现任务的同步或者资源共享功能。

①运行条件:

 创建 任务 Task1 和 Task2至N。 

 创建计数信号量可用资源为N。

②运行过程描述如下:

 任务 Task1 运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于0,Task1 将直接获取资源。如果信号量为0,任务 Task1 将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。

 任务 Task2至N 运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于0,Task2至N 将直接获取资源。如果信号量为0,任务 Task2至N将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。

三、计数信号量常用的API函数

 1、计数信号量典型流程与API函数

创建计数信号量   xSemaphoreCreateCounting()

释放计数信号量   xSemaphoreGive() 与 xSemaphoreGiveFromISR() 

获取计数信号量   xSemaphoreTake()

删除计数信号量    vSemaphoreDelete()

2、计数信号量创建与删除

①计数信号量控制块(句柄)

如下图:计数信号量的句柄为消息队列的句柄,因为计数信号量是一种长度大于1,消息大小为0的特殊消息队列

图片

图片

②计数信号量创建

函数原型:SemaphoreHandle_t xSemaphoreCreateCounting(

                                                              UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);

函数描述:函数 xSemaphoreCreateCounting 用于创建计数信号量。 

 第 1 个参数是设置此计数信号量支持的最大计数值。 

 第 2 个参数是设置计数信号量的初始值。

 返回值,如果创建成功会返回消息队列的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足,无法为此消息队列提供所需的空间会返回 NULL

说明:此函数基于消息队列函数实现:

图片

应用举例:

图片

③计数信号量删除

函数原型:void vSemaphoreDelete(SemaphoreHandle_t  xSemaphore)

函数描述:函数 vSemaphoreDelete可用于删除计数信号量。 

3、任务中计数信号量释放

函数原型:xSemaphoreGive( SemaphoreHandle_t xSemaphore ); 

函数描述:函数 xSemaphoreGive 用于在任务代码中释放信号量。

 第 1 个参数是信号量句柄。

 返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。

使用这个函数要注意以下问题:

1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xSemaphoreGiveFromISR。

2. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者xSemaphoreCreateCounting()创建了信号量。

3. 此函数不支持使用 xSemaphoreCreateRecursiveMutex()创建的信号量。

应用举例:

图片

4、中断中计数信号量释放

函数原型:xSemaphoreGiveFromISR ( SemaphoreHandle_t xSemaphore, 

                                                               signed BaseType_t *pxHigherPriorityTaskWoken)

函数描述:函数 xSemaphoreGiveFromISR 用于中断服务程序中释放信号量。

 第 1 个参数是信号量句柄。

 第 2 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE,说明有高优先级任务要执行,否则没有。

 返回值,如果信号量释放成功返回 pdTRUE,否则返回 errQUEUE_FULL。

使用这个函数要注意以下问题:

1. 此函数是基于消息队列函数 xQueueGiveFromISR 实现的:#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

2. 此函数是用于中断服务程序中调用的,故不可以任务代码中调用此函数,任务代码中中使用的是xSemaphoreGive。

3. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary()或者 xSemaphoreCreateCounting()创建了信号量。

4. 此函数不支持使用 xSemaphoreCreateMutex ()创建的信号量。

5、计数信号量获取

函数原型:xSemaphoreTake( SemaphoreHandle_t xSemaphore,

                                                  TickType_t xTicksToWait ); 

函数描述:函数 xSemaphoreTake 用于在任务代码中获取信号量。 

 第 1 个参数是信号量句柄。 

 第 2 个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。 返回值,如果创建成功会获取信号量返回 pdTRUE,否则返回 pdFALSE。

使用这个函数要注意以下问题:

1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xSemaphoreTakeFromISR。

2. 如果消息队列为空且第 2 个参数为 0,那么此函数会立即返回。

3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

应用举例:

图片

四、计数信号量编程

1、启用计数信号量

 2、创建计数信号量

 3、重要代码

int KEY_Read(void)
{
	int KeyCode = KEYNULL;

	//�?测KEY0
	if(KeyCode == KEYNULL)
	{
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
		{
			KeyCode = KEY0;
			//等待KEY0释放
			while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
			{
				osDelay(20); //必须用阻塞延�?
			}
		}
	}
	//�?测KEY1
	if(KeyCode == KEYNULL)
	{
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
		{
			KeyCode = KEY1;
			//等待KEY1释放
			while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
			{
				osDelay(20); //必须用阻塞延�?
			}
		}
	}
	return KeyCode;
}
/* USER CODE END Application */
void KEY_Task(void const * argument)
{
  /* USER CODE BEGIN KEY_Task */
	BaseType_t xResult;
	char buff[100];
  /* Infinite loop */
  for(;;)
  {
	  int KeyCode=KEY_Read();
	  //KEY0按下,获取信号量
	  if(KeyCode==KEY0)
	  {
		sprintf(buff,"%s \r\n","获取计数信号量,模拟车辆入库,申请停车位");
		HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
		xResult = xSemaphoreTake(myCountingSem01Handle,0);
			if(xResult == pdTRUE)
			{
				sprintf(buff,"%s \r\n","获取成功,成功申请停车位,发送同步显示信号");
				HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
				myCountingSem_ucMessagesWaiting--;
				sprintf(buff,"停车位剩余:%d \r\n",myCountingSem_ucMessagesWaiting);
				HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
			}
			else
			{
				sprintf(buff,"%s \r\n","获取失败,停车位已满");
				HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
			}
	  }
	  //KRY1按下,释放信号量
	  if(KeyCode==KEY1)
	  {
			sprintf(buff,"%s \r\n","释放计数信号量,模拟车辆出库,让出停车位");
			HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
		  xResult = xSemaphoreGive(myCountingSem01Handle);

			if(xResult == pdTRUE)
			{
				sprintf(buff,"%s \r\n","释放成功,成功让出停车位,发送同步显示信号");
				HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
				myCountingSem_ucMessagesWaiting++;

			}
			else
			{
				sprintf(buff,"%s \r\n","释放失败,停车位已空");
				HAL_UART_Transmit(&huart2, (uint8_t*)buff,strlen(buff), HAL_MAX_DELAY);
			}
	  }

    osDelay(20);
  }
  /* USER CODE END KEY_Task */
}
  /* USER CODE BEGIN RTOS_SEMAPHORES */
  if(myCountingSem01Handle==NULL)
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建计数信号量失败! \r\n",16, HAL_MAX_DELAY);
  else
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建计数信号量成功! \r\n",16, HAL_MAX_DELAY);
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */
uint8_t myCountingSem_ucMessagesWaiting = 20;
enum
{
	KEYNULL = -1,
	KEY0 = 0,
	KEY1 = 1,
};

/* USER CODE END Variables */
osThreadId KEYHandle;
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "string.h"
#include <stdio.h>
/* USER CODE END Includes */

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

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

相关文章

泛型编程| 模板初阶——懒人福音!

目录 前言介绍 函数模板 函数模板格式 函数模板的原理 函数模板的实例化 隐式实例化 显示实例化 模板参数匹配规则 类模板 总结 前言介绍 之前c语言实现swap函数的时候&#xff0c;我们不仅要修改参数的类型&#xff0c;还要修改函数的名字 而在学完函数重载之后&am…

东南大学齿轮箱故障诊断(Python代码,MSCNN结合LSTM结合注意力机制模型,代码有注释)

运行代码要求&#xff1a; 代码运行环境要求&#xff1a;Keras版本>2.4.0&#xff0c;python版本>3.6.0 1.东南大学采集数据平台&#xff1a; 数据 该数据集包含2个子数据集&#xff0c;包括轴承数据和齿轮数据&#xff0c;这两个子数据集都是在传动系动力学模拟器&am…

Android JNI实现锅炉压力显示系统详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 第一步创建GuoLu.c文件 // // Created by DELL on 2023/8/13. // #include <stdio.h…

Python 潮流周刊#15:如何分析 FastAPI 异步请求的性能?

你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。标题取自其中一则分享&#xff0c;不代表全部内容都是该主题&#xff0c;特此声明。 本周刊精心筛选国内外的 250 信息源&#xff0c;为你挑选最值得分享的文章、教程、开源…

记录一下关于word存放代码出现的问题

如下图所示&#xff0c;从 Word 中复制代码并粘贴到其他地方&#xff0c;例如文本编辑器或代码编辑器中&#xff0c;有时会出现额外的连字符或破折号。这是因为 Word 使用了特定的字符编码和格式&#xff0c;而这些字符在代码中可能不被支持或解析为特殊字符。   可见有时从…

Java线程调度以及算法

线程调度概况 Java的线程调度程序是JVM的一部分&#xff0c;它决定应该运行哪个线程。无法保证线程调度程序将选择运行哪个可运行线程。 一次只能有一个线程在一个进程中运行。线程调度程序主要使用抢占式或时间切片调度来调度线程。 抢占式调度与时间分片的区别 在抢占式调…

边缘计算:下一代计算模式的突破

章节一&#xff1a;引言 随着物联网、人工智能和大数据等技术的不断发展&#xff0c;计算需求变得越来越复杂&#xff0c;传统的云计算模式已经难以满足快速增长的数据处理需求。在这样的背景下&#xff0c;边缘计算作为一种全新的计算模式崭露头角&#xff0c;为我们带来了更加…

Pytorch深度学习-----实现神经网络模型在GPU上进行训练的方法

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

css3新增选择器总结

目录 一、属性选择器 二、结构伪类选择器 三、伪元素选择器 四、UI状态伪类选择器 五、反选伪类选择器 六、target选择器 七、父亲选择器、后代选择器 八、相邻兄弟选择器、兄弟们选择器 一、属性选择器 &#xff08;除IE6外的大部分浏览器支持&#xff09; E&#…

数据库设计,理解第二范式和第三范式的区别

在学习数据库范式的时候&#xff0c;很多人搞不清第二范式和第三范式他到底是有啥区别。 想讲清楚第二范式与第三范式的区别&#xff0c;不得不聊到键和相关属性的概念 键和相关属性的概念 范式的定义会使用到主健和候选健&#xff0c;数据库中的健&#xff08;Key)由一个或…

STM32F103C8T6开发笔记1:有线陀螺仪二自由度机械臂

经过之前几天的快速学习&#xff0c;今日尝试组装一款基于MPU6050陀螺仪控制的二自由度机械臂&#xff0c;本文对其使用器材以及基本原理进行介绍~ 组装效果图&#xff1a; 主要元器件如下&#xff1a; 器件个数15 KG以上 舵机3适合舵机的金属夹爪118650电池电源12V1云台支架2…

工博士与纷享销客达成战略合作,开启人工智能领域合作新篇章

近日&#xff0c;工博士与纷享销客在上海正式签署了战略合作协议&#xff0c;正式拉开了双方在人工智能与数字营销领域的合作序幕。这次合作将为双方带来更多机遇和发展空间&#xff0c;并为全球人工智能领域的客户提供更高效、智能的CRM解决方案。 < 双方项目人员合影 >…

React如何配置env环境变量

React版本&#xff1a; "react": "^18.2.0" 1、在package.json平级目录下创建.env文件 2、在‘.env’文件里配置环境变量 【1】PUBLIC_URL 描述&#xff1a;编译时文件的base-href 官方描述&#xff1a; // We use PUBLIC_URL environment variable …

③ vue组件

vue组件创建 在App.vue中添加。 技巧&#xff1a;先import&#xff0c;把vue组件地址写出来。然后在template中写名字。剩下的就自动生成。要看下import有没有多生成什么。 注意1&#xff1a; 注意2&#xff1a; 不只是能在App.vue中引入组件。任意组件中都可以引用其他组件…

msvcp110.dll丢失怎样修复,msvcp110.dll丢失修复方法

msvcp110.dll是Microsoft C库的一部分&#xff0c;它是运行依赖于该库的程序所必需的动态链接库文件。它的作用是提供C运行时库函数的实现&#xff0c;这些函数用于处理程序的内存管理、异常处理、多线程支持等。当系统中缺少或损坏了msvcp110.dll文件时&#xff0c;请及时修复…

STM32--TIM定时器(1)

文章目录 TIM简介定时器类型 通用定时器预分频器时序计数器时序定时中断基本结构TIM内部中断工程TIM外部中断工程 TIM简介 STM32的TIM&#xff08;定时器&#xff09;是一种非常常用的外设&#xff0c;用于实现各种定时和计数功能。它是基于时钟信号进行计数&#xff0c;并在计…

Redis的单线程与多线程

Redis的核心处理逻辑一直都是单线程 有一些分支模块是多线程(某些异步流程从4.0开始用的多线程&#xff0c;例如UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC等非阻塞的删除操作。网络I/O解包从6.0开始用的是多线程;) 为什么是单线程 多线程多好啊可以利用多核优势 官方给的解释 …

二、编写第一个 Spring MVC 程序

文章目录 一、编写第一个 Spring MVC 程序 一、编写第一个 Spring MVC 程序 代码示例 创建 maven 项目&#xff0c;以此项目为父项目&#xff0c;在父项目的 pom.xml 中导入相关依赖 <dependencies><dependency><groupId>junit</groupId><artifactI…

阿里云ACP知识点

前言&#xff1a;记录ACP错题 1、在创建阿里云ECS时&#xff0c;每台服务器必须要包含_______用来存储操作系统和核心配置。 系统盘&#xff08;不是实例&#xff0c;实例是一个虚拟的计算环境&#xff0c;由CPU、内存、系统盘和运行的操作系统组成&#xff1b;ESC实例作为云…

web基础和tomcat的安装,部署jpress应用

目录 1. 简述静态网页和动态网页的区别。 2. 简述 Webl.0 和 Web2.0 的区别。 3. 安装tomcat8&#xff0c;配置服务启动脚本&#xff0c;部署jpress应用。 1. 简述静态网页和动态网页的区别。 【1】定义区别 请求响应信息&#xff0c;发给客户端进行处理&#xff0c;由浏览…