FreeRTOS:事件标志组与任务通知
目录
一、事件标志组(Event Groups)
1、事件标志组的特点
2、事件标志组与队列、信号量的区别
3、关键API函数
4、示例代码
5、优缺点
6、总结
二、任务通知(Task Notifications)
1、任务通知的特点
2、关键API函数
3、使用场景
模拟二值信号量
模拟计数型信号量
模拟消息邮箱
4、优缺点
5、小结
三、总结
在FreeRTOS中,事件标志组(Event Groups)和任务通知(Task Notifications)是用于任务间同步和通信的重要机制。它们可以有效地帮助多个任务进行事件响应和通知,从而实现不同任务之间的协作
一、事件标志组(Event Groups)
事件标志组是一个非常强大的同步工具,它允许任务使用标志位的组合来表示多个事件的状态。每个任务可以检查一个或多个事件标志位,从而决定是否继续执行。事件标志组是一种位掩码机制,任务可以使用位操作来设置、清除或检查这些标志。
事件标志组是一种用于任务间同步的机制。它是由多个事件标志组成的一个集合,可以通过设置、清除或等待特定事件标志位来控制任务的执行流。每个事件标志组的每一位标志可以表示一个具体的事件状态,用户可以通过位操作来管理这些事件。
简单来说,事件标志组就像一个整数(无符号的 16 位或 32 位整数),每一位用来表示一个事件的状态。例如,某个任务可能会根据某些事件的发生情况来执行,而这些事件的状态通过事件标志组的各个位进行管理。
1、事件标志组的特点
-
每个位表示一个事件:每一位的状态(0 或 1)表示一个事件是否发生。比如,
bit0可能表示按键是否按下,bit1可能表示是否收到了数据包,等等。 -
事件标志的存储:事件标志组内部使用 16 位或 32 位无符号整数来存储事件标志。在 STM32 中,通常是 32 位无符号整数。事件标志的高 8 位用于控制事件标志组的管理信息,低 24 位用于存储具体的事件标志。因此,一个事件标志组最多可以存储 24 个事件标志。
-
支持任务和中断操作:事件标志组不仅可以在任务中使用,还支持中断处理程序(ISR)操作。
-
等待某个事件或多个事件:任务可以选择等待单个事件的发生,或者等待多个事件的同时发生。并且,任务在等待某个事件的过程中,可以选择是否在事件被清除后继续等待。
-
事件标志的操作:可以设置、清除或等待事件标志,这些操作可以是独立的,也可以是组合的。
-
事件的“广播”作用:与信号量或队列不同,事件标志组可以在事件发生时唤醒多个任务,满足多个任务的条件。
2、事件标志组与队列、信号量的区别
队列和信号量:通常情况下,事件发生时只会唤醒一个任务。队列的数据被读取后就消失,信号量在被获取后会减少。
事件标志组:事件发生时,会唤醒所有符合条件的任务,相当于具有“广播”功能。被唤醒的任务有两个选择:要么继续等待事件,要么清除事件标志,避免任务再次被唤醒。
3、关键API函数
创建一个事件标志组:
xEventGroupCreate()
设置事件标志组中的某些位:
EventBits_t xEventGroupSetBits(
EventGroupHandle_t xEventGroup, // 事件标志组句柄
const EventBits_t uxBitsToSet // 需要设置的事件标志位
);该函数用于设置指定的事件标志位。如果事件发生,指定的标志位将被设置为 1,通知等待该事件的任务。
清除事件标志组中的某些位:
xEventGroupClearBits()
等待某些事件标志位:
EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup, // 事件标志组句柄
const EventBits_t uxBitsToWaitFor, // 需要等待的事件标志
const BaseType_t xClearOnExit, // 退出时是否清除标志
const BaseType_t xWaitForAllBits, // 是否等待所有标志位成立
TickType_t xTicksToWait // 等待超时时间
);
该函数用于等待指定的事件标志位。如果xWaitForAllBits为pdTRUE,则任务会等待所有指定的标志位,否则只等待其中任何一个标志位。如果xClearOnExit为pdTRUE,则在事件满足条件后清除标志位。
同步两个或更多的任务的事件标志:
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup, // 事件标志组句柄
const EventBits_t uxBitsToSet, // 需要设置的标志位
const EventBits_t uxBitsToWaitFor, // 需要等待的标志位
TickType_t xTicksToWait // 等待超时时间
);
该函数用于实现同步操作,它将同时设置指定的事件标志,并等待另一个任务设置指定的标志位。
使用场景:
事件驱动的任务调度:任务根据事件标志位的状态来执行。
任务同步:多个任务需要在某些条件下同时执行。
4、示例代码
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"// 事件标志组句柄
EventGroupHandle_t xEventGroup;// 定义标志位
#define BIT_0 ( 1 << 0 )
#define BIT_1 ( 1 << 1 )// 任务1
void Task1(void *pvParameters) {while (1) {// 设置 BIT_0 标志位xEventGroupSetBits(xEventGroup, BIT_0);vTaskDelay(pdMS_TO_TICKS(1000));}
}// 任务2
void Task2(void *pvParameters) {while (1) {// 等待 BIT_0 被设置EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);if (uxBits & BIT_0) {// 响应事件printf("Task2 received BIT_0 event\n");}}
}int main(void) {// 创建事件标志组xEventGroup = xEventGroupCreate();// 创建任务xTaskCreate(Task1, "Task1", 128, NULL, 2, NULL);xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);// 启动调度器vTaskStartScheduler();for (;;);
}
解释:
xEventGroupSetBits(xEventGroup, BIT_0):任务1通过设置事件标志组中的BIT_0来通知任务2。
xEventGroupWaitBits(xEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY):任务2等待BIT_0标志位被设置,一旦设置,它会执行响应操作。
5、优缺点
优点:
事件标志组允许一个或多个任务等待不同的事件,并且可以组合多个标志位,支持灵活的同步机制。可减少不必要的任务切换,提高系统效率。
缺点:
可能会引入较大的内存开销,尤其是当事件标志组的位数较多时。
6、总结
事件标志组是一个强大的同步工具,适合多个任务需要根据不同事件状态来执行的场景。它提供了位操作、事件等待、事件清除等功能,能够灵活地管理任务间的同步。相比队列和信号量,事件标志组具有“广播”能力,能够同时唤醒多个任务,因此适合需要多个任务响应的场景。使用事件标志组时,开发者可以选择任务在事件发生后是否清除事件标志,确保任务的灵活性。总之,事件标志组是FreeRTOS中非常重要的同步工具,它为任务间通信提供了一种高效且灵活的方式。
二、任务通知(Task Notifications)
任务通知是另一种高效的任务间通信机制,它允许一个任务向另一个任务发送“通知”。与事件标志组不同,任务通知是基于一个简单的计数器或标志位的机制,它能够非常快速地通知任务发生了某个事件。在FreeRTOS中,任务通知是任务间通信的一种轻量级方式,可以用于同步和传递简单的状态信息。任务通知的实现通过任务控制块(TCB)中的
ulNotifiedValue和ucNotifyState成员来管理通知的值和状态。
1、任务通知的特点
-
不覆盖接收任务的通知值:可以选择更新通知值的部分位,而不是完全覆盖。
-
覆盖接收任务的通知值:可以选择完全覆盖接收任务的通知值。
-
更新通知值的某些位:类似于事件标志组,可以只更新通知值中的某些特定位。
-
增加接收任务的通知值:模拟计数信号量的行为,允许通知值递增。
-
类似队列、事件标志组和信号量:任务通知可以模拟二值信号量、计数型信号量、消息邮箱、事件标志组等多种功能。
2、关键API函数
xTaskNotifyGive():发送通知给任务。
xTaskNotifyWait():等待通知。
xTaskNotify():通知一个任务,并可以附加一个值。
xTaskNotifyAndQuery():向任务发送通知,并带上通知值,同时查询任务的当前通知值。
ulTaskNotifyTake():接收任务的通知值,适用于信号量的模拟。接收到通知后,通知值会被清零。
3、使用场景
轻量级任务间通信:任务通知通常用于需要简单同步或触发任务执行的场景。
不需要复杂状态控制的任务间同步:如果你只需要通知另一个任务某个事件发生,任务通知提供了一个非常快速和高效的机制。
模拟二值信号量
二值信号量通常用于同步任务,确保一个任务在执行时,另一个任务处于等待状态。在FreeRTOS中,任务通知可以用来模拟二值信号量。实现过程如下:
任务1:调用 xTaskNotifyGive() 向任务2发送通知,模拟释放信号量。
任务2:调用 ulTaskNotifyTake() 来等待信号量的获取,若信号量可用,则继续执行。
void Task1(void *pvParameters) {while (1) {// 模拟释放二值信号量xTaskNotifyGive(xTask2Handle);vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task2(void *pvParameters) {while (1) {// 模拟获取二值信号量ulTaskNotifyTake(pdTRUE, portMAX_DELAY);// 执行任务2}
}
模拟计数型信号量
计数型信号量允许多个任务共享资源。任务通知可以通过增加通知值来模拟计数信号量的行为。例如:
任务1:通过 xTaskNotifyGive() 向任务2发送通知,模拟计数信号量的增加。
任务2:通过 ulTaskNotifyTake() 等函数获取信号量。
void Task1(void *pvParameters) {while (1) {// 模拟计数型信号量的增加xTaskNotifyGive(xTask2Handle);vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task2(void *pvParameters) {while (1) {// 获取计数信号量ulTaskNotifyTake(pdTRUE, portMAX_DELAY);// 执行任务2}
}
模拟消息邮箱
任务通知也可以用来模拟消息邮箱。通过向任务发送带有数据的通知值,任务可以接收数据并进行处理。例如:
任务1:发送带有数据的通知(例如通过 xTaskNotify())。
任务2:接收通知并处理数据。
void Task1(void *pvParameters) {while (1) {uint32_t message = 42; // 模拟消息xTaskNotify(xTask2Handle, message, eSetValueWithOverwrite);vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task2(void *pvParameters) {while (1) {uint32_t receivedMessage;xTaskNotifyWait(0, 0, &receivedMessage, portMAX_DELAY);// 处理接收到的消息}
}
4、优缺点
优点:
比较轻量级,开销小,适用于频繁的任务通知。不需要像事件标志组那样维护多个标志位,易于理解和实现。
缺点:
只能用来通知任务,适用场景相对简单,无法像事件标志组那样处理复杂的同步需求。
无法向ISR发送通知:任务通知不能直接从中断服务程序(ISR)中发送。
无法广播给多个任务:任务通知只能通知一个任务,不能像事件标志组那样同时通知多个任务。
无法缓存多个数据:任务通知只保存一个值,无法像队列一样缓存多个数据。
发送受阻不支持阻塞:任务通知的发送不会被阻塞,发送方不能像信号量一样等待资源。
5、小结
任务通知是FreeRTOS中一种非常高效的任务间通信机制。它的优势在于:
低开销:任务通知操作非常高效,内存占用小。
灵活性:可以模拟二值信号量、计数型信号量、消息邮箱和事件标志组等功能。
然而,它也有一些局限性:
无法广播给多个任务:任务通知只能通知一个任务。
无法缓存多个数据:每个任务通知只能携带一个值,不像队列可以缓存多个数据。
综上所述,任务通知是一个非常适合在高效同步和传递简单状态信息的场景中使用的工具,特别是在资源有限的嵌入式系统中。
三、总结
事件标志组:
用于多个任务间的事件同步,可以同时检查多个标志位。
适合需要更复杂事件组合和同步的场景。
支持位操作和多标志组合,灵活性较强。
任务通知:
用于任务间的轻量级通知,适用于简单的同步或触发任务。
速度快,资源占用小,适合高频率的通知场景。
不适合复杂的状态同步和多事件组合。
两者的选择应该根据实际的应用需求来决定。如果是较为复杂的同步需求,事件标志组更为合适;如果仅需要简单的任务通知,则任务通知更为高效。
相关文章:
FreeRTOS:事件标志组与任务通知
目录 一、事件标志组(Event Groups) 1、事件标志组的特点 2、事件标志组与队列、信号量的区别 3、关键API函数 4、示例代码 5、优缺点 6、总结 二、任务通知(Task Notifications) 1、任务通知的特点 2、关键API函数 3、…...
c++11的动态类型
c17引入了any 和 variant,可以将任意数据类型统一用any或variant类型表示,在开发中还是能够带来很多便利的。在c11版本中,可以用下面这个例子,仿照实现一个Any类型。 #include <iostream> #include <stdexcept> #inc…...
付费会员渗透难,腾讯音乐的触顶挑战
腾讯音乐付费用户增长背后,月活跃用户数下滑3%,超级会员渗透率仅1.8%。 转载|原创新熵 作者丨晓伊 编辑丨蕨影 腾讯音乐此次营收同比正增长的到来,殊为不易。要知道,此前已连续四个季度出现营收同比下滑的态势。 11月12日&#x…...
内网安全隧道搭建-ngrok-frp-nps-sapp
1.ngrok 建立内网主机与公网跳板机的连接: 内网主机为客户机: 下载客户端执行 2.frp (1)以下为内网穿透端口转发 frp服务端配置: bindPort 为frp运行端口 服务端运行 ./frps -c frps.ini frp客户端配置…...
Load-Balanced-Online-OJ(负载均衡式在线OJ)
负载均衡式在线OJ 1. 项目介绍2. 项目说明4. 项目代码5. 项目演示 1. 项目介绍 2. 项目说明 4. 项目代码 5. 项目演示...
Postman之变量操作
系列文章目录 1.Postman之安装及汉化基本使用介绍 2.Postman之变量操作 3.Postman之数据提取 4.Postman之pm.test断言操作 5.Postman之newman Postman之变量操作 1.pm.globals全局变量2.pm.environment环境变量3.pm.collectionVariables集合变量4.pm.variables5.提取数据-设置变…...
查找字符串中某个字符返回字符位置
当然有正则表达式就非常简单,没有的话也不用担心,我们自己写算法完成这个功能. 早期的vs版本不支持vs,当然也可以下载boost来实现,关键还是不想下载,那么就自己写吧. 1.要求,查找字符串中同一个字符,并找出字符的位置. 2.根据字符位置,计算出对应的x,y坐标. 算法第一步,查找字…...
《数学物理学报》
作者指南 一、目的与范围 《数学物理学报》以刊登数学与物理科学的边缘学科中具有创造性的代表学科水平的科研成果为主的综合性学术刊物。其目的旨在向专业读者(研究生水平以上)提供数理学科领域的重要的、独创的成果。 二、投稿 要求文章内容具有创新…...
39页PDF | 毕马威_数据资产运营白皮书(限免下载)
一、前言 《毕马威数据资产运营白皮书》探讨了数据作为新型生产要素在企业数智化转型中的重要性,提出了数据资产运营的“三要素”(组织与意识、流程与规范、平台与工具)和“四重奏”(数据资产盘点、评估、治理、共享)…...
K8s 一键部署 MongoDB 的 Replica-Set 和 MongoDB-Express
什么是 MongoDB 副本集? MongoDB 副本集(Replica-Set)是一个分布式数据库系统,它包含一个主节点和多个从节点。主节点负责处理所有写操作,从节点用于读取数据。当主节点发生故障时,从节点可以自动选举一个…...
2024小迪安全基础入门第四课
目录 Web应用&蜜罐系统&堡垒机运维&API内外接口&第三方拓展架构&部署影响 堡垒机的核心功能 堡垒机的优势 API的结构 Web应用&蜜罐系统&堡垒机运维&API内外接口&第三方拓展架构&部署影响 蜜罐(Honeypot)是…...
一文详解使用java easyexcel导出文件的几种情况
情况一:简单的导出指定类型文档,不要求格式 filePath-文件路径// 设置响应头response.setContentType("application/octet-stream");// 字符集处理,确保文件名的正确显示response.setHeader("Content-Disposition","…...
【蓝桥杯C/C++】深入解析I/O高效性能优化:std::ios::sync_with_stdio(false)
博客主页: [小ᶻZ࿆] 本文专栏: 蓝桥杯C/C 文章目录 💯前言💯C 语言与 C 语言的输入输出对比1.1 C 语言的输入输出1.2 C 语言的输入输出 💯 std::ios::sync_with_stdio(false) 的作用与意义2.1 什么是 std::ios::sync_with_st…...
NUXT3学习日记四(路由中间件、导航守卫)
前言 在 Nuxt 3 中,中间件(Middleware)是用于在页面渲染之前或导航发生之前执行的函数。它们允许你在路由切换时执行逻辑,像是身份验证、重定向、权限控制、数据预加载等任务。中间件可以被全局使用,也可以只在特定页…...
数据科学与SQL:组距分组分析 | 区间分布问题
目录 0 问题描述 1 数据准备 2 问题分析 3 小结 0 问题描述 绝对值分布分析也可以理解为组距分组分析。对于某个指标而言,一个记录对应的指标值的绝对值,肯定落在所有指标值的绝对值的最小值和最大值构成的区间内,根据一定的算法&#x…...
odoo18中模型的常用字段类型
字段的公共属性: Char 字符类型,对应数据库中varchar类型,除了通用类型外接收另外两个参数: size: 字符长度,超出的长度将被截断 trim: 默认True,是否字段值应该被去空白。 Text 文本类型,对应数据库…...
【如何用更少的数据作出更好的决策】-gpt生成
如何用更少的数据作出更好的决策 用更少的数据作出更好的决策是一种能力的体现,需要结合有效的方法、严谨的逻辑以及对问题的深刻理解。以下是一些可以帮助你实现这一目标的策略: 明确目标 在收集和分析数据之前,先明确你的决策目标是什么…...
ara::com 与 AUTOSAR 元模型的关系总结
原文链接:AUTOSAR_EXP_ARAComAPI的7章笔记(6) 整体说明 先是表明此前解释 ara::com API 思想和机制时未涉及具体 AP 元模型清单部分,本章旨在阐明 ara::com 与相关元模型部分的关系,且是较高层次的基本理解性介绍&am…...
springboot整合hive
springboot整合hive pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.…...
浅谈 proxy
应用场景 Vue2采用的defineProperty去实现数据绑定,Vue3则改为Proxy,遇到了什么问题? - 在Vue2中不能检测数组和对象的变化 1. 无法检测 对象property 的添加或移除 var vm new Vue({data:{a:1} })// vm.a 是响应式的vm.b 2 // vm.b 是…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
