FreeRTOS(计数信号量)

 资料来源于硬件家园:资料汇总 - 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.mzph.cn/news/34664.shtml

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

相关文章

双指针算法

文章目录 双指针算法leetcode题目 双指针算法 双指针算法可以实现对于时间复杂度降一维度&#xff0c;使得O(n2)的算法时间复杂度变为O(n) 指针类型 对撞指针快慢指针 对撞指针 一般是用于顺序结构中的&#xff0c;也可以称为左右指针&#xff0c;从两端向中间移动&#xff0c…

【工作中问题解决实践 十一】Kafka消费者消费堆积且频繁rebalance

最近有点不走运&#xff0c;老是遇到基础服务的问题&#xff0c;还是记着点儿解决方法&#xff0c;以后再遇到快速解决吧&#xff0c;今天遇到这个问题倒不算紧急&#xff0c;但也能通过这个问题熟悉一下Kafka的配置。 问题背景 正在开会的时候突然收到一连串的报警&#xff…

上篇文章viewerjs(npm包补丁)的错误更正。(npm补丁本地没问题,流水线打包要没效果,要么报错)

1、上篇文档的包补丁在本地使用没有问题&#xff0c;并且同事拉代码后也会自动同步npm_modules&#xff0c;也没有问题 2、问题出现在打包上&#xff0c;我这边的项目是用teambition上的飞流设置的流水线来进行打包的&#xff0c;里面用到了两个指令 yarn install npm run bu…

IDEA关闭项目,但是后台程序没有关闭进程(解决方案)

最近遇到一个很奇怪的问题&#xff0c;idea关闭项目后&#xff0c;系统进程没有杀死进程&#xff0c;再次执行的时候会提示端口占用&#xff0c;并提示Process exited with an error: 1 (Exit value: 1) 错误原因&#xff1a;应用程序关闭后&#xff0c;进程不能同步关闭 解决方…

使用ffmpeg将m4a及wav等文件转换为MP3格式

要使用ffmpeg将m4a及wav等文件转换为MP3格式&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装 ffmpeg 确保您已经安装了ffmpeg软件。如果没有安装&#xff0c;请访问ffmpeg的官方网站https://ffmpeg.org/ 并按照说明进行安装。 Win10 / Win11 可以通过 winget 命令…

角角の Qt学习笔记(一)

目录 一、解决在创建新项目时遇到的几个问题 二、信号和槽&#xff08;非自定义&#xff09; 三、调用 UI 中的元素&#xff08;比如按钮&#xff09; 一、解决在创建新项目时遇到的几个问题 在新建项目时&#xff0c;我选择的构建系统为CMake。然后勾选了Generate form&…

fabric.js里toDataURL后,画布内容展示不全?

复现场景&#xff1a; 用fabric生成画布后&#xff0c;转成图片&#xff0c;然后直接在浏览器里打开&#xff0c;画布展示内容缺失 画布原图&#xff1a; toDataURL后链接在浏览器打开&#xff1a; 原因解析&#xff1a; base64链接太长&#xff0c;输入浏览器链接被截断&…

MySQL 数据库文件的导入导出

目录 数据库的导出 导出整个数据库 导出数据库中的数据表 导出数据库结构 导出数据库中表的表结构 导出多个数据库 导出所有数据库 数据库的导入 数据库的导出 mysqldump -h IP地址 -P 端口 -u 用户名 -p 数据库名 > 导出的文件名 用管理员权限打开cmd进入MySQL的bi…

python+tkinter实现图书管理系统(首发)

文章目录 前文运行环境功能图数据操作图书数据管理用户数据管理借书记录管理 功能界面管理员界面首页图书管理用户管理借书记录更改密码 普通用户界面 其他功能数字时间显示加载画面显示输入框提示词界面居中显示借书时间和还书时间记录公告栏数据操作 结尾 前文 本文将用tkin…

springboot异步任务

