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

news2025/1/16 16:13:41

本文是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上:程序下载。

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

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

相关文章

每日算法练习(1)

开一个新坑,记录下自己每天的算法练习,希望自己通过1个多月的学习,能够成为算法大神。 下面正式开始新坑。 两个数组的交集 这是牛客上的题,根据题意,我们有多种解法,这题用哈希比较好写。我们可以弄一个…

(保姆级教学)跨站请求伪造漏洞

1. CSRF漏洞 CSRF(Cross-site request forgery)跨站请求伪造,也被称为One Click Attack 或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS)&…

Linux内核启动过程图解(全程高能)

文章目录 1、进入第一条指令 _text2、start_kernel3、总结(内核启动流程描述) 1、进入第一条指令 _text 编译内核后,打开System.map文件,找到对应的_text和start_kernel ffffff8008080000 T _text //第一条指令 ffffff80096007f4 T start_kernel //…

代码随想录算法训练营第四十四天| 完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

一、完全背包 题目链接/文章讲解/视频讲解:https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85.html 状态:已解决 1.问题介绍 完全背包的模板题目:…

dp思维 枚举

题目链接 #include<bits/stdc.h> using namespace std; #define i64 long long const i64 mod 1e9 7; int main() {int n;cin >> n;vector<char>s(n 1);for (int i 1; i < n; i) {cin >> s[i];}//用ans记录所有满足条件的答案数量&#xff0c;c…

linux下使用qt+mpv调用GPU硬件解码

linux下GPU硬件解码接口&#xff0c;常用的有vdpau和vaapi。 mpv是基于mplayer开发的一个播放器。此外&#xff0c;mpv还提供了函数库libmpv&#xff0c;通过使用libmpv可以编写一个简单的播放器。 基于qtlibmpv的demo&#xff0c;官方例子代码如下&#xff1a;https://github.…

STM32之串口中断接收丢失数据

五六年没搞STM32了,这个项目一切都挺顺利,万万没想到被串口接收中断恶心到了。遇到的问题很奇怪 HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], LCD_UART_LEN); 这个代码中 LCD_UART_LEN=1的时候,接收过来的数据,数据包的第一个字节总是会跑到rx_buffer的末…

UE5集成gRPC

最近有项目需要在UE5里做RPC&#xff0c;对比了thrift、gRPC、rcplib等开源rpc框架&#xff0c;由于习惯使用protobuf&#xff0c;故选择了gRPC。然而&#xff0c;Google出品也是一言难尽啊&#xff0c;最起码编译太繁琐了。 本次使用的gRPC版本为1.62.1&#xff0c;UE5.2&…

面试stm32基础知识

1.ISP 第一步进入bootloader模式&#xff1a;先置BOOT0为高&#xff0c;BOOT1为低&#xff0c;再复位单片机进入bootloader模式&#xff0c;之后通过上位机下载程序&#xff1b; 第二步配置启动代码的地方&#xff1a;代码下载完毕后&#xff0c;置BOOT0为低&#xff0c;BOOT1…

【NTN 卫星通信】NTN的信关站应该建在哪些地方

1 概述 3GPP的卫星通信讨论了透传星和再生星两种方式。透传星方式&#xff0c;卫星主要是做为中继存在&#xff0c;基本上不做通信协议的处理。再生星方式&#xff0c;gNodeB的主要功能在卫星上&#xff0c;完成通信协议的主要内容。无论是透传星还是再生星&#xff0c;都需要通…

[C++]异常

基本使用 try {int a, b; std::cin >> a >> b;if(b 0) {throw std::runtime_error("unexpected zero");} else {cout << a / b << endl;}} catch(std::exception &e) { cout << "runtime_error: " << e.what() &…

战姬物语部署

一.准备环境 #关闭seliunx和防火墙 setenforce 0 systemctl stop firewalld systemctl disable firewalld #配置源&#xff0c;并安装常用工 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo curl -o /etc/yum.repos.d/epel.repo …

用队列实现栈(力扣第225题)

#include "stdio.h" #include "stdbool.h" #include "string.h" #include "stdlib.h" #include "assert.h"//初始化队列 typedef int QueueDataType;typedef struct queue {QueueDataType val;struct queue* next; }Qnode;t…

信号处理相关知识

一&#xff1a; 1.序列——三种典型序列通过matlab绘图即可 2.数字信号的自变量一定是整数&#xff0c;幅度上取值是有限的状态&#xff08;不一定是整数&#xff09;。 3.抽取和插值 4.模拟正弦信号sin(wt):w是角频率&#xff0c;单位rad/s,f是频率w/2Π。 5.假设用采样周…

盒子模型之弹性盒模型

经常适用于手机端图标布局 display: flex;让这个盒子显示成弹性盒&#xff08;很适合移动端布局&#xff09; 影响&#xff1a;1.让里面的子元素默认横向排列 2.如果子元素是行内元素&#xff0c;则直接变成块元素 3.只有一个元素&#xff0c;margin: auto;自动居中 <!DOCT…

CDPR全力开发《巫师4》,吸取《2077》教训

爆料来啦&#xff01;CDPR全力开发《巫师4》&#xff0c;吸取《2077》教训&#xff01; CD Projekt Red&#xff08;CDPR&#xff09;今天透露&#xff0c;目前全公司都在专心致志地开发《巫师4》&#xff0c;而《赛博朋克2077》的续作暂时还在早期阶段。之前参与2077的大多数…

228 基于matlab的神经网络人脸识别

基于matlab的神经网络人脸识别。 人脸识别以视网膜、 虹膜、 指纹等生物特征的识别作为生物标识符。生物特征识别不很容易伪造、 放错位置。新型脸识别使用的方法 RobustPCA 和径向基函数网络。程序已调通&#xff0c;可直接运行。 228 人脸识别 生物特征识 神经网络 - 小红书 …

12. MyBatis(二)

源码位置&#xff1a;MyBatis_demo 上篇文章我们学习了MyBatis的定义以及增删查改操作&#xff0c;并且学习了如何在xml文件中编写SQL时使用#{}的方式将参数和对象的属性映射到SQL语句中&#xff0c;上篇的内容已经足以应对大部分场景&#xff0c;本篇文章我们就要学习一下MyBa…

Jmeter 测试Dubbo接口-实例

1、Dubbo插件准备 ①把jmeter-plugins-dubbo-2.7.4.1-jar-with-dependencies.jar包放在D:\apache-jmeter-5.5\lib\ext目录 ②重新打开Jmeter客户端 在线程组-添加-取样器-dubbo simple&#xff0c;添加dubbo接口请求 2、Jmeter测试lottery接口 ①配置zookeeper参数 由于dub…

MapReduce案例-电影网站数据统计分析

本文适合大数据初学者学习MapReduce统计分析业务问题的步骤和基础的MapReduce编程方法&#xff0c;初步掌握Hadoop对计算任务的管理。 本文末尾有全部数据集和完整代码连接。 1.准备工作 安装Hadoop:Hadoop 3.3.2 离线安装-CSDN博客 按照好Hadoop之后要检查一下datanode运行情况…