FreeRTOS源码分析-8 信号量

news2025/1/19 3:21:04

1 信号量概念及应用

 

信号灯解决的问题:

行人、车辆共享一个马路共享资源的问题,第二个解决通过的问题。

计数停车位的问题:

解决共享资源停车位计数的问题,因为车位是有限的,客流是无限的,计数可以很好的解决这个问题。

FreeRTOS提供了3种信号量:二值信号量、计数信号量、互斥信号量。三种信号量都是基于消息队列开发的,因为消息队列既能计数又能阻塞。

二值信号量: 二值信号量是最简单的信号量类型,只有两个状态:0和1。它常用于控制共享资源的访问权。当一个任务想要访问临界资源时,首先会尝试获取二值信号量。如果信号量的值为1,表示资源可用,任务可以继续执行;如果信号量的值为0,表示资源被占用,任务会被阻塞等待。当资源释放后,持有该信号量的任务会将信号量的值置为1,唤醒等待的任务。 

 计数信号量: 计数信号量可以有多个状态值,用于控制多个任务对共享资源的访问。当一个任务想要访问临界资源时,它会尝试获取计数信号量。如果信号量的值大于0,则表示资源可用,任务可以继续执行,并将信号量的值减少1。如果信号量的值为0,则表示资源暂时不可用,任务会被阻塞等待。当资源释放后,持有该信号量的任务会将信号量的值增加,并唤醒等待的任务。

2 二值信号量函数应用

2.1 功能需求

  • 修改按键功能
  • 当按键按下触发打印一次CPU利用率
  • 使用二值信号量实现按键与任务间同步

2.2 API介绍

这里errQUEUQ_FULL代表信号量释放失败,已经信号量可用了,与上面有所区别。

 

2.3 功能实现

利用FreeRTOS创建一个信号量 

创建信号量,创建函数在MX_FREERTOS_Init中

void MX_FREERTOS_Init(void) {

  /* definition and creation of CpuPrintfBinarySem */
  osSemaphoreDef(CpuPrintfBinarySem);
  CpuPrintfBinarySemHandle = osSemaphoreCreate(osSemaphore(CpuPrintfBinarySem), 1);
   
    //其他业务省略
}

按键按下时给出信号量

#include "gpio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

teKeyStatus KeyStatus;
extern osSemaphoreId CpuPrintfBinarySemHandle;   //扩展引用信号量句柄


void MX_GPIO_Init(void)
{

  //略

}


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){

	if(Key3_Pin == GPIO_Pin){
		if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET){
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET){
			 
				printf("key3 is down!\r\n");
                //按键按下时,给出信号量(中断中)
				xSemaphoreGiveFromISR(CpuPrintfBinarySemHandle,NULL);
			
			}
		
		}else{
				HAL_Delay(10);
				if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_SET){
			    printf("key3 is up!\r\n");
			
			}
		}
	}
}

Key任务中获取信号量(任务中去除了OsDelay(10),CPU使用率得到了提升)

void Key_Task(void const * argument)
{
  KeyStatus = KEY_RESET;

  for(;;)
  {
    //获取信号量后执行函数
	if(xSemaphoreTake(CpuPrintfBinarySemHandle,portMAX_DELAY) == pdPASS){
		memset(u8TaskListBuff,0,400);
		vTaskGetRunTimeStats((char*)u8TaskListBuff);
		printf("Name      Abs Time        Time\r\n");
		printf("******************************************************\r\n");
		printf("%s",u8TaskListBuff);
		printf("******************************************************\r\n");
		KeyStatus = KEY_RESET;
	}
  }
}

3 计数信号量函数应用

3.1功能需求

  • 修改按键功能,模拟停车位出入功能(创建信号量)
  • 当按键K3按下获取车位(获取信号量)
  • 当按键K4按下释放车位(释放信号量)

3.2API介绍

 

3.3功能实现

CubeMX配置使能计数信号量

