【复习笔记】reeRTOS(七) 二值信号量和计数信号量

本文是FreeRTOS复习笔记的第七节,信号量。

上一篇文章: 【复习笔记】FreeRTOS(六) 队列操作

文章目录

  • 一、信号量分类
  • 二、二值信号量
    • 2.1.实验设计
    • 2.2.测试例程
    • 2.3.实验效果
  • 三、计数信号量
    • 3.1.实验设计
    • 3.2.测试例程
    • 3.3.实验效果


一、信号量分类

信号量是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,其实信号量主要的功能就是实现任务之间的同步与互斥,实现的方式主要就是依靠队列(信号量是特殊的队列)的任务阻塞机制。

队列也可以实现同步与互斥那为什么还要信号量?
因为信号量相比队列更节省空间,因为实现同步与互斥不需要传递数据,所以信号量没有队列后面的环形存储区,信号量主要就是依靠计数值uxMessagesWaiting(在队列中表示队列现有消息个数,在信号量中表示有效信号量个数)。

信号量的本质的都是特殊的队列,信号量只有队列头部,并没有后面的环形存储区,也就是说信号量只负责消息传递,并不传递数据。
信号量分为二值信号量、计数信号量、互斥信号量和递归互斥信号量。
在这里插入图片描述
本篇文章主要复习一下二值信号量和计数信号量。

二、二值信号量

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况。二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步。
在这里插入图片描述
二值信号量相关API函数:

函数作用
xSemaphoreCreateBinary()使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR()在中断中获取信号量

2.1.实验设计

实验目的:学会对FreeRTOS 二值信号量的使用
实验设计:将设计两个任务:
task1:用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量
task2:获取二值信号量,当成功获取后打印提示信息

2.2.测试例程

主函数 main.c代码如下:


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

QueueHandle_t semphore_handle;

void task1_task(void *p); //任务函数
void task2_task(void *p); //任务函数


int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);     	//初始化串口
	LED_Init();		        //初始化LED端口
	KEY_Init();				//初始化按键
	
	/*动态创建二值信号量*/
    semphore_handle = xSemaphoreCreateBinary();
	if(semphore_handle==NULL)
	{

		printf("信号量创建失败!\r\n");
	}
	
    xTaskCreate(task1_task,"task1_task",128,NULL,2,NULL); //任务1        
    xTaskCreate(task2_task,"task2_task",128,NULL,2,NULL);  //任务2 	
	
    vTaskStartScheduler();          //开启任务调度
}
 

/* 任务一,释放二值信号量 */
void task1_task(void *p)
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = KEY_Scan(0);
        if(key == WKUP_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
					taskENTER_CRITICAL();           //进入临界区
                    printf("Semaphore release success!\r\n");
					taskEXIT_CRITICAL();            //退出临界区
                }
				else 
				{
					taskENTER_CRITICAL();           //进入临界区
					printf("Semaphore release failure!\r\n");
					taskEXIT_CRITICAL();            //退出临界区
				}
            }            
        }
        vTaskDelay(10);
    }
}   


/* 任务二,获取二值信号量 */
void task2_task(void *p)
{

    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
			taskENTER_CRITICAL();           //进入临界区
            printf("Obtaining semaphore success!\r\n");
			taskEXIT_CRITICAL();            //退出临界区
        }
		else 
		{
			taskENTER_CRITICAL();           //进入临界区
			printf("Time out:%d\r\n",++i);
			taskEXIT_CRITICAL();            //退出临界区
		}
        
    }
}

程序上需要注意的是,每次使用printf()函数都添加了临界区保护,因为printf()比较耗时,如果不加临界区保护,打印长字符串的时候,打印到一半就被切换到另一个任务,这样很容易打印乱码。

2.3.实验效果

实验效果如下:
打开串口工具,每次按下按键,串口就会打印 Semaphore release success!代表成功释放了二值信号量,随后立即打印Obtaining semaphore success!代表成功捕获了二值信号量。
在这里插入图片描述

三、计数信号量

计数信号量也比较简单,计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。
计数型信号量适用场合:

  • 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0 。
  • 资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目。

通俗地说就是,信号量释放:cnt++,信号量被获取:cnt - -。

二值信号量相关API函数:

函数作用
xSemaphoreCreateCounting()使用动态方法创建计数型信号量
xSemaphoreCreateCountingStatic()使用静态方法创建计数型信号量
uxSemaphoreGetCount()获取信号量的计数值

计数型信号量的释放函数和获取函数与二值信号量相同 。

3.1.实验设计

