FreeRTOS(二值信号量)

news2025/1/12 8:46:09

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

目录

一、信号量的概念

1、信号量的基本概念

2、信号量的分类

二、二值信号量的定义与应用

1、二值信号量的定义

2、二值信号量的应用

三、二值信号量的运作机制

1、FreeRTOS任务间二值信号量的实现

2、FreeRTOS中断方式二值信号量的实现

四、二值信号量的常用API函数

1、使用二值信号量典型流程与API

2、二值信号量创建与删除

3、任务与中断中二值信号量释放

五、二值信号量编程

 1、信号量创建

2、避免串口误同步

3、任务释放信号量

 4、任务获取信号量

5、中断释放信号量

6、任务中获取中断释放的信号量


一、信号量的概念

1、信号量的基本概念

消息队列是实现任务与任务或任务与中断间通信的数据结构,可类比裸机编程中的数组

信号量是实现任务与任务或任务与中断间通信的机制,可以类比裸机编程中的标志位

信号量(semaphore)可以实现任务与任务或任务与中断间的同步功能(二值信号量)资源管理(计数信号量)临界资源的互斥访问(互斥信号量)

信号量是一个非负正数,二值信号量与互斥信号量取值范围为0-1计数信号量取值范围是0-N(N>1)

0:信号量为空,所有试图获取它的任务都将处于阻塞状态,直到超时退出或其他任务释放信号量

正数:表示有一个或多个信号量供获取

2、信号量的分类

二值信号量(同步应用)

计数信号量(资源管理)

互斥信号量(互斥访问)

递归互斥信号量(简要了解即可)

二、二值信号量的定义与应用

1、二值信号量的定义

当信号量被获取了,信号量值变为0;当信号量被释放了,信号量值变为1。 把这种取值只有0与1两种状态的信号量称之为二值信号量。创建二值信号量时,系统会为创建的二值信号量分配内存

二值信号量是一种长度为1,消息大小为0的特殊消息队列。  因为这个队列只有空或满两种状态,而且消息大小为0,因此在运用时,只需要知道队列中是否有消息即可,而无需关注消息是什么。

2、二值信号量的应用

在嵌入式操作系统中,二值信号量是任务与任务或任务与中断间同步的重要手段。二值信号量也可以用于临界资源的访问,但不建议,因为存在任务优先级翻转问题.

任务与任务中同步的应用场景:

假设有一个温湿度传感器,每1s采集一次数据,那么让它在液晶屏中显示数据,这个周期也是1s,如果液晶屏刷新的周期是100ms,那么此时的温湿度数据还没更新,液晶屏根本无须刷新,只需要在1s后温湿度数据更新时刷新即可,否则CPU就是白白做了多次的无效数据更新操作,造成 CPU 资源浪费。如果液晶屏刷新的周期是 10s,那么温湿度的数据都变化了10次,液晶屏才来更新数据,那么这个产品测得的结果就是不准确的,所以还是需要同步协调工作,在温湿度采集完毕之后进行液晶屏数据的刷新,这样得到的结果才是最准确的,并且不会浪费 CPU 的资源。

任务与中断中同步的应用场景:

在串口接收中,我们不知道什么时候有数据发送过来,但如果设置一个任务专门时刻查询是否有数据到来,将会浪费CPU资源,所以在这种情况下使用二值信号量是很好的办法:当没有数据到来时,任务进入阻塞态,不参与任务的调度;等到数据到来了,释放一个二值信号量,任务就立即从阻塞态中解除,进入就绪态,然后在运行时处理数据,这样系统的资源就会得到很好的利用。

三、二值信号量的运作机制

1、FreeRTOS任务间二值信号量的实现

任务间二值信号量的实现是指各个任务之间使用信号量实现任务的同步功能。

运行条件:

创建 2 个任务 Task1 和 Task2。 

创建二值信号量默认的初始值是 0,也就是没有可用资源。

运行过程描述如下:

任务 Task1 运行过程中调用函数 xSemaphoreTake 获取信号量资源,但是由于创建二值信号的初始值是 0,没有信号量可以用,任务 Task1 将由运行态转到阻塞状态。运行的过程中,任务 Task2 通过函数 xSemaphoreGive 释放信号量,任务 Task1 由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态,实现Task1与Task2的同步功能。

2、FreeRTOS中断方式二值信号量的实现

运行条件:

创建 1 个任务 Task1 和一个串口接收中断。 

