【FreeRTOS(二)】FreeRTOS新手入门——计数型信号量和二进制信号量的基本使用并附代码解析

news2024/10/6 1:40:20

写在前面:
本文章如有错漏之处,敬请指正,另外本文为网络材料整理,侵删。

FreeRTOS信号量的基本使用&代码解析

    • 一、信号量概述
    • 二、计数型信号量
    • 三、二进制信号量
    • 四、信号量函数API
    • 1、创建信号量
    • 2、删除一个信号量
    • 3、信号量释放
    • 4、信号量获取
    • 五、示例代码
    • 1、使用二进制信号量来同步
    • 2、使用计数型信号量

一、信号量概述

信号量(Semaphore):常用于任务的同步,通过该信号,就能够控制某个任务的执行。
信号量分两种:二进制信号量和计数型信号量。

二、计数型信号量

计数型信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目,访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。
例如:某个资源限定只能有3个任务访问,那么第4个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务1)释放掉该资源的时候,第4个任务才能获取到信号量从而进行资源的访问。其运行机制具体见下图
在这里插入图片描述
再举个形象点的例子:
有三个停车位,我停车,停车位-1,我开车走了,停车位+1。申请资源就是P操作,释放资源就是V操作
在这里插入图片描述

三、二进制信号量

二进制信号量和计数型信号量的唯一区别就是计数值的最大值被限定为1。
要想访问资源需要先“take”信号量,让计数值减1,用完资源后,“give”信号量,让计数值加1
在这里插入图片描述

四、信号量函数API

1、创建信号量

使用信号量之前,要先创建,得到一个句柄,使用信号量时,要使用句柄来表明使用哪个信号量。
二进制信号量和计数型信号量创建函数不同
创建二进制信号量函数原型:

静态创建:
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );
动态创建:
SemaphoreHandle_t xSemaphoreCreateBinary( void );

创建计数型信号量函数原型:

静态创建:
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, 
UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer );
uxMaxCount: 最大计数值 
uxInitialCount: 初始计数值
pxSemaphoreBuffer: StaticSemaphore_t 结构体指针 
返回值: 返回句柄,非NULL表示成功

动态创建:
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, 
UBaseType_t uxInitialCount);

2、删除一个信号量

vSemaphoreDelete()用于删除一个信号量,包括二进制信号量、计数信号量、互斥量和递归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。

void vSemaphoreDelete(SemaphoreHandle_t xSemaphore)
xSemaphore——信号量句柄

3、信号量释放

xSemaphoreGive()是一个用于释放信号量的宏,真正的实现过程是调用消息队列通用发送函数。释放的信号量对象必须是已经被创建的。可以用于二进制信号量、计数型信号量、互斥信号量的释放。

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

4、信号量获取

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, 
TickType_t xTicksToWait);

xTicksToWait——如果无法马上获得信号量,阻塞一会。0不阻塞,马上返回

五、示例代码

1、使用二进制信号量来同步

创建两个任务,一个用于释放信号量,一个用于获取信号量。

#include "FreeRTOS.h"
#include "task.h"
#include "xsc_test.h"
#include <stdio.h>
#include <kernel/dpl/ClockP.h>


StackType_t xscStack1[1024] __attribute__((aligned(32)));
StackType_t xscStack2[1024] __attribute__((aligned(32)));
StaticTask_t    xscTaskObj1;
StaticTask_t    xscTaskObj2;

void vSendTask(void *pvParameters)
{
    int cnt_ok = 0;
    int cnt_err = 0;
    const TickType_t xTicksToWait = pdMS_TO_TICKS(10UL);

    while (1)
    {
        for (int i = 0; i < 3; i++)
        {
            if (xSemaphoreGive(xBinarySemaphore) == pdTRUE)
            {
                DebugP_log("Give xBinarySemaphore %d time: OK\r\n", cnt_ok++);
            }
            else
            {
                DebugP_log("Give xBinarySemaphore %d time: ERR\r\n", cnt_err++);
            }
        }
        vTaskDelay(xTicksToWait);
    }
}

