FreeRtos开发之计数信号量

embedded/2024/12/3 8:42:24/

前面介绍过了计数信号量的定义取值只有0与1两种状态的信号量称之为二值信号量
取值大于1的信号量称之为计数信号量
计数信号量的取值也可以为1,但通常大于1,如果取值为1,相当于只有0与1两种状态,用二值信号量即可。
计数信号量应用场景:
计数信号量允许多个任务对其进行操作,但限制了任务的数量。比如有一个停车场,里面只有50个车位,那么只能停50辆车,相当于我们的计数信号量是50。假如一开始停车场的车位还有50个,那么每进去一辆车就要消耗一个停车位,车位的数量就要减1,相应地,我们的计数信号量在使用之后也需要减 1。当停车场停满了50 辆车时,此时的停车位数量为0,再来的车就不能停进去了,否则将没法停车了,也相当于我们的计数信号量为0,后面的任务对这个停车场资源的访问也无法进行。当有车从停车场离开时,车位又空余出来了,那么后面的车就能停进去了。信号量操作也是一样的,当我们释放了这个资源,后面的任务才能对这个资源进行访问。
在这里插入图片描述任务 Task1 运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于0,Task1 将直接获取资源。如果信号量为0,任务 Task1 将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数xSemaphoreGive 释放掉资源。
任务 Task2至TaskN 等N个任务运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于0,Task2至N 将直接获取资源。如果信号量为0,任务 Task2至N将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive释放掉资源。

计数信号量使用流程和常用的API函数

  1. 创建计数信号量 xSemaphoreCreateCounting()
  2. 释放计数信号量 xSemaphoreGive()与xSemaphoreGiveFromlSR()
  3. 获取计数信号量 xSemaphoreTake()
  4. 删除计数信号量 vSemaphoreDelete()
1、计数信号量创建与删除

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

//FreeRtos源码中定义的句柄
typedef QueueHandle_t semaphoreHandle_t,

在程序中定义计数信号量

//定义计数信号量
osSemaphoreId myCountingSem01Handle = NULL;

2、计数信号量创建
函数原型:

Semaphorelandle_t xSemaphoreCreateCounting(UBaseType_t uxllaxCount,/*支持的最大计数值 */UBascTypc_t uxInitialCount);/*初始计数值 */

函数描述:
函数 xSemaphoreCreateCounting 用于创建计数信号量。
第 1个参数是设置此计数信号量支持的最大计数值。
第 2 个参数是设置计数信号量的初始值。这里的初始值设置就是从0到第一个参数设置的最大计数值uxllaxCount
返回值,如果创建成功会返回消息队列的句柄,如果由于 FreeRTOSConfig.h 文件中heap 大小不足,无法为此消息队列提供所需的空间会返回 NULL

应用举例:

myCountingSem01Handle = xSemaphoreCreateCounting(2020)
if(myCountingSem01Handle == NULL)printf("创建计数信号量失败\r\n\r\n");
elseprintf("创建计数信号量成功\r\n\r\n");

3、计数信号量删除
函数原型:

void vSemaphoreDeete(SemaphoreHandle_t xSemaphore); /* 参数为计数信号量句枘 */

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

4、任务中计数信号量释放,当前计数值加加
函数原型:

xSemaphoreGive(SemaphoreHandle_t xSemaphore); /*参数使用计数信号量柄*/

函数描述:
函数 xSemaphoreGive 用于在任务代码中释放信号量。
参数是信号量句柄。
返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。
使用这个函数要注意以下问题:
1.此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xSemaphoreGiveFromISR.
2.使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(),xSemaphoreCreate-Mutex() 或者xSemaphoreCreateCounting()创建了信号量。
3.此函数不支持使用 xSemaphoreCreateRecursiveMutex()创建的信号量。
应用举例:

printf("释放计数信号量,模拟车辆出库,让出停位");
xResult=xSemaphoreGive(myCountingSemlHandle);
if(xResult == pdTRUE)
{printf("释放成功,成功让出停车位,发送同步显示信号\r\n");
}
else
{printf("释放失败,停车位已空\r\n");
}

5、中断中计数信号量释放
函数原型:

xSemaphoreGiveFromISR(Semaphorellandle t xSemaphore,/* 信号量句柄*/
signed BaseType_t *pxHigherPriorityTaskWoken /* 高优先级任务是否被唤醒的状态保存 */)

函数描述:
函数 xSemaphoreGiveFromlSR 用于中断服务程序中释放信号量。
第 1个参数是信号量句柄。
第 2 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 PdTRUE,说明有高优先级任务要执行,否则没有。返回值,如果信号量释放成功返回 pdTRUE,否则返回 errQUEUE FULL
使用这个函数要注意以下问题:
1.此函数是基于消息队列函数 xQueueGiveFromISR 实现的:#define xSemaphore-GiveFromlSR( xSemaphore, pxHigherPriorityTaskWoken ) \xQueueGiveFromlSR( (QueueHandle t )( xSemaphore ),( pxHigherPriorityTaskWoken ))
2.此函数是用于中断服务程序中调用的,故不可以任务代码中调用此函数,任务代码中中使用的是xSemaphoreGive。
3.使用此函数前,一定要保证用函数xSemaphoreCreateBinary()或者xSemaphoreCreateCounting()创建了信号量。
4.此函数不支持使用 xSemaphoreCreateMutex()创建的信号量.

