FreeRTOS入门教程(事件组概念和函数使用)

news2024/12/28 22:42:51

文章目录

  • 前言
  • 一、事件组概念
  • 二、事件组和信号量,队列的区别
  • 三、事件组相关函数
  • 三、事件组应用示例
    • 1.等待多个事件
    • 2.任务同步
  • 总结


前言

本篇文章将带大家学习什么是事件组以及如何使用事件组。

一、事件组概念

事件组通常是由一组位(bits)组成的数据结构,其中每一位都对应着某个特定的事件。每个位可以被设置或清除,表示相应的事件发生或未发生。这种位的组合形成了一个类似于二进制数的集合,每个位都代表着某个特定的状态或事件。因此,可以将事件组视为由一系列二进制位构成的数据结构,每个位代表着一个独立的事件状态。

事件组中的每一位可以表示一个特定的事件或状态,任务可以根据这些事件或状态来进行相应的处理。通过设置或清除这些位,可以触发或取消相应的事件处理逻辑。事件组的位通常用于指示某些事件是否发生、是否完成或是否需要进一步处理。这种灵活性使得事件组成为一种强大的工具,用于任务间的同步、通信和协作。

在使用事件组时,可以通过操作单独的位来表示不同的事件或状态,从而实现复杂的任务管理和事件处理。通过适当地设置和清除位,可以实现任务间的协同工作,以满足特定的应用需求。事件组的这种位级别的控制使其成为处理任务间通信和同步的有效工具。

在这里插入图片描述

事件组的位数:

如果 configUSE_16_BIT_TICKS 被设置为 1(启用),则事件组有16位,其中最高的8位(从最高位到第9位)用于内核管理,而剩下的低8位留给用户自定义事件。

如果 configUSE_16_BIT_TICKS 被设置为 0(禁用),则事件组有32位,其中最高的8位(从最高位到第25位)用于内核管理,而剩下的低24位留给用户自定义事件。

二、事件组和信号量,队列的区别

1.事件组(Event Group):

概念:事件组是由多个位组成的数据结构,用于表示多个事件的状态。任务可以等待事件组中的特定位或位组合被设置,从而实现任务同步。

用途:事件组常用于任务间的事件通知和同步。任务可以等待多个事件中的任意一个或多个同时发生,或者等待所有事件都发生后再继续执行。

特点:事件组允许任务等待多个事件,提供了更灵活的同步机制。

2.信号量(Semaphore):

概念:信号量是一个计数器,用于控制多个任务对共享资源的访问。信号量的值表示可用资源的数量,任务可以请求或释放资源,信号量负责追踪可用资源的数量。

用途:信号量常用于控制共享资源的访问,防止多个任务同时访问共享资源,以避免竞争条件(Race Condition)。

特点:信号量是一个计数器,可以用于控制资源的数量和分配。

2.队列(Queue):

概念:队列是一个数据结构,用于在任务间传递数据。任务可以将数据发送到队列,另一个任务则可以从队列中接收这些数据。

用途:队列常用于任务间的数据传递,允许任务异步地发送和接收数据,实现解耦和通信。

特点:队列是一个数据缓冲区,可以用于存储和传递数据,任务间的数据传递是异步的。

区别总结:

事件组用于任务间的事件通知和同步,任务可以等待多个事件的发生。

信号量用于控制对共享资源的访问,防止多个任务同时访问资源。

队列用于任务间的数据传递,任务可以异步地发送和接收数据。

三、事件组相关函数

xEventGroupCreate() - 创建事件组

EventGroupHandle_t xEventGroupCreate(void);

参数:无
返回值:事件组的句柄 (EventGroupHandle_t)
意义:用于创建一个事件组。该函数会返回一个事件组的句柄,以供后续操作。

xEventGroupSetBits() - 设置事件组位

EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);

参数:
xEventGroup:事件组的句柄
uxBitsToSet:要设置的位,通常使用位掩码来表示要设置的事件位
返回值:设置后的事件组状态,包括所有事件位的当前状态
意义:用于设置事件组的一个或多个位,表示事件发生。可以使用位掩码将要设置的位传递给该函数。

xEventGroupClearBits() - 清除事件组位

EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear);

参数:
xEventGroup:事件组的句柄
uxBitsToClear:要清除的位,通常使用位掩码来表示要清除的事件位
返回值:清除后的事件组状态,包括所有事件位的当前状态
意义:用于清除事件组的一个或多个位,表示事件未发生。可以使用位掩码将要清除的位传递给该函数。

xEventGroupWaitBits() - 等待事件组位的设置

EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, BaseType_t xClearOnExit, BaseType_t xWaitForAllBits, TickType_t xTicksToWait);

参数:
xEventGroup:事件组的句柄
uxBitsToWaitFor:等待的位,通常使用位掩码来表示要等待的事件位
xClearOnExit:指定是否在退出时清除等待的位(可选参数)
xWaitForAllBits:指定是否等待所有位都被设置(可选参数)
xTicksToWait:等待的最大时间(以时钟节拍为单位),如果设置为 0,则将一直等待
返回值:返回事件组的当前状态,包括所有事件位的当前状态
意义:用于等待事件组中的特定位被设置。根据参数的设置,可以选择等待所有位都被设置或只等待其中之一被设置,还可以选择在等待完成后清除等待的位。

xEventGroupSync() 函数用于同步多个任务的操作,使它们能够在相同的时间点上等待一组特定的事件。它通过等待事件组的特定位或位组合被设置来实现同步。

EventBits_t xEventGroupSync(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet,
    const EventBits_t uxBitsToWaitFor,
    TickType_t xTicksToWait
);

参数:

xEventGroup:要同步的事件组的句柄。
uxBitsToSet:需要设置的位,通常使用位掩码来表示要设置的事件位。
uxBitsToWaitFor:需要等待的位,通常使用位掩码来表示要等待的事件位。
xTicksToWait:等待的最大时间(以时钟节拍为单位),如果设置为 0,则将一直等待。
返回值:

该函数返回事件组的当前状态,包括所有事件位的当前状态。

意义:

xEventGroupSync() 函数的目的是等待指定的位被设置,并在这些位被设置时进行同步。

当调用 xEventGroupSync() 函数时,调用任务将等待事件组中的指定位被设置,同时设置另一组位。一旦事件组中的所有指定位都被设置,调用任务将继续执行。

如果超过指定的等待时间(由 xTicksToWait 参数确定),但事件组中的指定位仍未被设置,调用任务将根据超时行为采取相应措施。

通过 xEventGroupSync() 函数,你可以实现任务间的同步操作,确保多个任务在特定事件发生时同时执行。这对于某些需要多任务协作的场景非常有用,例如在任务需要同时开始执行某个操作时,或者需要等待某些条件满足后再执行后续操作时。

三、事件组应用示例

1.等待多个事件

下面的代码创建了三个任务,分别是洗菜,生火,炒菜三个任务,炒菜任务需要等待洗菜和生火两个任务完成后才能够继续执行,否则就阻塞无法执行。

/* 事件组句柄 */
EventGroupHandle_t xEventGroup;

/* bit0: 洗菜
 * bit1: 生火
 * bit2: 炒菜
 */
#define WASHING  (1<<0)
#define FIRING   (1<<1)
#define COOKING  (1<<2)

static void vWashingTask( void *pvParameters )
{
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		printf("I am washing %d time....\r\n", i++);
		
		/* 发出事件: 我洗完菜了 */
		xEventGroupSetBits(xEventGroup, WASHING);	
		
		/* 等待大厨炒完菜, 再继续洗菜 */
		xEventGroupWaitBits(xEventGroup, COOKING, pdTRUE, pdTRUE, portMAX_DELAY);
	}
}

static void vFiringTask( void *pvParameters )
{
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		/* 等待洗完菜, 才生火 */
		xEventGroupWaitBits(xEventGroup, WASHING, pdFALSE, pdTRUE, portMAX_DELAY);
		
		printf("I am firing %d time....\r\n", i++);

		/* 发出事件: 我生好火了 */
		xEventGroupSetBits(xEventGroup, FIRING);			
	}
}

static void vCookingTask( void *pvParameters )
{
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		/* 等待2件事: 洗菜, 生火 */
		xEventGroupWaitBits(xEventGroup, WASHING|FIRING, pdTRUE, pdTRUE, portMAX_DELAY);
		
		printf("I am cooking %d time....\r\n", i++);
		
		/* 发出事件: 我炒好菜了 */
		xEventGroupSetBits(xEventGroup, COOKING);			
	}
}

//创建事件组
xEventGroup = xEventGroupCreate( );

/* 创建3个任务: 洗菜/生火/炒菜
*/
xTaskCreate( vWashingTask, "Task1", 1000, NULL, 1, NULL );
xTaskCreate( vFiringTask,  "Task2", 1000, NULL, 2, NULL );
xTaskCreate( vCookingTask, "Task3", 1000, NULL, 3, NULL );