二值信号量的初始值为 0,串口中断调用函数 xSemaphoreGiveFromISR 释放信号量,任务 Task1调用函数 xSemaphoreTake 获取信号量资源。

运行过程描述如下:

任务 Task1 运行过程中调用函数 xSemaphoreTake,由于信号量的初始值是 0,没有信号量资源可用,任务 Task1 由运行态进入到阻塞态。

Task1 阻塞的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中调用函数xSemaphoreGiveFromISR 释放信号量资源,信号量数值加 1,此时信号量计数值为 1,任务 Task1由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态,任务 Task1 获得信号量后,信号量数值减 1,此时信号量计数值又变成了 0。 

再次循环执行时,任务 Task1 调用函数 xSemaphoreTake 由于没有资源可用再次进入到挂起态,等待串口释放二值信号量资源,如此往复循环。

实际应用中,中断方式的消息机制要注意以下四个问题:

 中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应

 实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效地保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行

 中断服务程序中一定要调用专用于二值信号量设置函数,即以 FromISR 结尾的函数

 如果 FreeRTOS 工程的中断函数中调用了 FreeRTOS 的二值信号量的 API 函数,退出的时候要检测是否有高优先级任务就绪,如果有就绪的,需要在退出中断后进行任务切换

四、二值信号量的常用API函数

1、使用二值信号量典型流程与API

> 创建二值信号量   xSemaphoreCreateBinary()

> 释放二值信号量    xSemaphoreGive() 与 xSemaphoreGiveFromISR() 

> 获取二值信号量    xSemaphoreTake()

> 删除二值信号量    vSemaphoreDelete()

2、二值信号量创建与删除

二值信号量控制块(句柄):二值信号量的句柄为消息队列的句柄,因为二值信号量是一种长度为1,消息大小为0的特殊消息队列

二值信号量创建

函数原型:SemaphoreHandle_t xSemaphoreCreateBinary(void)

函数描述:函数 xSemaphoreCreateBinary 用于创建二值信号量。 

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

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

图片

 应用举例

图片

 二值信号量删除

函数原型:void vSemaphoreDelete(void)

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

3、任务与中断中二值信号量释放

任务中二值信号量释放

函数原型:xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */

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

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

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

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

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

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

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

中断中二值信号量释放

函数原型: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 ()创建的信号量。

五、二值信号量编程

 1、信号量创建

2、避免串口误同步

STM32Cube生成的FreeRTos代码创建二值信号量时,默认为1,此处释放避免串口误同步

  xSemaphoreTake(myBinarySem01Handle,0);//STM32CubeMX生成的FreeRTos代码创建二值信号量时,默认为1,此处释放,避免串口误同步
  xSemaphoreTake(myBinarySemISRHandle,0);//STM32CubeMX生成的FreeRTos代码创建二值信号量时,默认为1,此处释放,避免串口误同步

3、任务释放信号量

void ReleaseSem_Task(void const * argument)
{
  /* USER CODE BEGIN ReleaseSem_Task */
	BaseType_t xResult;
	uint16_t GiveCnt=0;   //释放计数
	char buff[50];
  /* Infinite loop */
  for(;;)
  {
	HAL_UART_Transmit(&huart2, (uint8_t *)"发送同步信号!!! \r\n",18, HAL_MAX_DELAY);
	xResult=xSemaphoreGive(myBinarySem01Handle);
	if(xResult==pdTRUE)
	{
		sprintf(buff,"成功发送二值信号量同步信号,次数 = %u \r\n",++GiveCnt);
		HAL_UART_Transmit(&huart2, (uint8_t *)buff, strlen(buff), HAL_MAX_DELAY);
	}
	else
	{
		HAL_UART_Transmit(&huart2, (uint8_t *)"发送同步信号失败 \r\n\r\n", 17, HAL_MAX_DELAY);
	}
    osDelay(1000);
  }
  /* USER CODE END ReleaseSem_Task */
}

 4、任务获取信号量

