FreeRTOS 实时操作系统第十二讲 - 计数信号量

一、信号量的概念

1、信号量的基本概念

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

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

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

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

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

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

2、信号量的分类

  1. 二值信号量 (重点讲解同步应用)
  2. 技术信号量 (重点讲解资源管理)
  3. 互斥信号量 (重点讲解互斥访问)
  4. 递归互斥信号量 (简要了解即可)

二、计数信号量的定义与应用

1、计数信号量的定义

  取值只有 0 与 1 两种状态的信号量称之为二值信号量
  取值大于 1 的信号量称之为计数信号量
  Note:计数信号量的取值也可以为 1,但通常大于 1,如果取值为 1,相当于只有 0 与 1 两种状态,用二值信号量即可。
  创建计数信号量时,系统会为创建的计数信号量分配内存,计数信号量创建完成后的示意图如下:

  从上图可以看出,计数信号量是一种长度大于 1,消息大小为 0 的特殊消息队列。

  因为这个队列的消息大小为 0,因此在运用时,只需要知道队列中是否有消息即可,而无需关注消息是什么。

2、计数信号量的应用

  在嵌入式操作系统中,计数信号量是资源管理的重要手段,主要用于任务与任务间。

应用场景:

  计数信号量允许多个任务对其进行操作,但限制了任务的数量。比如有一个停车场,里面只有 50 个车位,那么只能停 50 辆车,相当于我们的信号量有 50 个。假如一开始停车场的车位还有 50 个,那么每进去一辆车就要消耗一个停车位,车位的数量就要减 1,相应地,我们的信号量在使用之后也需要减 1。当停车场停满了 50 辆车时,此时的停车位数量为 0,再来的车就不能停进去了,否则将没法停车了,也相当于我们的信号量为 0,后面的任务对这个停车场资源的访问也无法进行。当有车从停车场离开时,车位又空余出来了,那么后面的车就能停进去了。信号量操作也是一样的,当我们释放了这个资源,后面的任务才能对这个资源进行访问。

三、计数信号量的运作机制

FreeRTOS 任务间计数信号量的实现

  任务间信号量的实现是指各个任务之间使用信号量实现任务的同步或者资源共享功能。下面我们通过如下的框图来说明一下 FreeRTOS 计数信号量的实现,让大家有一个形象的认识。


运行条件:

  1. 创建 任务 Task1 和 Task2 至 N。
  2. 创建计数信号量可用资源为 N。

运行过程描述如下:

  • 任务 Task1 运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于 0,Task1 将直接获取资源。如果信号量为 0,任务 Task1 将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。

  • 任务 Task2 至 N 运行过程中调用函数 xSemaphoreTake 获取信号量资源,如果信号量大于 0,Task2 至 N 将直接获取资源。如果信号量为 0,任务 Task2 至 N 将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 xSemaphoreGive 释放掉资源。

上面就是一个简单的 FreeRTOS 任务间计数信号量的使用过程。

四、计数信号量常用的 API 函数

1、使用计数信号量的典型流程如下:

  1. 创建计数信号量
  2. 释放计数信号量
  3. 获取计数信号量
  4. 删除计数信号量

2、常用 API 函数如下:

  1. xSemaphoreCreateCounting()
  2. xSemaphoreGive() 与 xSemaphoreGiveFromISR()
  3. xSemaphoreTake()
  4. vSemaphoreDelete()

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

二值信号量控制块 (句柄)

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

计数信号量创建

函数原型:

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, /* 支持的最大计数值 */                             UBaseType_t uxInitialCount); /* 初始计数值 */

函数描述:
  函数 xSemaphoreCreateCounting 用于创建计数信号量。

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

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

应用举例:

计数信号量删除

函数原型:

void vSemaphoreDelete(void)

函数描述:

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

4、任务中计数信号量释放

函数原型:

xSemaphoreGive(SemaphoreHandle_t xSemaphore); /* 信号量句柄 */

函数描述:

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

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

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

  1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是 xSemaphoreGiveFromISR。
  2. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting() 创建了信号量。
  3. 此函数不支持使用 xSemaphoreCreateRecursiveMutex() 创建的信号量。

应用举例:

5、中断中计数信号量释放

函数原型:

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 () 创建的信号量。

6、计数信号量获取

函数原型:

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

应用举例:

五、技术信号量的应用编程

视频讲解

串口输出信息:

STM32cubeMX 配置:



代码:


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

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

相关文章

maven、springboot项目编译打包本地jar、第三方jar包

0. 引言 一般我们在maven项目中都是通过引入pom坐标的形式来引入第三方jar包,但某些场景下,第三方是直接提供的jar包文件,这就需要我们从本地引入第三方包并进行打包。所以我们今天来看下如何进行本地引入第三方包操作 1. 步骤 1、在项目下…

GNSS位移监测站对尾矿库坝体表面位移进行自动化监测

表面位移监测:通过GNSS位移监测站对尾矿库坝体表面位移进行自动化监测,掌握尾矿坝整体表面位置的变化及其变化速率(包括平面位移和垂直沉降),确定尾矿坝坝体整体位移变形的情况,是确定尾矿库安全性的重要指…

基于Java+SpringBoot+Vue志愿者服务平台设计和实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行交流合作✌ 主要内容:SpringBoot、Vue、SSM、HLM…