void vRecvTask(void *argc)
{
    int cnt_ok = 0;
    int cnt_err = 0;
    while (1)
    {
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE)
        {
            DebugP_log("Get xBinarySemaphore OK:%d\r\n", cnt_ok++);
        }
        else
        {
            DebugP_log("Get xBinarySemaphore ERR:%d\r\n", cnt_err++);
        }
    }
}

//Xsc_TestInit这个函数我在main函数调用了,就相当于main函数就行
void Xsc_TestInit(void)
{
    //创建二进制信号量
    xBinarySemaphore = xSemaphoreCreateBinary();    
    if (xBinarySemaphore != NULL)
    {
        //释放信号量
        xTaskCreateStatic(vSendTask, "vSendTask", 1024, NULL, 2, xscStack1, &xscTaskObj1);
        //获取信号量
        xTaskCreateStatic(vRecvTask, "vRecvTask", 1024, NULL, 1, xscStack2, &xscTaskObj2);
    }
    else
    {
        DebugP_log("xSemaphoreCreateBinary Failed!\r\n ");
    }
}


代码解释:
连续三次释放信号量,只有第一次能成功,然后发送任务阻塞,接收任务执行,获得信号量阻塞。在vTaskDelay退出之前都是运行的空闲任务。发送任务再次执行,连续三次释放信号量,只有第一次能成功,发送任务再阻塞,接受任务唤醒,打印OK。
demo演示:
在这里插入图片描述

2、使用计数型信号量

创建了一个计数型信号量,最大计数值为3,初始值为0,然后创建两个任务,一个用于释放信号量,一个用于获取信号量。

#include "FreeRTOS.h"
#include "task.h"
#include "xsc_test.h"
#include <stdio.h>
#include <kernel/dpl/ClockP.h>


StackType_t xscStack1[1024] __attribute__((aligned(32)));
StackType_t xscStack2[1024] __attribute__((aligned(32)));
StaticTask_t    xscTaskObj1;
StaticTask_t    xscTaskObj2;

void vSendTask(void *pvParameters)
{
    int cnt_ok = 0;
    int cnt_err = 0;
    const TickType_t xTicksToWait = pdMS_TO_TICKS(10UL);

    while (1)
    {
        for (int i = 0; i < 4; i++)
        {
            
            if (xSemaphoreGive(xCountingSemaphore) == pdTRUE)
            {
                DebugP_log("Give xCountingSemaphore %d time: OK\r\n", cnt_ok++);
            }
            else
            {
                DebugP_log("Give xCountingSemaphore %d time: ERR\r\n", cnt_err++);
            }
        }
        vTaskDelay(xTicksToWait);
    }
}

void vRecvTask(void *argc)
{
    int cnt_ok = 0;
    int cnt_err = 0;
    while (1)
    {
        if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE)
        {
            DebugP_log("Get xCountingSemaphore OK:%d\r\n", cnt_ok++);
        }
        else
        {
            DebugP_log("Get xCountingSemaphore ERR:%d\r\n", cnt_err++);
        }
    }
}

//Xsc_TestInit这个函数我在main函数调用了,就相当于main函数就行
void Xsc_TestInit(void)
{
    //创建计数型信号量
    xCountingSemaphore = xSemaphoreCreateCounting(3, 0);
    if (xCountingSemaphore != NULL)
    {
        //释放信号量
        xTaskCreateStatic(vSendTask, "vSendTask", 1024, NULL, 2, xscStack1, &xscTaskObj1);
        //获取信号量
        xTaskCreateStatic(vRecvTask, "vRecvTask", 1024, NULL, 1, xscStack2, &xscTaskObj2);
    }
    else
    {
        DebugP_log("xSemaphoreCreateBinary Failed!\r\n ");
    }
}


代码解释:
发送任务连续释放4个信号量,只有前面3次成功,第4次失败,阻塞,接受任务唤醒,得到三个信号量,阻塞。发送任务再次执行。
demo演示:
在这里插入图片描述

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

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

相关文章

ASP.NET动态Web开发技术第5章

第5章数据验证一.预习笔记 1.验证控件概述&#xff1a; 2.RequiredFieldValidator&#xff08;必填验证&#xff09; 常用属性1&#xff1a;ControlToValidator:被验证的输入控件的ID 常用属性2&#xff1a;Text&#xff1a;验证失败时&#xff0c;验证控件显示的文本 常用…

8.3 总体分布的假设检验

学习目标&#xff1a; 如果我要学习总体分布的假设检验&#xff0c;我会采取以下步骤&#xff1a; 掌握基础概念&#xff1a;学习和掌握统计学中基础的概念&#xff0c;如总体、样本、假设检验、p值等等。 学习检验方法&#xff1a;了解和学习不同的总体分布假设检验方法&…

亚信科技AntDB数据库荣膺第十二届数据技术嘉年华(DTC 2023)“最具潜力数据库”大奖

近日&#xff0c;亚信科技AntDB数据库产品在第十二届数据技术嘉年华&#xff08;DTC 2023&#xff09;峰会上斩获“2022年度最具潜力数据库”大奖。亚信安慧副总裁张桦先生受邀参会&#xff0c;并发表了《AntDB数据库通信行业核心系统应用与创新》的主题演讲&#xff0c;分享了…

vue实现好看的相册、图片网站

目录 一、效果图 1.项目访问地址 2.画虫官方效果图&#xff1a; 3.作者实现的效果图&#xff1a; 二、代码实现 1.项目结构截图 2.路由配置代码&#xff1a; 3. 头部底部主页面内容显示容器的代码 4.首页&#xff0c;即标签页的代码 三、项目启动说明 四、总结 一、…

Android---MVC/MVP/MVVM的演进

目录 一个文件打天下 一个文件--->MVC MVC--->MVP MVP--->MVVM 6大设计原则 完整demo 我们通过"#字棋"游戏来展现MVC-->MVP-->MVVM 之间的演进 一个文件打天下 数据、视图以及逻辑都放在一个 class 里面。而一个 class 里最多 500 行代码&…

springboot 密码加密

首先介绍一下jasypt的使用方法 版本对应的坑 使用的时候还是遇到一个坑&#xff0c;就是jasypt的版本与spring boot版本存在对应情况。可以看到jasypt是区分java7和java8的&#xff0c;也存在依赖spring版本的情况。 自己尝试了一下 在使用jasypt-spring-boot-starter的前提…

优思学院|职场达人有什么晋升秘诀?

作为职场人士&#xff0c;升职晋升是我们一直追求的目标。然而&#xff0c;在职场中&#xff0c;竞争是激烈的&#xff0c;只有那些真正做到了突出表现和积极进取的人才能获得晋升机会。这里将分享七个职场达人的晋升秘诀&#xff0c;希望对那些正在寻找升职机会的人有所帮助。…

Python圈的普罗米修斯——一套近乎完善的监控系统

文章目录前言一、怎么采集监控数据&#xff1f;二、采集的数据结构与指标类型2.1 数据结构2.2 指标类型2.3 实例概念2.4.数据可视化2.5.应用前景总结前言 普罗米修斯(Prometheus)是一个SoundCloud公司开源的监控系统。当年&#xff0c;由于SoundCloud公司生产了太多的服务&…

SQL综合查询下

SQL综合查询下 目录SQL综合查询下18、查询所有人都选修了的课程号与课程名题目代码题解19、SQL查询&#xff1a;查询没有参加选课的学生。题目代码20、SQL查询&#xff1a;统计各门课程选修人数&#xff0c;要求输出课程代号&#xff0c;课程名&#xff0c;有成绩人数&#xff…

Express使用

