STM32CubeMX学习笔记25---FreeRTOS信号量

news2024/12/25 12:23:18

一、信号量简介

        信号量用于同步,任务间或者任务和中断间同步

        互斥量用户互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁。

        二值信号量:同步。如果存在两个线程,为线程1和线程2,如果线程1发送了信号量,线程2接收到了信号量,这两个线程同步工作,如果线程1没有发送信号量,那么线程2也就不工作(前提线程2中有信号量接收等待函数)。相当于一个标志位。

        计数信号量:资源使用统计

        互斥量:互斥信号是一个拥有优先级继承的二值信号量

        递归互斥:互斥中嵌套互斥

        优先级反转,由于高优先级任务在等低优先级任务释放信号量的过程中,低优先级被中优先级任务抢夺CPU,最终导致中优先级的任务比高优先级的任务先运行,也就是优先级翻转,而互斥信号量就可以将这个影响降到最低,当高优先级任务发现需要信号量被低级信号量占用需要等待时,就将低优先级任务变成和自己同级优先级,就不至于在等待的过程中被中优先级抢占CPU。

1 信号量的常规操作

信号量这个名字很恰当:

  • 信号:起通知作用
  • 量:还可以用来表示资源的数量
    • 当"量"没有限制时,它就是"计数型信号量"(Counting Semaphores)
    • 当"量"只有0、1两个取值时,它就是"二进制信号量"(Binary Semaphores)
  • 支持的动作:"give"给出资源,计数值加1;"take"获得资源,计数值减1

计数型信号量的典型场景是:

  • 计数:事件产生时"give"信号量,让计数值加1;处理事件时要先"take"信号量,就是获得信号量,让计数值减1。
  • 资源管理:要想访问资源需要先"take"信号量,让计数值减1;用完资源后"give"信号量,让计数值加1。

信号量的"give"、"take"双方并不需要相同,可以用于生产者-消费者场合:

  • 生产者为任务A、B,消费者为任务C、D
  • 一开始信号量的计数值为0,如果任务C、D想获得信号量,会有两种结果:
    • 阻塞:买不到东西咱就等等吧,可以定个闹钟(超时时间)
    • 即刻返回失败:不等
  • 任务A、B可以生产资源,就是让信号量的计数值增加1,并且把等待这个资源的顾客唤醒
  • 唤醒谁?谁优先级高就唤醒谁,如果大家优先级一样就唤醒等待时间最长的人

二进制信号量跟计数型的唯一差别,就是计数值的最大值被限定为1。

 

2 信号量跟队列的对比

差异列表如下:

队列信号量
可以容纳多个数据,
创建队列时有2部分内存: 队列结构体、存储数据的空间
只有计数值,无法容纳其他数据。
创建信号量时,只需要分配信号量结构体
生产者:没有空间存入数据时可以阻塞生产者:用于不阻塞,计数值已经达到最大时返回失败
消费者:没有数据时可以阻塞消费者:没有资源时可以阻塞

3 两种信号量的对比

信号量的计数值都有限制:限定了最大值。如果最大值被限定为1,那么它就是二进制信号量;如果最大值不是1,它就是计数型信号量。

差别列表如下:

二进制信号量技术型信号量
被创建时初始值为0被创建时初始值可以设定
其他操作是一样的其他操作是一样的

 使用信号量时,先创建、然后去添加资源、获得资源。使用句柄来表示一个信号量。

 以上转自:韦东山freeRTOS系列教程之【第六章】信号量(semaphore)_freertos semaphore-CSDN博客

二、STM32CubeMX设置

 1、根据上一章的步骤创建两个任务:

STM32CubeMX学习笔记22---FreeRTOS(任务创建和删除)-CSDN博客

任务LED1用作发送,LED2用作接收。

 2、创建信号量

        在 Timers and Semaphores 进行配置。

(1)、创建二值信号量Binary Semaphore

  • Semaphore Name: 信号量名称
  • Allocation: 分配方式:Dynamic 动态内存创建
  • Conrol Block Name: 控制块名称
(2)、 创建计数信号量Counting Semaphore

        要想使用计数信号量必须在 Config parameters 中把 USE_COUNTING_SEMAPHORES 选择 Enabled 来使能。

         在 Timers and Semaphores 创建计数信号量

  • Semaphore Name: 信号量名称
  • Count: 计数信号量的最大值
  • Allocation: 分配方式:Dynamic 动态内存创建
  • Conrol Block Name: 控制块名称
3、生成代码

三、程序编程