创建信号量

 

  //FreeRTOS会根据CubeMX帮我们创建任务
  osSemaphoreDef(KeyCountingSem);
  KeyCountingSemHandle = osSemaphoreCreate(osSemaphore(KeyCountingSem), 4);

KEY3按下获取信号量,KEY4按下释放信号量

//按键检测回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){

	//是不是KEY3
	if(Key3_Pin == GPIO_Pin)
    {
		//key3是否按下
		if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
        {
			//软件去抖动
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
            {
			 
				//建立一个标志位
				printf("key3 is down!\r\n");
				KeyStatus = KEY_DOWN;
				if(xSemaphoreTakeFromISR(KeyCountingSemHandle,NULL) == pdPASS)
                {
					
					printf("获取车位成功!\r\n");
				
				}
				else{
					printf("获取车位失败! 车位已经占满!\r\n");
				
				}
			}
		}
        else
        {
		    
		    //软件去抖动
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_SET)
            {
				//建立一个标志位
			    printf("key3 is up!\r\n");
				KeyStatus = KEY_UP;
			}
		}
	}
	//是不是Key4
	if(Key4_Pin == GPIO_Pin){
		//Key4是否按下
		if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
        {
			//软件去抖动
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
            {
				//建立一个标志位
				printf("Key4 is down!\r\n");
				KeyStatus = KEY_DOWN;
				if(xSemaphoreGiveFromISR(KeyCountingSemHandle,NULL) == pdPASS)
                {
					printf("释放车位成功!\r\n");
				}
				else
                {
					printf("释放车位失败! 车位为空!\r\n");
				}
			}
		}
        else
        {
			//软件去抖动
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_SET
            {
				//建立一个标志位
			    printf("Key4 is up!\r\n");
				KeyStatus = KEY_UP;
			
			}
		}
	}
}

4 信号量实现原理

创建和删除

计数信号量创建内部也是调用xQueueGenericCreate,这个源码在消息队列中已经介绍过,只要分析消息队列长度、队列的类型

创建源码分析(删除参考消息队列)

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateBinary() 
	/*
		1、队列长度=1 二值信号的值 无非就是0和1
		2、队列项的长度=semSEMAPHORE_QUEUE_ITEM_LENGTH   = ( ( uint8_t ) 0U )消息空间没有意义
		3、队列的类型=queueQUEUE_TYPE_BINARY_SEMAPHORE   =( ( uint8_t ) 3U ) 只用于调试使用
	*/
	xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	/*
		内部调用消息队列计数信号量的创建,重点分析它
	*/
	#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) 
	xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif

	/*
		1、计数信号量的最大值
		2、计数信号量的初始值
	
	*/
	QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
	{
	QueueHandle_t xHandle;
		//调用消息队列的创建
		xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

		if( xHandle != NULL )
		{
			//uxMessagesWaiting:将要处理的消息个数,这个去接收,是不会进入阻塞态的
			//赋值为计数信号量的初始值的目的,创建之后,就可以获取信号量,代表可用的资源数量
			( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;

			traceCREATE_COUNTING_SEMAPHORE();
		}
		else
		{
			traceCREATE_COUNTING_SEMAPHORE_FAILED();
		}

		return xHandle;
	}

 信号量释放

信号量获取

 

 信号量发送和接收源码分析

/*
	消息队列的发送和接收,都有阻塞任务的功能
	信号量的释放,却没有阻塞参数?????
	参数:
	1、信号量的句柄
	2、发送的缓冲区  NULL
	3、阻塞等待时间 = semGIVE_BLOCK_TIME -=  ( ( TickType_t ) 0U )???
		信号量释放是一个紧急的事件,当信号量资源已经到达最大值时,就不需要再等待其他任务使用
		所以不需要阻塞
	4、队列插入方式=queueSEND_TO_BACK 队尾
*/	
#define xSemaphoreGive( xSemaphore )		
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

/*
	1、再次封装了消息队列在中断中的发送接口xQueueGiveFromISR
	2、区别就是 give没有copy的功能  -----因为信号量不占用内存空间
*/
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )	
xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