void BinarySem_Syn_Task(void const * argument)
{
  /* USER CODE BEGIN BinarySem_Syn_Task */
	BaseType_t xResult;
	uint16_t TakeCnt=0;   //获取计数
	char buff[50];
  /* Infinite loop */
  for(;;)
  {
	HAL_UART_Transmit(&huart2,(uint8_t *)"等待同步信号,无限等待 \r\n", 25, HAL_MAX_DELAY);
	xResult=xSemaphoreTake(myBinarySem01Handle,portMAX_DELAY);
	if(xResult==pdTRUE)
	{
		sprintf(buff,"成功接收到二值信号量同步信号,次数 = %u \r\n\r\n",++TakeCnt);
		HAL_UART_Transmit(&huart2, (uint8_t *)buff, strlen(buff), HAL_MAX_DELAY);
	}
  }
  /* USER CODE END BinarySem_Syn_Task */
}

5、中断释放信号量

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	BaseType_t xHigherPriorityTaskWoken =pdTRUE;
	if(huart->Instance==huart2.Instance)
	{
		xSemaphoreGiveFromISR(myBinarySemISRHandle,&xHigherPriorityTaskWoken);
		//如果有高优先级任务就绪,执行一次任务切换
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
}

6、任务中获取中断释放的信号量

void BinarySemSyneISR_Task(void const * argument)
{
  /* USER CODE BEGIN BinarySemSyneISR_Task */
	BaseType_t xResult;
	uint16_t TakeCnt=0;   //获取计数
	char buff[50];
	char rxBuff[10];
  /* Infinite loop */
	for (;;)
	{
		HAL_UART_Receive_IT(&huart2, (uint8_t*) rxBuff, strlen(rxBuff));

		HAL_UART_Transmit(&huart2, (uint8_t*) "等待串口中断同步信号,无限等待 \r\n", 21,
		HAL_MAX_DELAY);
		xResult = xSemaphoreTake(myBinarySemISRHandle, portMAX_DELAY);
		if (xResult == pdTRUE)
		{
			sprintf(buff, "成功接收到二值信号量同步信号,次数 = %u \r\n\r\n", ++TakeCnt);
			HAL_UART_Transmit(&huart2, (uint8_t*) buff, strlen(buff),HAL_MAX_DELAY);
			sprintf(buff, "成功接收到串口数据: %s \r\n\r\n", rxBuff);
			HAL_UART_Transmit(&huart2, (uint8_t*) buff, strlen(buff),HAL_MAX_DELAY);
		}
	}
  /* USER CODE END BinarySemSyneISR_Task */
}

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

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

相关文章

应用冷启bindservice耗时

背景:sdk初始化的时候耗时过长,而sdk,init方法中只有一个bindservice及一些变量的初始化,却好事100ms 查看trace发现binderservice耗时只占init耗时的一小部分,但是init逻辑并没有其他代码。 这里servicebind返回快的另一原因是se…

【Java】线程数据共享和安全 -ThreadLocal

🎄欢迎来到边境矢梦的csdn博文🎄 🎄本文主要梳理线程数据共享和安全 -ThreadLocal🎄 🌈我是边境矢梦,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下&#x1faf…

CAN-FD总线通信应用理解

传统的车载CAN总线最高支持500 kbit/s的传输速率,每帧只能承载8 bytes的数据,由于传输速率和数据长度的限制,在自动驾驶和智能网联对网络通信的高要求背景下,使用传统 CAN 通信势必会导致总线负载率过高从而导致网络拥堵,传统CAN总线通信的瓶颈逐渐凸显。 2011年,为满足带…

并发相关面试题

巩固基础,砥砺前行 。 只有不断重复,才能做到超越自己。 能坚持把简单的事情做到极致,也是不容易的。 如何理解volatile关键字 在并发领域中,存在三大特性:原子性、有序性、可见性。volatile关键字用来修饰对象的属性…

HCIP学习--BGP2

目录 前置内容 BGP宣告问题 BGP自动汇总问题 BGP 的认证 BGP的聚合(汇总) 标准的BGP聚合配置 非标准的BGP聚合 路由传递干涉策略 抑制列表 Route-map 分发列表 前缀列表 BGP在MA网络中下一跳问题-ICMP重定向 查看与某个邻居收发的路由 配置 有条件打破IBGP水平…

MyBatis框架:创建Mapper接口和映射文件,实现基本增删改查

目录 1、Mapper接口和映射文件关系 2、Mapper接口和映射文件的命名规则 2.1 Mapper接口的命名规则 2.2 映射文件的命名规则 3、Mapper接口和映射文件的创建及增删改查的实现 3.1 Mapper接口和映射文件的创建 3.2 增删改查的实现 3.2.1表结构 3.2.2 创建表User对应的实体…

excel 之 VBA