1、创建一个二值量、并返回一个ID:osSemaphoreCreate
函数osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count)
参数semaphore_def: 引用由osSemaphoreDef定义的信号量

count: 信号量数量
返回值成功返回信号量ID,失败返回0

例:

osSemaphoreId myBinarySem01Handle;  

 /* definition and creation of myBinarySem01 */
  osSemaphoreDef(myBinarySem01);
  myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
2、删除一个信号量:osSemaphoreDelete

包括二值信号量,计数信号量,互斥量和递归互斥量。如果有任务阻塞在该信号量上,那么不要删除信号量

函数osStatus osSemaphoreDelete (osSemaphoreId semaphore_id)
参数semaphore_id: 信号量ID
返回值错误码

例:

osSemaphoreDelete(myBinarySem01Handle);
 3、释放信号量的宏:osSemaphoreRelease 

释放的对象必须是已经创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数xSemaphoreCreateRecursiveMutex() 创建的互斥量。可用在中断服务程序中。

函数osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
参数semaphore_id: 信号量ID
返回值错误码

 例:

osSemaphoreRelease(myBinarySem01Handle);
4、获取信号量:osSemaphoreWait

用于获取信号量,不带中断保护。获取的信号量对象可以是二值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个 API 函数获取。可用在中断服务程序中。

 5、二值信号量实验

 运作机制:

创建信号量时,系统会为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1。

二值信号量获取,任何任务都可以从创建的二值信号量资源中获取一个二值信号量,获取成功则返回正确,否则任务会根据用户指定的阻塞超时时间来等待其它任务/中断释放信号量。在等待这段时间,系统将任务变成阻塞态,任务将被挂到该信号量的阻塞等待列表中。

假如某个时间中断/任务释放了信号量,那么,由于获取无效信号量而进入阻塞态的任务将获得信号量并且恢复为就绪态状态。

(1)、osSemaphoreRelease 释放宏

当计数大于5时,释放二值信号量。

void LED1_Task1(void const * argument)
{
  /* USER CODE BEGIN LED1_Task1 */
  /* Infinite loop */
	 osStatus xReturn;
	
	int i=0;
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("led1 run ..%d\n",i++);	
		if(i>5){
				xReturn = osSemaphoreRelease(myBinarySem01Handle);//给出二值信号量 
        if(osOK == xReturn)
        {
            printf("release!\r\n"); 
        }
        else 
        {
            printf("BinarySem release fail!\r\n"); 
        }
			}
		
    osDelay(1000);
  }
  /* USER CODE END LED1_Task1 */
}

(2)、获取二值信号量osSemaphoreWait

一直等待二值信号量

void LED2_Task03(void const * argument)
{
  /* USER CODE BEGIN LED2_Task03 */
  /* Infinite loop */
	 osStatus xReturn = osErrorValue;
	int i=0;
  for(;;)
  { 
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("wait Binary ..%d\n",i++);	
		xReturn = osSemaphoreWait(myBinarySem01Handle, /* 二值信号量句柄 */ 
                               osWaitForever); /* 等待时间 */ 
    if(osOK == xReturn) 
    {
        printf("BinarySem get!\n\n");
    }
    osDelay(600);
  }
  /* USER CODE END LED2_Task03 */
}

(3)、下载验证:

编译无误后下载到板子上可以看到LED2一直等待信号量,等LED1释放了二值信号量后才继续运行。

         在编写程序的时候发现,如果多次释放二值信号量时,只有第一次会成功,而接收函数也只能接收到一次的二值信号量,也就是说二值信号量是一对一的,而且如果多个任务都接收同一个信号量,那么只有优先级高能接收到信号量,低优先级的会接收不到!因此引入了一下的计数信号量,可实现多个任务接收信号量。

 6、计数信号量实验

运作机制:

        使用计数型信号量时,可以多次释放信号量;当信号量的计数值达到最大时,再次释放信号量就会出错。

        如果信号量计数值为n,就可以连续n次获取信号量,第(n+1)次获取信号量就会阻塞或失败。

(1)、osSemaphoreRelease 释放宏

当计数为5的倍数时,释放两次计数信号量。

void LED1_Task1(void const * argument)
{
  /* USER CODE BEGIN LED1_Task1 */
  /* Infinite loop */
	 osStatus xReturn;
	
	int i=0;
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("led1 run ..%d\n",i++);	
		if(i%5==0){
				xReturn = osSemaphoreRelease(myCountingSem01Handle);//给出计数信号量 
        if(osOK == xReturn)
        {
            printf("release 1! \r\n"); 
        }
        else 
        {
            printf("BinarySem release fail!\r\n"); 
        }
								xReturn = osSemaphoreRelease(myCountingSem01Handle);//给出计数信号量 
        if(osOK == xReturn)
        {
            printf("release 2! \r\n"); 
        }
        else 
        {
            printf("BinarySem release fail!\r\n"); 
        }
			}
		
    osDelay(1000);
  }
}

(2)、获取计数信号量osSemaphoreWait

两个任务分别一直等待计数信号量

void LED2_Task03(void const * argument)
{
  /* USER CODE BEGIN LED2_Task03 */
  /* Infinite loop */
	 osStatus xReturn = osErrorValue;
	int i=0;
  for(;;)
  { 
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("wait Binary ..%d\n",i++);	

    xReturn = osSemaphoreWait(myCountingSem01Handle, /* 计数信号量句柄 */ 
                               osWaitForever); /* 等待时间 */ 
		
    osDelay(600);
  }
  /* USER CODE END LED2_Task03 */
}


void StartTask3(void const * argument)
{
  /* USER CODE BEGIN StartTask3 */
  /* Infinite loop */
	 osStatus xReturn = osErrorValue;
	int i=0;
  for(;;)
  { 
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("wait Binary 33..%d\n",i++);	
		
		xReturn = osSemaphoreWait(myCountingSem01Handle, /* 二值信号量句柄 */ 
													 osWaitForever); /* 等待时间 */ 

    osDelay(700);
  }
  /* USER CODE END StartTask3 */
}

(3)、下载验证:

编译无误后下载到板子上可以看到LED2一直等待信号量,等LED1释放了二值信号量后才继续运行。

 四、参考文献

STM32CubeMX学习笔记(30)——FreeRTOS实时操作系统使用(信号量)_ossemaphorecreate-CSDN博客

韦东山freeRTOS系列教程之【第六章】信号量(semaphore)_freertos semaphore-CSDN博客 

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

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

相关文章

大载重无人机基础技术,研发一款50KG负重六旋翼无人机技术及成本分析

六旋翼无人机是一种多旋翼无人机,具有六个旋翼,通常呈“X”形布局。它采用电动串列式结构,具有垂直起降、悬停、前飞、后飞、侧飞、俯仰、翻滚等多种飞行动作的能力。六旋翼无人机通常被用于航拍、农业植保、环境监测、地形测绘等领域。 六旋…

PolarDN MISC(简单)大礼包 :详细思路过程

0和255 题目给了俩个文件,一个.txt,一个.py .txt文件中包含0和255 一个字节有八位,每一位只能储存1或0,计算机只懂二进制,所以就是2的八次方,又计算机规定从0开始计数,所以是0至255 考虑用编码转换工具将其…

Android: Gradle 命令

一、查看整个项目依赖传递关系 x.x.x (*) 该依赖已经有了,将不再重复依赖。x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替。x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替,并且该依赖已经有了,不再重复依赖。 1. gradlew ap…

redis常用五大数据类型

目录 Key 字符串String 常用命令 列表List 常用命令 集合Set 常用命令 Hash哈希 键值对集合 有序集合Zset Redis新数据类型 Key set key value...添加keykeys *查看当前库中所有的keyexist key该key是否存在type keykey的类型del key删除keyunlink key根据value选择非阻塞…

基于springboot+vue的火锅店管理系统

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

2024.3.16-408学习笔记-C-数据在内存中的存储

1、整数型存储 整数型存储就是所有整型家族里的数据类型的存储方式,也就是说包含了字符类型的存储(因为字符的操作符的返回值是ASCII码值,故实际上存储的是整数)。 1.1、有符号整数 有符号整数包含char,short&#x…

Combining external-latent attention for medical image segmentation

结合外部潜在注意的医学图像分割 摘要 注意机制是提高医学图像分割性能的新切入点。如何合理分配权重是注意力机制的关键,目前流行的方法包括全局压缩和使用自注意操作的非局部信息交互。然而,这些方法过于关注外部特征,缺乏对潜在特征的开发。全局压缩方法通过全局均值或…

(一)基于IDEA的JAVA基础3

通过之前的内容,我们在建好的文件夹下建一个java文件,我们来在IDEA中写一下之前用记事本写的helloworld,我们先看一下java代码的规范: 1.java程序文件名一定要有意义,首字母一定要大写。 2.class后面的名字:由大小写字母&#x…

【NTN 卫星通信】 车辆物联网设备通过NTN和TN切换的应用场景

1 场景描述 对于有两个3GPP无线接入网服务的大面积农田和农场,物联网设备可以通过NTN和TN接入网同时受益于5G系统的双转向数据连接能力。   在这个用例中,我们有一个广域的农业自动化应用系统来控制农业车辆,例如,一个装有数百个…

遇见AI暴雨受邀参加NVIDIA GTC 2024

2024年3月18日,备受瞩目全球科技盛会——2024 GTC在美国加州圣何塞拉开帷幕,此次大会旨在展示最新的GPU(图形处理单元)技术和应用,以及人工智能、数据科学、深度学习、虚拟化等领域的创新和发展。作为NVIDIA多年的战略…

浅尝大菠萝Pinia

1、pinia简介 Pinia(发音为 /piːnjʌ/,类似于英语中的“peenya”)是最接近有效包名 pia(西班牙语中的_pineapple_)的词。 Pinia 是由 Vue.js 团队成员开发,新一代的状态管理器,即 Vuex5.x。 …

C语言-----冒泡排序

今天,让我们来学习一下C语言中一个简单的排序算法------冒泡排序。 什么是冒泡排序呢? 冒泡排序是C语言中一个可以将一个数组的内容按照升序或者降序进行重新排列的算法。简单来说,是一种排序的思维。 冒泡排序的核心思想:让同…

MS2574/2574T/2574S高速、四通道差动线路驱动器

品简述 MS2574/MS2574T/MS2574S 是一款高速、低功耗的四通道 差动线路驱动芯片,用于平衡或非平衡的数字数据传输。可 以满足 ANSI TIA/EIA-422-B 和 ITU (原 CCITT )建议 V.11 的要求。 三态输出可提供用于驱动双绞线或平行双线传输线路等…

【面试精讲】深克隆和浅克隆的实现方式?深克隆和浅克隆有什么区别?

【面试精讲】深克隆和浅克隆的实现方式?深克隆和浅克隆有什么区别? 目录 本文导读 一、浅克隆(Shallow Clone) 二、深克隆(Deep Clone) 1、递归使用clone()方法实现深克隆 2、使用序列化实现深克隆 …

程序员思维之新机遇面前如何选择

你会不会因为 AI 的爆火,焦虑 AI 会替代自己的工作,焦虑其他人躬身入局把你淘汰。 我有过,甚至想全部精力都去学习 ChatGPT、Stable diffusion 、 Midjourney,每新出来一款大语言模型就想预约体验,那段时间像是无头的…

【复现】某指挥调度管理平台 SQL注入漏洞_66

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议: 五. 搜索语法: 六.免责声明 一.概述 该平台提供强大的指挥调度功能,可以实时监控和管理通信网络设备、维护人员和工作任务等。用户可以通过该平台发送指令…

代码随想录算法训练营Day52 ||leetCode 300.最长递增子序列 || 674. 最长连续递增序列 || 718. 最长重复子数组

300.最长递增子序列 class Solution { public:int lengthOfLIS(vector<int>& nums) {if (nums.size() < 1) return nums.size();vector<int> dp(nums.size(), 1);int result 0;for (int i 1; i < nums.size(); i) {for (int j 0; j < i; j) {if (…

概率基础——逻辑回归多分类法

概率基础——逻辑回归多分类法 逻辑回归是一种经典的分类算法&#xff0c;通常用于解决二分类问题。然而&#xff0c;在实际应用中&#xff0c;我们经常会遇到多分类任务。本文将简单介绍逻辑回归的理论、多分类方法以及优缺点&#xff0c;并提供一个Python实现的示例。 逻辑…

2 使用GPU理解并行计算

2.1 简介 本章旨在对并行程序设计的基本概念及其与GPU技术的联系做一个宽泛的介绍。本章主要面向具有串行程序设计经验&#xff0c;但对并行处理概念缺乏了解的读者。我们将用GPU的基本知识来讲解并行程序设计的基本概念。 2.2 传统的串行代码 绝大多数程序员是在串行程序占据…

3.6 条件判断语句cmp,je,ja,jb及adc、sbb指令

汇编语言 1. adc指令 adc是带进位加法指令&#xff0c;它利用了CF位上记录的进位值指令格式&#xff1a;adc 操作对象1&#xff0c;操作对象2功能&#xff1a;操作对象1 操作对象1 操作对象2 CF例如&#xff1a;adc ax,bx&#xff0c;实现的功能是&#xff1a;ax ax bx …