实验目的:学会对FreeRTOS 计数信号量的使用
实验设计:将设计两个任务:
task1:用于按键扫描,当检测到按键KEY0被按下时,释放计数型信号量。
task2:每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值。

3.2.测试例程

主函数 main.c代码如下:


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

QueueHandle_t count_semphore_handle;

void task1_task(void *p); //任务函数
void task2_task(void *p); //任务函数


int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);     	//初始化串口
	LED_Init();		        //初始化LED端口
	KEY_Init();				//初始化按键
	
     /* 创建计数型信号量 */
    count_semphore_handle = xSemaphoreCreateCounting(100 , 0); //最大计数值设为100,初始值为0
	if(count_semphore_handle==NULL)
	{

		printf("信号量创建失败!\r\n");
	}
	
    xTaskCreate(task1_task,"task1_task",128,NULL,2,NULL); //任务1        
    xTaskCreate(task2_task,"task2_task",128,NULL,2,NULL);  //任务2 	
	
    vTaskStartScheduler();          //开启任务调度
}
 

/* 任务一,释放计数型信号量 */
void task1_task(void *p)
{

    uint8_t key = 0;
    while(1) 
    {
        key = KEY_Scan(0);
        if(key == WKUP_PRES)
        {
            if(count_semphore_handle != NULL)
            {
                xSemaphoreGive(count_semphore_handle);    /* 释放信号量 */
            }            
        }
        vTaskDelay(10);
    }
}   

/* 任务二,获取计数型信号量 */
void task2_task(void *p)
{
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
			taskENTER_CRITICAL();           //进入临界区
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
			taskEXIT_CRITICAL();            //退出临界区
        }
        vTaskDelay(1000);
        
    }
}

3.3.实验效果

实验效果如下:
烧录程序,打开串口工具,复位。由于初始值设为0,按键没有按下的时候,串口不会打印任何东西。在1000ms内快速按下按键,就可以看到打印出信号量的计数值。按下按键的频率越快,信号量的计数值越大;没有按键按下的时候,信号量的计数值会自动减少直到0。

在这里插入图片描述
如果超过1000ms才按下,信号量的计数值就也会变成0.
在这里插入图片描述

本节主要是学习和掌握二值信号量、计数信号量的基本使用。
完整程序放在gitee上:程序下载。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

盲盒新风潮:从玩具到文化符号的转变

亲爱的朋友们,我是微三云的周丽,一名专注于私域电商模式创新的探索者。 随着互联网和电子商务的迅猛发展,商业模式不断创新,盲盒电商作为其中的一种新兴形式,正逐渐引起人们的关注。盲盒电商不仅仅局限于传统的日用品…

【电控实现5.1】

标幺系统 vb:峰值

【Node.js】 fs模块全解析

🔥【Node.js】 fs模块全解析 📢 引言 在Node.js开发中,fs模块犹如一把万能钥匙,解锁着整个文件系统的操作。从读取文件、写入文件、检查状态到目录管理,无所不能。接下来,我们将逐一揭开fs模块中最常用的那…

【C++】string的使用

目录 1、为什么学习string类? 2、标准库中的string类 2.1 string类 2.2 string类的常见接口声明 2.2.1 string类的常见构造 ​编辑 2.2.2 string类对象的访问及遍历操作 2.2.3 string类对象的容量操作 2.2.4 string类对象的修改操作 ​编辑 1、为什么学习s…

CERLAB无人机自主框架: 2-动态目标检测与跟踪

前言:更多更新文章详见我的个人博客主页【MGodmonkeyの世界】 描述:欢迎来到CERLAB无人机自主框架,这是一个用于自主无人飞行器 (UAV) 的多功能模块化框架。该框架包括不同的组件 (模拟器,感知,映射,规划和…

gemini国内能用吗

gemini国内能用吗 虽然 Gemini 的具体功能和性能还未完全公开,但基于 Google 在 AI 领域的强大背景和技术实力,已经火出圈了,很多小伙伴已经迫不及待想了解一下它有什么优势以及如何快速使用上 首先我们来讲一下gemini的优势 多模态能力&a…

美摄智能视频创作平台,满足企业个性化的创作需求

视频已成为企业传播信息、展示品牌、吸引客户的重要手段,传统的视频制作方式往往耗时耗力,且效果不佳。美摄科技凭借其深厚的技术积累和创新能力,推出了面向企业的智能视频创作平台解决方案,助力企业轻松实现高质量的视频制作与传…

5 CatBoost模型

目录 1 背景 2 原理 2.1 类别特征处理 2.1.1 传统目标编码: TS 2.1.2 Greedy TS 2.1.3 ordered TS编码 2.1.4 CatBoost处理Categorical features总结 2.2.预测偏移处理 2.2.1 梯度无偏估计 2.3 树的构建​​​​​​​ 3 优缺点 优点 4 代码 1 背景 终于…

OpenHarmony鸿蒙南向开发案例:【智能门铃】

样例简介 智能门铃通过监控来访者信息,告诉主人门外是否有人按铃、有陌生人靠近或者无人状态。主人可以在数字管家中远程接收消息,并根据需要进行远程取消报警和一键开锁。同时,也可以通过室内屏幕获取门外状态。室内屏幕显示界面使用DevEco…

【创建型模式】单例模式

一、单例模式概述 单例模式的定义:又叫单件模式,确保一个类只有一个实例,并提供一个全局访问点。(对象创建型) 要点: 1.某个类只能有一个实例;2.必须自行创建这个实例;3.必须自行向整…

C语言 | 动态内存管理

目录: 1. 为什么要有动态内存分配 2. malloc和free 3. calloc和realloc 4. 常见的动态内存的错误 5. 动态内存经典笔试题分析 6. 柔性数组 1. 为什么要有动态内存分配 我们已经掌握的内存开辟方式有: int val 20; //在栈空间上开辟四个字节 cha…

MR-JE-70A 三菱MR-JE伺服驱动器(750W通用型)

三菱MR-JE伺服驱动器(750W通用型) MR-JE-70A外部连接,MR-JE-70A用户手册,MR-JE-70A 三相或单相AC220V三菱通用型伺服放大器750W,配套电机HG-SN52J-S100、HG-KN73J-S100。 MR-JE-70A参数说明:伺服驱动器通用型750W,三相或单相AC200V~240V 三…

C语言野指针【入门详解】

目录 一、什么是野指针 二、野指针的成因 2.1 指针未初始化 2.2 指针越界访问 2.3 指针指向的空间释放 三、如何规避野指针 3.1 初始化指针 3.2 小心越界访问 3.3 当指针不用时,及时置为空 3.4 避免返回局部变量的地址 *结语: 希望这篇关于指…

IM即时通讯软件,WorkPlus私有化部署全面支持信创环境

在数字化转型的浪潮中,政企单位对即时通讯(IM)软件的需求日益增长。然而,随着信息化程度的提高,数据安全和信息泄露风险也日益凸显。在这样的背景下,WorkPlus作为一款私有化部署的IM即时通讯软件,以其在安全性、管理便…

汇编语言——将BX中的无符号数和有符号数以二进制、八进制、十六进制、十进制形式输出

文章目录 将BX中的无符号数以二进制形式输出将BX中的无符号数以八进制形式输出将BX中的无符号数以十六进制形式输出将BX中的无符号数以十进制形式输出将BX中的有符号数以十进制形式输出 将BX中的无符号数以二进制形式输出 利用移位指令会影响CF,默认dl30h(数字0)&a…

时序深入之CPR(Clock Pessimism Removal)详解

目录 一、CPR概念 二、CPR的计算 三、CPR的开启关闭 四、CPR为0 ​五、参考资料 一、CPR概念 在时序报告的目标时钟路径中,会有一行数据clock pesssimism,第一次见可能都会对这个概念感到疑惑 同样在每条时序路径的summary中,clock pat…

自动化测试Selenium(4)

WebDriver相关api 定位一组元素 webdriver可以很方便地使用findElement方法来定位某个特定的对象, 不过有时候我们需要定位一组对象, 这时候就要使用findElements方法. 定位一组对象一般用于一下场景: 批量操作对象, 比如将页面上的checkbox都勾上. 先获取一组对象, 再在这组…

[最新]访问/加速StackOverFlow的方法

但是有很多问题都是在StackOverFlow上有现成的解决方案,而某度搜索引擎…前一页的回答互相抄袭,看着实在胀眼睛。 话不多说,解决办法: 直接访问插件商店下载插件(最快捷方便,点点就行)&#x…

Linux系统编程——权限概念和权限管理

目录 一,关于Shell 1.1 外壳程序 1.2 shell的作用 1.3 shell运行原理 二,权限概念 2.1 用户与权限 2.2 su(用户切换指令) ​编辑 2.3 提升指令权限和信任名单 三,文件权限 3.1 关于文件权限 3.2 文件访问者…

UG10.如何设置鼠标滚轮操作模型放大缩小方向?

UG10.如何设置鼠标滚轮操作模型放大缩小方向呢?看一下具体操作步骤吧。 首先打开UG10.软件,在主菜单栏选择【文件】下拉菜单,选择【实用工具】。 点击【用户默认设置】。 文章源自四五设计网-https://www.45te.com/45545.html 选中【基本环…