/*
	1、句柄
	2、接收缓冲区  = NULL 
	3、阻塞等待时间
	4、是否允许 删除消息空间 = pdFALSE,不删除给其他任务使用
*/
#define xSemaphoreTake( xSemaphore, xBlockTime )		
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )
/*
	1、句柄
	2、接收缓冲区  = NULL 
	3、NULL
*/
#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )	
xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )

问: 消息队列的发送和接收,都有阻塞任务的功能,信号量的释放,却没有阻塞参数?

信号量释放是一个紧急的事件,当信号量资源已经到达最大值时,就不需要再等待其他任务使用,所以不需要阻塞

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

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

相关文章

无涯教程-Lua - Iterators(迭代器)

迭代器是一种构造,使您可以遍历所谓的集合或集合的元素。在Lua中,这些集合通常引用表,这些表用于创建各种数据结构(如数组)。 通用迭代器 通用的 for 迭代器提供集合中每个元素的键值对。下面给出一个简单的示例。 array{"Lua",…

程序的编译和调试

gcc编译器 gcc(GNU Compiler Collection)是GNU推出的多平台编译器,可将C、C源程序编译连接成可执行文件,支持以下后缀: .c c语言源代码 .h 程序所包含的头文件 .i 已经预处理过的C源代码文件 .s 汇编语言源代码文件 .o 编译后的目标文件…

Leetcode69 x的平方根

二分查找法 代码 public int mySqrt(int x){int l 0, r x,ans -1;while(l < r){int mid l (r - l) / 2;if((long) mid * mid < x){ans mid;l mid 1;}else{r mid - 1;}}return ans; }

C++ 多文件结构和编译预处理命令——C++程序的一般组织结构

1. C程序的一般组织结构 C源程序的结构基本上都是由3个部分构成&#xff1a;类的定义、类的成员的实现和主函数。因为所编写的程序比较小&#xff0c;所以这三个部分都写在了同一个文件当中。在规模比较大的项目中&#xff0c;往往需要多个源程序文件&#xff0c;每个源程序文…

windows编译zookeeker动态库供C++链接使用以及遇到的错误处理方法

windows下面C链接zookeeper资料不多&#xff0c;特此记录一下 编译环境VS 2015 一. 相关安装包安装下载 1. zookeeper zookeeper3.6.4 下载zip包解压即可 2. ant apache-ant-1.9.16 将包进行解压D:project\apache-ant-1.9.16&#xff0c;然后配置环境变量 新建 ANT_HOME 系…

linux/windows如何退出telnet

在运维过程中&#xff0c;我们需要使用telnet来验证tcp端口的连通性&#xff0c;当tcp端口的连通性处于正常状态时&#xff0c;那么此时如果退出呢&#xff1f;下面就为大家讲一讲这个小常识&#xff0c;希望能对大家有所帮助。 1.Linux下telnet退出 如果没有telnet命令&…

Apache Flink概述

Flink 是构建在数据流之上的一款有状态的流计算框架&#xff0c;通常被人们称为第三代大数据分析方案 第一代大数据处理方案&#xff1a;基于Hadoop的MapReduce 静态批处理 | Storm 实时流计算 &#xff0c;两套独立的计算引擎&#xff0c;难度大&#xff08;2014年9月&#x…

第五章 Git

5-1、Git的安装 1、为什么要使用代码版本控制系统 【1】版本控制 【2】开发中存在的麻烦 2、Git和SVN的对比 【1】Git和SVN对比 &#xff08;1&#xff09;SVN &#xff08;2&#xff09;Git 3、Git下载和安装 【1】下载 【2】安装 一路下一步就好了&#xff0c;更换安装…

【力扣】 12. 整数转罗马数字 模拟

力扣 12. 整数转罗马数字 解题思路 当某个位数的某个数不为4或9时&#xff0c;高位对应的字符总是在低位对应的字符前面。只有当该数为4或9时&#xff0c;低位对应的字符在高位前面。 根据这一特性&#xff0c;我们进行分类讨论。 1.当数为4时&#xff0c;则对应的罗马数为 10 …

【心电图信号压缩】ECG信号压缩与通过三次样条近似重建的ECG信号压缩研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Vue [Day2]

指令修饰符 v-model.trim v-model.number 事件名.stop click.stop 事件名.prevent keyup.enter <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-w…

数据泄露的平均成本创历史新高

IBM Security 发布了年度数据泄露成本报告&#xff0c;显示数据泄露的全球平均成本在 2023 年达到 445 万美元&#xff0c;创下该报告的历史新高&#xff0c;并且比过去 3 年增加了 15%。 检测和升级成本在同一时间段内跃升了 42%&#xff0c;占违规成本的最高部分&#xff0c…

促科技创新:高德数据优化篇之OceanBase最佳实践

本文作者&#xff1a; 振飞&#xff08;高德地图总裁&#xff09; 炳蔚&#xff08;高德技术服务平台负责人&#xff09; 福辰&#xff08;高德服务端架构师&#xff09; 背景 高德成立于2002年&#xff0c;是中国领先的移动数字地图、导航及实时交通信息服务提供商&#xff0c…

Ctfshow web入门 JWT篇 web345-web350 详细题解 全

CTFshow JWT web345 先看题目&#xff0c;提示admin。 抓个包看看看。 好吧我不装了&#xff0c;其实我知道是JWT。直接开做。 在jwt.io转换后&#xff0c;发现不存在第三部分的签证&#xff0c;也就不需要知道密钥。 全称是JSON Web Token。 通俗地说&#xff0c;JWT的本质…

通讯协议030——全网独有的OPC HDA知识一之基本概念(一)

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui)。 目前&#xff0c;大多数历史数据系统都使用自己的专有接口对外提供数据服务&#xff0c;不能与任何其他系统互操作。OPC HDA规范旨在提供历史数据访问的标准接口&#xff0c;促进用…