6、计数信号量获取,计数值减减,计数值为0的话就获取不到
函数原型:

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,那么此函数会永久等待直到信号量可用。

应用举例:

printf("获取计数信号量,拟车辆入库,申请停车位");
xResult=xSemaphoreTake(myCountingSem01Handle,0);  
//返回值为TRUE说明计数值不为0,但此时计数值会减1,为FLASE说明计数值为0,此时其他任务或中断还没有释放计数信号量,需要根据设置的时间进行等待
if(xResult == pdTRUE)
{printf("获取成功,成功申请停车位,发送同步显示信号\r\n");xSemaphoreGive(myBinarysem01Handle);myCountingSem_ucMessagesWaiting--;
}
elseprintf("获取失败,停车位已满\r\n");

计数信号量使用的案例

假设现在停车场有20个停车位

osSemaphoreId myCountingSem01Handle = NULL;
myCountingSem01Handle = xSemaphoreCreateCounting(2020)

这里就是设置第一个参数最大值为20,初始值为20
当有车要进来的时候,就会获取信号量(计数值减一),当有车出去的时候就会释放信号量(信号量加一)
这里为了方便查看还有多少个空位置,见了一个初始值为20的myCountingSem_ucMessagesWaiting变量,这个变量的值会在显示屏显示出来,当有车要进来的时候计数信号量获取会减减,此时会将这个变量减1,相反车出去时会加一,但是有一个问题,这个显示在另外一个任务中实现的,所以就需要使用二值信号量进行同步保护
按键检测任务

extern int myCountingSem_ucMessagesWaiting;  //外部引用
//KEY3处理
if(KEY.KeyCode == KEY3)
{printf("获取计数信号量,模拟车辆入库,申请停车位");xResult =xSemaphoreTake(myCountingSem01Handle,0);if(xResult == pdTRUE){printf("获取成功,成功申请停车位,发送同步显示信号\r\n");xSemaphoreGive(myBinarySem01Handle);  //使用二值信号量保护当前空余位置数量myCountingSem_ucMessagesWaiting--;}elseprintf("获取失败,停车位已满\r\n");
}
//KEY4处理
if(KEY.KeyCode == KEY4)
{printf("释放计数信号量,模拟车辆出库,让出停车位");xResult=xSemaphoreGive(myCountingSem01Handle);if(xResult == pdTRUE){printf("释放成功,成功让出停车位,发送同步显示信号\r\n");xSemaphoreGive(myBinarysem1Handle);myCountingSem_ucMessagesWaiting++;}elseprintf("释放失败,停车位已空\r\n");
}
int myCountingSem_ucMessagesWaiting;
void play()
{while(1){BaseType_t xResult;//获取二值信号量,成功会让二值信号量变为0,成功说明获取到了,如果为0,就需要等待其他任务或者中断将二值信号量释放xResult = xSemaphoreTake(myBinarySem01Handle,portMAX DELAY);  if(xResult == pdTRUE){screen_play(myCountingSem_ucMessagesWaiting);}}
}

http://www.ppmy.cn/embedded/142555.html

相关文章

JavaEE-线程池专题

文章目录 线程池存在的逻辑线程池的继承逻辑结构线程池策略模拟示意图集线程池的构造方法(参数含义)corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryRejectedExecutionHandler线程池完整测试代码 线程池的高度封装类Executors简介测试代码 线程池存在的逻…

字符串p型编码

字符串p型编码 C 语言实现C 实现Java 实现Python 实现 💐The Begin💐点点关注,收藏不迷路💐 给定一个完全由数字字符(‘0’,‘1’,‘2’,…,‘9’)构成的字符串str,请写出str的p型编码串。例如&…

什么是内存对齐?为什么需要内存对齐?

参考【C/C】内存对齐(超详细,看这一篇就够了)_c内存对齐-CSDN博客 1)什么是内存对齐? 内存对齐是一种将数据在内存中的存储位置按照特定规则进行排列的方式。通常,数据会被存储在内存地址是其大小的整数倍的…

第六届国际科技创新学术交流会暨管理科学信息化与经济创新发展(MSIEID 2024)

重要信息 大会官网:msieid2024.iaecst.org (点击了解大会,参会等内容) 大会时间:2024年12月6-8日 大会地点:中国-广州 大会简介 随着全球化和信息化的不断深入,管理科学、信息化和经济发展…

windows下kafka初体验简易demo

这里提供了windows下的java1.8和kafka3.9.0版本汇总,可直接免费下载 【免费】java1.8kafka2.13版本汇总资源-CSDN文库 解压后可以得到一个文件夹 资料汇总内有一个kafka文件资料包.tgz,解压后可得到下述文件夹kafka_2.13-3.9.0,资料汇总内还…

LabVIEW实现串口调试助手

目录 1、串口通信原理 2、硬件环境部署 3、串口通信函数 4、程序架构 5、前面板设计 6、程序框图设计 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联…

C# 中的设计模式:构建灵活且可维护的应用程序

C#中的设计模式(Design Patterns)。设计模式是软件开发中常见问题的通用解决方案,它们提供了一种标准化的方法来解决特定的设计问题。通过使用设计模式,可以提高代码的可维护性、可扩展性和复用性。以下是一篇关于C#中常用设计模式…

最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 输入:nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff…