当前位置: 首页 > news >正文

STM32-FreeRTOS操作系统-二值信号量与计数信号量

引言

在嵌入式开发领域,任务同步与通信是系统稳定运行的核心。STM32配合FreeRTOS操作系统,为开发者提供了强大的工具支持。其中,二值信号量和计数信号量作为FreeRTOS的关键同步机制,分别用于任务间的简单同步和资源计数控制。二值信号量如同一个开关,用于控制任务的进入与退出;而计数信号量则用于管理有限资源的访问,确保资源的合理分配。本文将深入剖析这两种信号量的工作原理、使用方法及在STM32平台上的应用实例,助力开发者精准掌握其精髓,提升系统开发效率与稳定性。

什么是二值信号量?

二值信号量是一种同步机制,用于在多任务环境中协调任务的执行。它本质上是一个只能取两个值(通常是0和1)的变量,类似于一个开关。当信号量的值为1时,表示资源可用;当值为0时,表示资源已被占用。在FreeRTOS中,二值信号量主要用于任务间的同步,而不是用于互斥。它的工作原理如下:当一个任务需要资源时,它会尝试获取信号量。如果信号量的值为1,任务会将其值减1并继续执行;如果信号量的值为0,任务会进入阻塞状态,等待信号量变为1。当另一个任务释放资源时,它会将信号量的值加1,从而唤醒等待的任务。

什么是计数信号量?

计数信号量是一种用于同步和资源管理的机制,它维护一个非负整数值,表示可用资源的数量。在FreeRTOS中,计数信号量通常用于管理有限数量的资源,例如缓冲区、硬件设备等。计数信号量的工作原理如下:当一个任务需要使用资源时,它会尝试获取信号量。如果信号量的值大于0,任务会将其值减1并继续执行,表示占用了一个资源;如果信号量的值为0,任务会进入阻塞状态,等待资源变为可用。当任务释放资源时,它会将信号量的值加1,从而表示资源数量增加,可能会唤醒等待的任务。

二值信号量

创建二值信号量句柄

步骤都跟前面的创建任务差不多,只不过在创建任务的基础上添加了二值信号量的相关函数,从本文章开始就不再细说怎么创建任务,直接开始讲二值信号量的创建及使用。

首先定义一下二值信号量的句柄并为其赋初始值为NULL:

SemaphoreHandle_t BinarySem_Handle =NULL; //二值信号量句柄

然后是xSemaphoreCreateBinary,它是FreeRTOS操作系统提供的一个函数,用于创建一个二值信号量,前面也说了,二值信号量是一个特殊的信号量,它的值只能是0或1,通常用于任务间的同步。其函数原型为:

SemaphoreHandle_t xSemaphoreCreateBinary(void);

这个函数没有参数,但有一个返回值,通常返回的是一个二值信号量的句柄,如果创建成功就返回相应的值,失败就返回NULL。

发送二值信号量

也叫释放二值信号量,其函数为xSemaphoreGive(),该函数原型为:

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)

参数xSemaphore为需要发送或释放的信号量句柄,这里我们填入上面定义好的二值信号量句柄。同时,该函数还有一个返回值,返回的类型是BaseType_t,通常是一个整数类型。如果返回值为pdTRUE(在freertos中通常定义为1),则表示信号量发送或释放成功。如果返回值为pdFALSH(在freertos中通常定义为0),则表示发送或释放失败。

获取二值信号量

有发送就有获取,其函数为xSemaphoreTake(),该函数原型为:

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

参数xSemaphore跟上面的一样,填入定义好的句柄即可。参数xTicksToWait为任务等待信号量的最大时间,可根据需要选择,通常为portMAX_DELAY。该函数还有一个返回值,跟上面一样,这里就跳过了。

二值信号量示例代码

#include "myfreertos.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "Usart.h"
#include "oled.h"
#include "Task.h"
#include "led.h"
#include "key.h"TaskHandle_t MyTaskHandler;//任务句柄TaskHandle_t MyTask1Handler;//任务1句柄TaskHandle_t SendTask_Handler;  //发送消息句柄TaskHandle_t ReceiveTask_Handler;//接收消息句柄void MyTask(void *pvParameters);    //声明启动函数void MyTask1(void *pvParameters);   //声明任务1函数void Send_task(void *pvParameters);  //声明发送消息函数void Receive_task(void *pvParameters);  //声明接收消息函数SemaphoreHandle_t BinarySem_Handle =NULL; //二值信号量句柄void Start_Task(void)
{xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务vTaskStartScheduler();//启动任务调动		
}void MyTask(void *arg)            //开始创建任务函数
{taskENTER_CRITICAL();           //进入临界区	/* 创建二值信号量 BinarySem */BinarySem_Handle = xSemaphoreCreateBinary();	xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//动态方法创建任务1xTaskCreate(Receive_task,"Receive_task",50,NULL,3,&ReceiveTask_Handler);//创建接收消息任务xTaskCreate(Send_task,"Send_task",50,NULL,4,&SendTask_Handler);  //创建发送消息任务vTaskDelete(MyTaskHandler);    //删除开始任务taskEXIT_CRITICAL();           //退出临界区
}void MyTask1(void *arg)     //任务1函数体
{while(1){OLED_ShowString(1,1,"Runing Task Led");GPIO_ResetBits(GPIOC,GPIO_Pin_13);vTaskDelay(300);GPIO_SetBits(GPIOC,GPIO_Pin_13);vTaskDelay(900);}	
}//接收任务函数
void Receive_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while(1){		      //获取二值信号量 xSemaphore,没获取到则一直等待xReturn = xSemaphoreTake(BinarySem_Handle,100); /* 等待时间 */if(pdTRUE == xReturn)OLED_ShowString(3,1,"Receive_OK");elseOLED_ShowString(3,1,"Receive_NO");vTaskDelay(200);}
}//发送任务函数
void Send_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while(1){if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==1){xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量if( xReturn == pdTRUE )OLED_ShowString(2,1,"Send_OK");}else{OLED_ShowString(2,1,"Send_NO");}vTaskDelay(200);}
}

计数信号量

创建计数信号量

跟二值信号量一样,先定义一个计数信号量句柄并给其赋初值为NULL:

SemaphoreHandle_t CountSem_Handle =NULL; //计数信号量句柄

xSemaphoreCreateCounting()是FreeRTOS中用于创建计数信号量的函数,其函数原型为:

SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount
)

参数uxMaxCount是计数信号量的最大计数值,当信号量值达此值时,不能再通过xSemaphoreGive增加其值。参数uxInitialCount为信号量的初始计数值。该函数还有一个返回值,如果成功创建信号量,就返回信号量的句柄,失败则返回NULL。

发送计数信号量与获取计数信号量

步骤都跟二值信号量大致相同,这里就不过多论述。

计数信号量示例代码

#include "myfreertos.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "Usart.h"
#include "oled.h"
#include "Task.h"
#include "led.h"
#include "key.h"TaskHandle_t MyTaskHandler;//任务句柄TaskHandle_t MyTask1Handler;//任务1句柄TaskHandle_t SendTask_Handler;  //发送消息句柄TaskHandle_t ReceiveTask_Handler;//接收消息句柄void MyTask(void *pvParameters);    //声明启动函数void MyTask1(void *pvParameters);   //声明任务1函数void Send_task(void *pvParameters);  //声明发送消息函数void Receive_task(void *pvParameters);  //声明接收消息函数SemaphoreHandle_t CountSem_Handle =NULL; //计数信号量句柄void Start_Task(void)
{xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务vTaskStartScheduler();//启动任务调动		
}void MyTask(void *arg)            //开始创建任务函数
{taskENTER_CRITICAL();           //进入临界区	/* 创建 CountSem */CountSem_Handle = xSemaphoreCreateCounting(5,5); 	xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//动态方法创建任务1xTaskCreate(Receive_task,"Receive_task",50,NULL,3,&ReceiveTask_Handler);//创建接收消息任务xTaskCreate(Send_task,"Send_task",50,NULL,4,&SendTask_Handler);  //创建发送消息任务vTaskDelete(MyTaskHandler);    //删除开始任务taskEXIT_CRITICAL();           //退出临界区
}void MyTask1(void *arg)     //任务1函数体
{while(1){OLED_ShowString(1,1,"Runing Task Led");GPIO_ResetBits(GPIOC,GPIO_Pin_13);vTaskDelay(300);GPIO_SetBits(GPIOC,GPIO_Pin_13);vTaskDelay(900);}	
}//接收任务函数
void Receive_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while(1){		      //获取计数信号量 xSemaphore,没获取到则一直等待xReturn = xSemaphoreTake(CountSem_Handle,100); /* 等待时间 */if(pdTRUE == xReturn)OLED_ShowString(3,1,"Receive_OK");//elseOLED_ShowString(3,1,"Receive_NO");vTaskDelay(200);}
}//发送任务函数
void Send_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while(1){if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==1){xReturn = xSemaphoreGive( CountSem_Handle );//给出计数信号量if( xReturn == pdTRUE )OLED_ShowString(2,1,"Send_OK");}else{OLED_ShowString(2,1,"Send_NO");}vTaskDelay(200);}
}

二值信号量与计数信号量的区别