[技术杂谈]使用VLC将视频转成一个可循环rtsp流

通过vlc播放器,将一个视频转成rtsp流,搭建一个rtsp服务器。rtsp客户端可访问这个视频的rtsp流。 1. 打开vlc播放器,使用的版本如下 2. 菜单:媒体 ---> 流 3. 添加视频文件,点击添加一个mp4 文件 4. 选择串流&…

Servlet 3.0的异步处理

1、传统Servlet处理 Web容器会为每个请求分配一个线程,默认情况下,响应完成前,该线程占用的资源都不会被释放。若有些请求需要长时间(例如长处理时间运算、等待某个资源),就会长时间占用线程所需资源,若这类请求很多&…

Spring中的数据校验

文章目录 引言摘要正文基于 ValidationUtils的简单校验基于自定义 Validator的校验Spring内置校验 LocalValidatorFactoryBeanHibernateValidator校验使用HibernateValidator自定义校验规则 总结 引言 我们在日常的软件开发过程中,尤其是WEB开发过程中,…

antd Table 动态数据 合并单元格(合并行)

antd Table 组件动态合并单元格 最近处理table的时候 遇到了要合并同一列的几行的情况,比如第一列的前面三行都是同一个对象的名字,此时合并显示比较妥当,但是数据是后端接口来的,而且可以筛选条件,搜索出来的数据就是…

Linux的ping命令、wget命令、curl命令

一、ping命令 通过ping命令,可以检查指定的网络服务器是否是可联通状态 形式:ping [-c num] ip或主机名 -c:检查的次数,不使用-c,将无限次数持续检查 ip或主机名:被检查的服务器的ip地址或主机名地址 …

Jenkins分布式实现: 构建弹性和可扩展的CI/CD环境!

Jenkins是一个流行的开源持续集成(Continuous Integration,CI)和持续交付(Continuous Delivery,CD)工具,它通过自动化构建、测试和部署过程,帮助开发团队更高效地交付软件。Jenkins的…

AtomicReference自旋加CAS保证对象引用原子性

AtomicReference类提供了对象引用的非阻塞原子性读写操作,并且提供了其他一些高级的用法,对象的引用其实是一个4字节的数字,代表着在JVM堆内存中的引用地址,对一个4字节数字的读取操作和写入操作本身就是原子性的 package Atomic…

数据结构入门到入土——ArrayList与顺序表

目录 一,线性表 二,顺序表 1.接口实现 三,ArrayList简介 四,ArrayList使用 1.ArrayList的构造 2.ArrayList常见操作 3.ArrayList的遍历 4.ArrayList的扩容机制 五,ArrayLisit的具体使用 杨辉三角 一&#x…

【SpringMVC】常用注解

什么是MVC? MVC是一种程序分层开发模式,分别是Model(模型),View(视图)以及Controller(控制器)。这样做可以将程序的用户界面和业务逻辑分离,使得代码具有良好…

虚拟机快照

1.为什么使用快照 在学习阶段我们无法避免的可能损坏Linux操作系统。 如果损坏的话,重新安装一个Linux操作系统就会十分麻烦。 VMware虚拟机(Workstation和Funsion)支持为虚拟机制作快照。 通过快照将当前虚拟机的状态保存下来,在…

CentOS 7.6下的HTTP隧道代理配置详解

在CentOS 7.6操作系统中,配置HTTP隧道代理需要一定的技术知识和经验。下面我们将详细介绍如何配置HTTP隧道代理,以确保网络通信的安全性和稳定性。 首先,我们需要了解HTTP隧道代理的基本原理。HTTP隧道代理是一种通过HTTP协议传输其他协议数…

SSM农产品朔源管理系统----计算机毕业设计

项目介绍 本项目分为前后台,分为普通用户、管理员、企业用户三种角色; 普通用户无需登录,可在前台直接进行溯源查询,管理员、企业用户可登录后台进行管理; 超级管理员角色包含以下功能: 登录,管理企业,设…

用友U8 Cloud smartweb2.RPC.d XXE漏洞复现

0x01 产品简介 用友U8 Cloud 提供企业级云ERP整体解决方案,全面支持多组织业务协同,实现企业互联网资源连接。 U8 Cloud 亦是亚太地区成长型企业最广泛采用的云解决方案。 0x02 漏洞概述 用友U8 Cloud smartweb2.RPC.d接口处存在 XXE漏洞,攻击者可通过该漏洞获取敏感文件…

云卷云舒:【实战篇】Redis迁移

1. 简介 Remote Dictionary Server(Redis)是一个由Salvatore Sanfilippo写的key-value存储系统,是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 2. 迁移原理 redis-sh…

性能优化-OpenMP基础教程(三)

本文主要介绍OpenMP并行编程的环境变量和实战、主要对比理解嵌套并行的效果。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC)开发基础教程 🎀CSDN主页 发狂的小花 &…

麒麟镜像下载

试用版下载链接 产品试用申请国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站 下载自己对应的操作系统 我下载的是 共享文件下载 - Kylin Distro 然后用迅雷下载就可以了

基于sumo实现交通灯控制算法的模板

基于sumo实现交通灯控制算法的模板 目录 在windows安装run hello world networkroutesviewsettings & configurationsimulation 交通灯控制系统 介绍文件生成器类(FileGenerator)道路网络(Network)辅助函数生成道路网络&am…