在Service类声明一个注解Async作为异步方法的标识 package com.qf.sping09test.service;import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service;Service public class AsyncService {//告诉spring这是一个异步的方法Asyncp…

JavaWeb 中对 HTTP 协议的学习

HTTP1 Web概述1.1 Web和JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器 1.3 Web核心 2 HTTP2.1 简介2.2 请求数据格式2.2.1 格式介绍2.2.2 实例演示 2.3 响应数据格式2.3.1 格式介绍2.3.2 响应状态码2.3.…

面试热题(合并两个有序列表)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 合并链表这类型题也是比较经典的题了&#xff0c;因为链表是由指针相互指向而确定位置&#xff0c;所以我们只需要改变某些节点的指针便可以做到对链表进行排序 今天这个方法…

Spring Bean 生命周期的执行流程

问题描述 Spring 生命周期全过程大致分为五个阶段&#xff1a; 1、创建前准备阶段 2、创建实例阶段 3、依赖注入阶段 4、 容器缓存阶段 5、销毁实例阶段 下图是 Spring Bean 生命周期完整流程图&#xff0c;其中对每个阶段的具体操作做了详细介绍&#xff1a; 一、创建前准备阶…

阶梯费用计算(配置化_最小demo)

本文旨在提供一种配置化思路计算阶梯费用&#xff0c;更高级的做法则是通过数据库配置&#xff0c;注册中心等&#xff1b;在表达式上可以采用自定义或者spel表达式等其他方式进行处理&#xff1b;(代码仅展示最小demo,部分不完善地方自行补充) 思路&#xff1a;N个区间对应N个…

Spring Boot 项目应用消息服务器RabbitMQ(简单介绍)

一、背景 本章讲述的是在用户下单环节&#xff0c;消息服务器RabbitMQ 的应用 1.1 消息服务器的应用 在写一个电商项目的小demo&#xff0c;在电商项目中&#xff0c;消息服务器的应用&#xff1a; 1、订单状态通知&#xff1a;当用户下单、支付成功、订单发货、订单完成等…

【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记

运行时类信息&#xff08;RTTI&#xff09; C: ##是拼接 #是替换成字符串 // RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <afxwin.h>#ifdef _DEBUG #define new DEBUG_NEW #endifCWinApp th…

六种不同的CRM系统类型分别有哪些特点?

企业想要管理销售&#xff0c;可以选择CRM系统&#xff1b;企业想要优化业务流程&#xff0c;可以选择CRM系统&#xff1b;企业想要提高收入&#xff0c;可以选择CRM系统。下面来说说&#xff0c;CRM是什么&#xff1f;六种常见CRM系统类型对比。 什么是CRM&#xff1f; CRM是…

优秀的 Modbus 从站(从机、服务端)仿真器、串口调试工具

文章目录 优秀的 Modbus 从站&#xff08;从机、服务端&#xff09;仿真器、串口调试工具主要功能软件截图 优秀的 Modbus 从站&#xff08;从机、服务端&#xff09;仿真器、串口调试工具 官网下载地址&#xff1a;http://www.redisant.cn/mse 主要功能 支持多种Modbus协议…

Java课题笔记~ Spring 集成 MyBatis

Spring 集成 MyBatis 将 MyBatis 与 Spring 进行整合&#xff0c;主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以该整合&#xff0c;只需要将 SqlSessionFactory 的对象生成器SqlSessionFactoryBean 注册在 Spring 容器中&#xff0c;再将其注入给 Dao…

多目标优化算法之樽海鞘算法(MSSA)

樽海鞘算法的主要灵感是樽海鞘在海洋中航行和觅食时的群聚行为。相关文献表示&#xff0c;多目标优化之樽海鞘算法的结果表明&#xff0c;该算法可以逼近帕雷托最优解&#xff0c;收敛性和覆盖率高。 通过给SSA算法配备一个食物来源库来解决第一个问题。该存储库维护了到目前为…