二值信号量只有两种状态(0和1),通常用于控制互斥访问,一次只允许一个进程进入临界区。而计数信号量可以有多个值,用于表示资源的数量,允许多个进程同时访问有限数量的资源。

讲解一下上面两个代码的思路

二值信号量

这个我主要是通过按下按键然后去释放二值信号量,按下按键释放二值信号量,然后被接收二值信号量任务接收,该任务就会执行里面的程序,执行完以后退出,二值信号量的信息返回值有由1变0,等待下一次信号的到来。值得注意的是,二值信号量是单次事件,如果需要执行多个事件,执行事件的顺序就从高优先级向低优先级执行,不能同时执行。

计数信号量

跟二值信号量的思路逻辑是一样的,但对于计数信号量而言,它能同时执行多个任务,提高了资源的利用率。

总结

以上是我的个人看法,如有不足,欢迎指出!


文章转载自:

http://EwEYPoi6.gwmjy.cn
http://Q9axuixa.gwmjy.cn
http://AuoT71Iz.gwmjy.cn
http://26eVTk2f.gwmjy.cn
http://WXMRn9JY.gwmjy.cn
http://CXzT0JFZ.gwmjy.cn
http://VIQtQDt2.gwmjy.cn
http://UBygCYnM.gwmjy.cn
http://p5Wy3jL8.gwmjy.cn
http://ji9WbZ86.gwmjy.cn
http://G3Dsyu3u.gwmjy.cn
http://Bt5F0mJP.gwmjy.cn
http://rjnvTD2y.gwmjy.cn
http://ggb4TJHd.gwmjy.cn
http://feXTQvl8.gwmjy.cn
http://UtSkQaEi.gwmjy.cn
http://lYFCevUv.gwmjy.cn
http://DpErvC1F.gwmjy.cn
http://t09rcrki.gwmjy.cn
http://vycnZpzj.gwmjy.cn
http://xpc1fquW.gwmjy.cn
http://84U5lC8D.gwmjy.cn
http://fx9rZ9bF.gwmjy.cn
http://RFrA2FPu.gwmjy.cn
http://1IrjEBH9.gwmjy.cn
http://NsL8MnRa.gwmjy.cn
http://xYCO3mXP.gwmjy.cn
http://rb1yHU7O.gwmjy.cn
http://qGtFCcfl.gwmjy.cn
http://8ZTrevnc.gwmjy.cn
http://www.dtcms.com/a/379874.html

相关文章:

  • 蒸面器/蒸脸仪方案开发,蒸面器/蒸脸仪MCU控制方案分析
  • 容器技术崛起:从PaaS到Docker的变革探问
  • 如何定位Mysql慢查询和短而频的查询
  • 机器学习的基本流程:从数据到模型
  • springboot rabbitmq 消息队列入门与实战
  • 使用vllm部署neo4j的text2cypher-gemma-2-9b-it-finetuned-2024v1模型
  • 栈-844.比较含退格的字符串-力扣(LeetCode)
  • [Dify] HTTP 请求节点详解:如何在 Dify 中配置与调用第三方 API
  • SQL优化简单思路
  • 构建AI智能体:三十一、AI医疗场景实践:医学知识精准问答+临床智能辅助决策CDSS
  • HTTP的Web服务测试在Python中的实现
  • 华为HCIE-云计算培训课程有哪些?
  • 绕过 FlashAttention-2 限制:在 Turing 架构上使用 PyTorch 实现 FlashAttention
  • 美食分享|基于Springboot和vue的地方美食分享网站系统设计与实现(源码+数据库+文档)
  • 华为HICE云计算的含金量高吗?
  • 【算法--链表】146.LRU缓存--通俗讲解
  • 5 绑定表
  • 记录一次利用arthas和skywalking做接口性能优化的全过程
  • 缓存三大劫攻防战:穿透、击穿、雪崩的Java实战防御体系(一)
  • 单轴导纳控制 (Single-Axis Admittance Control) 算法介绍
  • 软考~系统规划与管理师考试——真题篇——章节——第1章 信息系统与信息技术发展——纯享题目版
  • 霸王餐返利app的分布式架构设计:基于事件驱动的订单处理系统
  • Android SystemServer 启动 service源码分析
  • CentOS搭建本地源
  • Python的pip镜像源配置
  • ES6 面试题及详细答案 80题 (55-61)-- 类与继承
  • 云手机在办公领域中自动化的应用
  • Flink面试题及详细答案100道(21-40)- 基础概念与架构
  • 用Python打造专业级老照片修复工具:让时光倒流的数字魔法
  • 第八章:移动端着色器的优化-Mobile Shader Adjustment《Unity Shaders and Effets Cookbook》