2.任务同步

#include <FreeRTOS.h>
#include <task.h>
#include <event_groups.h>

// 定义事件组句柄
EventGroupHandle_t xEventGroup;

// 生产者任务
void vProducerTask(void *pvParameters) {
    // 生产数据过程
    for (int i = 1; i <= 5; i++) {
        // 生产数据,假设为整数 i
        printf("Producing data: %d\n", i);
        xEventGroupSetBits(xEventGroup, 0x01); // 设置位0(表示数据可用)
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 模拟生产数据需要时间
    }
}

// 消费者任务
void vConsumerTask(void *pvParameters) {
    // 等待数据可用
    xEventGroupSync(
        xEventGroup,
        0x01,     // 设置位0,表示数据可用
        0x01,     // 等待位0被设置
        portMAX_DELAY  // 无限等待
    );

    // 开始处理数据
    printf("Consumer task started!\n");
    while (1) {
        // 处理数据
        printf("Consuming data\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 模拟处理数据需要时间
    }
}

int main(void) {
    // 创建事件组
    xEventGroup = xEventGroupCreate();

    // 创建生产者任务
    xTaskCreate(vProducerTask, "Producer", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    // 创建消费者任务
    xTaskCreate(vConsumerTask, "Consumer", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

    // 启动调度器
    vTaskStartScheduler();

    // 这里不会执行,因为任务是永远不会返回的
    return 0;
}

总结

本篇文章主要给大家讲解了事件组的概念和具体的函数和使用方法。

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

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

相关文章

CSS页面基本布局

前提回顾 1. 超文本标记语言&#xff08;HTML&#xff09;是一种标记语言&#xff0c;用来结构化我们的网页内容并赋予内容含义&#xff1b; &#xff08;超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language /ˈhaɪpətekst ˈmɑːkʌp ˈlŋɡwɪdʒ /…

Linux小技巧之awk必知必会

Linux Shell 三剑客之一&#xff0c;废话不多说直接上干货。 目录 1. 指定分隔符2.指定打印行数3.打印最后一列4.打印倒数第二列5.匹配输出6.if 判断7.统计列总数值8.时间格式转换 示例数据&#xff1a; 1. 指定分隔符 awk -F | {print $1} log.txt # awk -F | {print $1}…

STM32MP135和STM32MP157的区别

本文介绍了STMicroelectronics公司推出的两款多核处理器STM32MP135和STM32MP157之间的区别&#xff0c;包括主频、集成硬件模块数量、内存大小和电压调节模块等方面。 STMicroelectronics是一家领先的半导体解决方案提供商&#xff0c;在嵌入式系统领域有着丰富的经验。他们…

序列合并

题目描述 有两个长度为 N 的单调不降序列 A,B&#xff0c;在 A,B 中各取一个数相加可以得到 个和&#xff0c;求这 个和中最小的 个。 输入格式 第一行一个正整数 N&#xff1b; 第二行 N 个整数 1…N​。 第三行 N 个整数 1…N​。 输出格式 一行 N 个整数&#xff…

从头开始使用 KNN 进行 KNN 和 MNIST 手写数字识别的初学者指南

坦维佩努穆迪 Kaggle参考&#xff1a; MNIST Perfect 100% using kNN | Kaggle 一、说明 MNIST &#xff08;“修改后的国家标准与技术研究所”&#xff09;是事实上的计算机视觉“hello world”数据集。自 1999 年发布以来&#xff0c;这个经典的手写图像数据集一直作为分类算…

编写内联函数求解 2x²+4x+5的值,并用主函数调用该函数

动态内存分配可以根据实际需要在程序运行过程中动态地申请内存空间,这种内存空间的分配和释放是由程序员自己管理的,因此也被称为手动内存分配。 C++ 中,动态内存的分配和释放是通过 new 和 delete 操作符进行的。new 操作符用于在堆内存上为对象动态分配空间,dele…

Python之哈希表-哈希表原理

Python之哈希表-哈希表原理 集合Set 集合&#xff0c;简称集。由任意个元素构成的集体。高级语言都实现了这个非常重要的数据结构类型。Python中&#xff0c;它是可变的、无序的、不重复的元素的集合 初始化 set() -> new empty set objectset(iterable) -> new set …

Docker——如何自定义镜像【将自己的项目制作成镜像】?

目录 前言&#xff1a;我们以前是如何部署项目的&#xff1f; 1、镜像由哪几部分构成的 2、如何手动自定义一个镜像 2.1、Dockerfile 2.2、dockerfile文本文件中&#xff0c;最终要写什么&#xff1f; 2.3、构建镜像 3、案例&#xff1a;部署java项目 4、如何与其他容器…

Python 机器学习入门之ID3决策树算法

系列文章目录 第一章 Python 机器学习入门之线性回归 第一章 Python 机器学习入门之梯度下降法 第一章 Python 机器学习入门之牛顿法 第二章 Python 机器学习入门之逻辑回归 番外 Python 机器学习入门之K近邻算法 番外 Python 机器学习入门之K-Means聚类算法 第三章 Python 机…

华为云HECS云服务器docker环境下安装nacos

华为云HECS云服务器&#xff0c;安装docker环境&#xff0c;查看如下文章。 华为云HECS安装docker-CSDN博客 一、拉取镜像 docker pull nacos/nacos-server二、宿主机创建挂载目录 执行如下命令&#xff1a; mkdir -p /usr/local/nacos/logs mkdir -p /usr/local/nacos/con…

服务端监控要怎么做?

目录 前言 一、Google的四类黄金指标 二、RED方法 三、USE方法 RED方法 vs USE方法 四、监控指标 WEB服务监控 MySQL数据库监控 QPS TPS 最大连接数 缓存监控 总结 前言 众所周知&#xff0c;业界各种大中型软件系统在生产运行时&#xff0c;总会有一些手段来…

ESP32C3 LuatOS TM1650②动态显示累加整数

--注意:因使用了sys.wait()所有api需要在协程中使用 -- 用法实例 PROJECT "ESP32C3_TM1650" VERSION "1.0.0" _G.sys require("sys") local tm1650 require "tm1650"-- 拆分整数&#xff0c;并把最低位数存放在数组最大索引处 loc…

车载网关通信能力解析——SV900-5G车载网关推荐

随着车联网的发展,各类车载设备对车载网关的需求日益增长。车载网关作为车与车、车与路、车与云之间连接的关键设备,其通信能力直接影响整个系统的性能。本文将详细解析车载网关的通信能力,并推荐性价比高的SV900-5G车载网关。 链接直达&#xff1a;https://www.key-iot.com/i…

类与对象-对象特性-构造函数与析构函数

#include<iostream> using namespace std; class Person { public: //构造函数 : //没有返回值,不写void //函数名与类名相同 //构造函数可以有参数,能发生重载 //创建对象时,构造函数会自动调用,而且只调用一次 Person() { cout << &quo…

哈里斯鹰算法优化BP神经网络(HHO-BP)回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Python爬虫:ad广告引擎的模拟登录

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

Ubuntu桌面环境的切换方法

你在找它吗&#xff1f; 国内麒麟、深度等系统虽然界面更炫&#xff0c;但——软件仓库与Ubuntu官方已不兼容。国内系统遇到稳定性问题&#xff0c;还是得拿Ubuntu做参照。今天本来介绍下这款Linux桌面。 为什么在 Ubuntu 上考虑 LXQt&#xff1f; 性能&#xff1a;LXQt设计为…

基于SSM的教务管理系统运行教程

文章目录 1、前期必备1.1、所需软件版本说明1.2、下载源码1.3、下载开发工具1.4、下载JDK并配置环境变量1.5、安装数据库和数据库管理工具1.6、安装配置Maven 2、将SQL文件导入到数据库2.1、新建MySQL连接2.2、新建数据库并导入SQL 3、用Eclipse运行程序3.1、导入educationalMa…

操作系统【OS】操作系统的引导

激活CPU。 激活的CPU读取ROM中的boot程序&#xff0c;将指令寄存器置为BIOS(基本输入输出系统)的第一条指令&#xff0c; 即开始执行BIOS的指令。硬件自检。 启动BIOS程序后&#xff0c;先进行硬件自检&#xff0c;检查硬件是否出现故障。如有故障&#xff0c;主板会发出不同含…

【JavaEE】Java多线程编程案例 -- 多线程篇(3)

Java多线程编程案例 1. 单例模式1.1 代码的简单实现1.2 懒汉模式的线程安全代码 2. 阻塞队列2.1 阻塞队列的概念2.2 使用库中的BlockingDeque2.3 模拟实现阻塞队列2.4 生产者消费者模型 3. 定时器3.1 概念3.2 使用库的定时器 - Timer类3.3 模拟实现定时器 4. 线程池4.1 概念4.2…