2023年信息系统项目管理师-学习计划安排

1. 关注信管网&#xff1a; 信管网 - 考试专业网站&#xff01; (cnitpm.com) 2023年下半年信息系统项目管理师报名时间将于8月14日开始&#xff0c;各地报名时间不同&#xff0c;请考生注意查看当地报名时间&#xff0c;但报名官网入口是统一的&#xff0c;均在中国计算机技术…

如何评估DC电源模块的效率

BOSHIDA 如何评估DC电源模块的效率 BOSHIDA DC电源模块的效率是指输入电功率与输出电功率的比率&#xff0c;通常以百分比的形式表示。因为电源模块的效率和整个系统的运行时间、负载变化等因素有关&#xff0c;因此需要进行多种测试和评估来确定其真实效率。 以下是一些评估D…

500余名师生齐聚线下!智能汽车竞赛百度创意组东西部赛区圆满结束

“全国大学生智能汽车竞赛”是教育部倡导的大学生科技A类竞赛&#xff0c;中国高等教育学会将其列为含金量最高的大学生竞赛之一&#xff0c;为《全国普通高校大学生竞赛排行榜》榜单内赛事。飞桨共承办了百度完全模型组和百度智慧交通组两大赛道。其中&#xff0c;创意组赛事共…

springboot+vue学生宿舍寝室管理系统的设计与开发fyaa5--论文

金桂圆寝室管理系统主要包括管理员、宿管和喾三大部分。 管理员主要功能为&#xff1a;个人中心、学生管理、宿管管理、楼宇信息管理、宿舍信息管理、住宿信息管理、宿舍更换管理、退宿信息管理等功能。 宿管主要功能为&#xff1a;个人中心、宿舍信息管理、住宿信息管理、宿舍…

java+springboot+mysql企业邮件管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的企业邮件管理系统&#xff0c;系统包含超级管理员、管理员、员工角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;员工管理&#xff1b;反馈管理&#xff1b;系统公告&#xff1b;个人…