1、excel和VBA 高效办公,把重复性的工作写成VBA代码(VB代码的衍生物,语法和VBA相同)。 首先打开开发工具模式,如果没有选显卡,需要手动打开 打开程序编辑界面 快捷键 altF11一般操作 程序调试&#xf…

详解JAVA远程debug

目录 1.什么是远程debug? 2.远程debug普通JAVA程序 环境 测试程序 程序启动指令 编译器配置 3.远程debug JAVA Web程序 4.远程debug spring boot程序 1.什么是远程debug? 远程debug,也就是可以在本地debug远端部署的程序&#xff0c…

深入浅出:MyBatis的使用方法及最佳实践

这里写目录标题 添加MyBatis框架⽀持配置连接字符串和MyBatis配置连接字符串配置 MyBatis 中的 XML 路径 添加业务代码创建数据库和表添加用户实体类添加 mapper 接⼝添加 UserMapper.xml添加 Service层添加 Controller层 增删改操作增加操作删除操作修改操作 添加MyBatis框架⽀…

vue3+ts+vite全局配置Element-Plus主题色

概述 我找了很多博客,想全局配置Elmenet-Plus组件主题色,但都没有效果。所以有了这篇博客,希望能对你有所帮助!!! 文章目录 概述一、先看效果二、创建全局颜色文件2.1 /src/styles 下新建 element-plus.sc…

王道机组难题分析

第四章 指令系统 大端方式:就是高地址存放高位, LSB的意思是:全称为Least Significant Bit,在二进制数中意为最低有效位 MSB的意思是:全称为Most Significant Bit,在二进制数中属于最高有效位 操作数可以理…

设备工单管理系统如何实现工单流程自动化?

设备工单管理系统属于工单系统的一种,基于其丰富的功能,它可以同时处理不同的多组流程,旨在有效处理发起人提交的事情,指派相应人员完成服务请求和记录全流程。该系统主要面向后勤管理、设备维护、物业管理、酒店民宿等服务行业设…

微服务07-分布式缓存

前提: 单机的Redis存在四大问题: 解决办法:基于Redis集群解决单机Redis存在的问题 1、Redis持久化 Redis有两种持久化方案: RDB持久化AOF持久化1.1 RDB持久化 RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所…

redis的基础命令01

1、操作库的指令 1、清除当前库---flushdb 2、清除所有库---flushAll 2、操作key的指令 最常用的指令get、set 1)set key value 2)get key 基础指令 1、del 删除单个:del key 、批量删除:del key1 key2 key3 2、exists 判断key是否…

jpa查询返回自定义对象、返回指定VO、POJO

jpa查询返回自定义对象、返回指定VO、POJO jpa查询返回自定义对象、返回指定VO、POJO,JPA查询前会做大量处理,还有线程通知的操作。若并发大,处理性能直线下降。但是jpa就因为做了大量处理,对多数据库兼容极好,操作方…

QT之UDP通信

QT之UDP通信 UDP不分客户端口服务器,只需要使用一个类QUdpSocket QT += core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = udp TEMPLATE = app# The following define makes your compiler emit warnings if you use # any feature of Qt …

通义大模型:打造更智能、更灵活的自然语言处理技术

大家好,今天我想向大家介绍一款备受瞩目的自然语言处理技术——通义大模型。作为一种基于深度学习的人工智能技术,通义大模型能够模拟人类的思维方式,实现更智能、更灵活的自然语言处理,为我们的生活和工作带来了极大的便利。 在…

Java解决四大查找(一)

Java解决四大查找 一.线性查找1.1 题目1.2 思路分析1.3 代码演示 二.二分查找(双指针法)2.1 题目2.2 思路分析(图解加文字)2.3 代码演示 一.线性查找 1.1 题目 在数组{1,8,1024,521,1889}中查找数字8,如果有&#xff…

数学建模(二)线性规划

课程推荐:6 线性规划模型基本原理与编程实现_哔哩哔哩_bilibili 在人们的生产实践中,经常会遇到如何利用现有资源来安排生产,以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支:数学规划。而线性规划(Linear Program…

基于 FPGA 的电机控制

FPGA 非常适合精密电机控制,在这个项目中,我们将创建一个简单的电机控制程序,在此基础上可以构建更复杂的应用。 需要的硬件 Digilent Pmod HB3 介绍 我们可以用一个简单的 8 位微控制器来控制电机,输出一个简单的脉宽调制波形。然…