文章目录Express 使用概述下载Express简单使用Express 生成器安装生成器使用基本路由使用路由获取请求数据获取路由参数处理请求体设置响应方式一&#xff1a;兼容http模块方式二&#xff1a;express的响应方法其他响应中间件简介全局中间件路由中间件静态资源中间件Router简介…

SkyWalking服务应用

文章目录SkyWalking服务应用案例准备案例实施1.部署Elasticsearch服务2.部署SkyWalking OAP服务3.部署SkyWalking UI服务4.搭建并启动应用商城服务SkyWalking服务应用 案例准备 节点规划 IP主机名节点192.168.100.10node-1Skywalking实验节点192.168.100.20mall商城搭建节点…

【毕业设计】基于程序化生成和音频检测的生态仿真与3D内容生成系统----程序化生成地形算法设计

2 程序化生成地形算法设计 2.1 地形曲线的生成 2.1.1 初始化高度场 struct Make2DGridPrimitive : INode {virtual void apply() override {size_t nx get_input<NumericObject>("nx")->get<int>();nx std::max(nx, (size_t)1);size_t ny has_in…

适配器详解

目录 1、适配器简介 2、函数对象适配器 ​编辑 3、函数指针作为适配器 ptr_fun ​编辑 4、类中的成员函数作为适配器 mem_fun_ref 5、取反适配器 5.1、not1 一元取反适配器 ​编辑 5.2、not2 二元取反适配器 1、适配器简介 适配器 为算法 提供接口目前的适配器最多能扩…

第一次习题总结

目录 求第K个数 求逆序对的数量 数的三次方根 一维前缀和 二维前缀和&#xff08;子矩阵的和&#xff09; 求第K个数 思路&#xff1a;用快速选择&#xff0c;时间复杂度为O(N) sl和sr是左边和右边数的个数&#xff0c;当k<sl&#xff0c;即倒数第K个数在左边范围内&#x…

【JY】减隔震设计思考:隔震篇

【写在前文】随着隔标颁布&#xff0c;国内外大大小小的地震的经历。越来越多的人重视减隔震分析和设计&#xff0c;也听到不少的疑惑声音&#xff0c;个人也有一点热点问题的感悟与大家分享。在个人看来&#xff1a;建筑减隔震&#xff1a;七分构造三分算&#xff01;特别注意…

[Netty源码] Netty轻量级对象池实现分析 (十三)

文章目录1.对象池技术介绍2.如何实现对象池3.Netty对象池实现分析3.1 Recycler3.2 Handler3.3 Stack3.4 WeakOrderQueue3.5 Link4.总结1.对象池技术介绍 对象池其实就是缓存一些对象从而避免大量创建同一个类型的对象, 类似线程池。对象池缓存了一些已经创建好的对象, 避免需要…

uni-app--》什么是uniapp?如何开发uniapp?

&#x1f3cd;️作者简介&#xff1a;大家好&#xff0c;我是亦世凡华、渴望知识储备自己的一名在校大学生 &#x1f6f5;个人主页&#xff1a;亦世凡华、 &#x1f6fa;系列专栏&#xff1a;uni-app &#x1f6b2;座右铭&#xff1a;人生亦可燃烧&#xff0c;亦可腐败&#xf…

企业电子招投标采购系统源码——功能模块功能描述+数字化采购管理 采购招投标

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

HTTP API接口设计规范

1. 所有请求使用POST方法 使用post&#xff0c;相对于get的query string&#xff0c;可以支持复杂类型的请求参数。例如日常项目中碰到get请求参数为数组类型的情况。 便于对请求和响应统一做签名、加密、日志等处理 2. URL规则 URL中只能含有英文&#xff0c;使用英文单词或…

Docker配置DL envs教程

Docker容器与镜像的区别 Docker镜像类似于虚拟镜像&#xff0c;是一个只读的文件&#xff0c;包括进程需要运行所需要的可执行文件、依赖软件、库文件、配置文件等等。 而容器则是基于镜像创建的进程&#xff0c;可以利用容器来运行应用。 总结来说&#xff0c;镜像只读&#…