FreeRTOS(计数信号量)

news/2025/1/11 20:50:34/

 资料来源于硬件家园:资料汇总 - 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;//�?测KEY0if(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); //必须用阻塞延�?}}}//�?测KEY1if(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);elseHAL_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.ppmy.cn/news/1028199.html

相关文章

分布式应用:Zabbix监控Tomcat

目录 一、理论 1.Zabbix监控Tomcat 二、实验 1.Zabbix监控Tomcat 三、问题 1.获取软件包失败 2.tomcat 配置 JMX remote monitor不生效 3.Zabbix客户端日志报错 一、理论 1.Zabbix监控Tomcat &#xff08;1&#xff09;环境 zabbix服务端&#xff1a;192.168.204.214 …

1015 Reversible Primes

个人学习记录&#xff0c;代码难免不尽人意。 reversible prime in any number system is a prime whose “reverse” in that number system is also a prime. For example in the decimal system 73 is a reversible prime because its reverse 37 is also a prime. Now giv…

什么是http协议

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;即超文本传输协议&#xff0c;是一种用于在网络上传输超文本数据的协议。它是客户端&#xff08;如浏览器&#xff09;和服务器之间进行通信的基础&#xff0c;用于请求和响应在Web上的资源。 HTTP 协议的工作原理如下…

SSL证书DV和OV的区别?

SSL证书是在互联网通信中保护数据传输安全的一种加密工具。它能够确保客户端和服务器之间的通信得以加密&#xff0c;防止第三方窃听或篡改信息。在选择SSL证书时&#xff0c;常见的有DV证书和OV证书&#xff0c;它们在验证标准和信任级别上有所不同。那么SSL证书DV和OV的有哪些…

MySQL~事务的四大特性和隔离级别

事务的四大特性 1.原子性&#xff1a;一个事务&#xff08;transaction&#xff09;中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成。事务在执行过程中发生错误&#xff0c;会被回滚&#xff08;Rollback&#xff09;到事务开始前的状态&#xff0c;就像这个…

室温超导是什么?有哪些应用场景?

目录 一、应用场景&#xff1a;二、案例分析&#xff1a; 室温超导是指在室温下&#xff08;即约 20C 至 30C&#xff09;实现超导现象的材料。超导是指某些材料在低温下电阻为零的物理现象&#xff0c;室温超导材料是超导材料的一种。室温超导现象的发现和研究是超导领域的一个…

Linux(Web与html)

域名 DNS与域名&#xff1a; 网络是基于tcp/ip协议进行通信和连接的 tcp/ip协议是五层协议&#xff1a;应用层–传输层—网络层----数据链路层----物理层每一台主机都有一个唯一的地址标识&#xff08;固定的ip地址&#xff0c;用于区分用户和计算机。 ip地址&#xff1a;由…

Spring boot中的线程池-ThreadPoolTaskExecutor

一、jdk的阻塞队列&#xff1a; 二、Spring boot工程的有哪些阻塞队列呢&#xff1f; 1、默认注入的ThreadPoolTaskExecutor 视频解说&#xff1a; 线程池篇-springboot项目中的service层里简单注入ThreadPoolTaskExecutor并且使用_哔哩哔哩_bilibili 程序代码&#xff1a;…