RT-Thread之事件集使用示例

news/2025/10/27 19:37:59/文章来源:https://www.cnblogs.com/lsksp/p/19169960

一、事件集概述

事件集(Event)是 RT-Thread 中用于线程间同步的轻量级 IPC 机制,核心特性是 “多标志位” 与 “灵活触发”:通过 32 个事件标志位(bit0~bit31)表示不同事件状态,支持线程按 “逻辑与(AND)” 或 “逻辑或(OR)” 关系等待多个事件,实现一对多、多对多的线程同步。

典型应用场景

  • 线程等待多个外部触发条件(如按键按下 + 传感器数据就绪),满足任意条件或全部条件时执行;
  • 状态机切换同步(如设备初始化完成 + 网络连接成功后,触发业务线程启动);
  • 中断与线程间通信(中断服务程序发送事件,线程等待事件处理异步通知)。

二、事件集核心 API 函数

事件集的操作围绕 “创建 / 初始化 / 发送事件 / 接收事件 / 删除 / 脱离” 展开,需区分动态创建(依赖内存堆)与静态初始化(基于全局 / 静态变量),核心函数与参数说明如下。

2.1 事件集的创建与初始化

用于完成事件集的初始化工作,动态创建会从堆中分配内存,静态初始化需提前定义全局 / 静态事件集对象。

2.1.1 动态创建

通过函数从内存堆中分配事件集资源,返回事件集句柄用于后续操作。

rt_event_t rt_event_create(const char *name, rt_uint8_t flag);
  • name:事件集名称。
  • flag:等待队列排序方式,可选值为 RT_IPC_FLAG_FIFO(先进先出)或 RT_IPC_FLAG_PRIO(按线程优先级排序)。

2.1.2 静态初始化

基于已定义的全局 / 静态事件集对象进行初始化,无需分配堆内存。

rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);
  • event:提前定义的全局 / 静态事件集对象(如 struct rt_event event_test)。
  • name:事件集名称,功能同动态创建。
  • flag:等待队列排序方式,功能同动态创建。

2.2 事件的发送与接收

发送事件用于设置事件集中的标志位,接收事件用于等待指定标志位满足条件,支持按逻辑关系触发。

2.2.1 发送事件

设置事件集中的一个或多个标志位,可唤醒等待该事件的线程。

rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
  • event:目标事件集句柄 / 对象。
  • set:待设置的事件标志位(如 0x01 表示 bit0,0x03 表示 bit0 + bit1)。
  • 注意:可在中断服务程序(ISR)中调用,用于通知线程处理异步事件。

2.2.2 接收事件

等待事件集中的指定标志位,满足逻辑关系(AND / OR)时唤醒线程,支持超时机制。

rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved);
  • event:目标事件集句柄 / 对象。
  • set:待等待的事件标志位(如 0x03 表示等待 bit0 或 bit1)。
  • option:对标志位的配置,RT_EVENT_FLAG_AND(全部标志位满足)或 RT_EVENT_FLAG_OR(任意标志位满足),也可以选用RT_EVENT_FLAG_CLEAR,表示接收事件后自动清除已满足的标志位
  • timeout:等待时间(单位:tick),可选值为 RT_WAITING_FOREVER(永久等待)、0(无等待)或具体数值。
  • recved:输出参数,用于返回实际接收到的事件标志位(可设为 RT_NULL)。

2.3 事件集的删除与脱离

用于回收事件集占用的系统资源,动态创建的事件集需删除,静态初始化的事件集需脱离。

2.3.1 动态删除

用于销毁通过 rt_event_create 动态创建的事件集,释放其占用的堆内存。

rt_err_t rt_event_delete(rt_event_t event);
  • event:动态创建的事件集句柄。
  • 适用场景:动态创建的事件集不再使用时,需调用此函数避免内存泄漏。

2.3.2 静态脱离

用于脱离通过 rt_event_init 静态初始化的事件集,仅注销其在系统中的登记信息,不释放内存。

rt_err_t rt_event_detach(rt_event_t event);
  • event:静态初始化的事件集对象。
  • 适用场景:静态事件集不再使用时,需调用此函数释放系统资源(如等待队列)。

三、事件集使用示例

3.1 源代码

#include "thread_task.h"
#include "main.h"
#include <stdio.h>      
#include "rtthread.h"
#include <rthw.h>/******************************************** 线程 1 ******************************************************/
#define THREAD_1_PRIORITY  		4           /* 线程优先级(值越小优先级越高) */
#define THREAD_1_STACK_SIZE		512         /* 线程栈空间大小(单位:字节) */
#define THREAD_1_TIMESLICE		10           /* 线程时间片个数(单位:tick) */
static struct rt_thread *thread_1_handle;    /* 线程句柄 *//******************************************** 线程 2 ******************************************************/
#define THREAD_2_PRIORITY  		5           /* 线程优先级(低于线程1) */
#define THREAD_2_STACK_SIZE		512         /* 线程栈空间大小 */
#define THREAD_2_TIMESLICE		10           /* 线程时间片个数 */
static struct rt_thread *thread_2_handle;    /* 线程句柄 *//* 静态事件集对象(全局定义,提前分配内存) */
struct rt_event event_test;
/* 事件标志位定义:bit0 表示线程1触发事件,bit1 表示线程2触发事件 */
#define EVENT1_FLAG    (1 << 0)  /* 0x01 */
#define EVENT2_FLAG    (1 << 1)  /* 0x02 */
#define EVENT3_FLAG    (1 << 2)  /* 0x04 *//*** @brief  LED闪烁函数(固定闪烁6次,即3个完整周期)* @param  time:每次翻转后的延迟时间(单位:tick)*/
void LED_toggle(uint16_t time)
{   for(uint8_t i = 0; i < 6; i++)  {HAL_GPIO_TogglePin(GPIOC, LED1_Pin);rt_thread_delay(time);      }
}/*** @brief  线程1入口函数(高优先级,接收事件触发LED慢速闪烁)* @param  param:线程参数(未使用,设为RT_NULL)*/
void thread_1_entry(void* param)
{rt_uint32_t recved = 0;while(1){/* 接收事件:等待EVENT1+EVENT2,AND逻辑,永久等待,接收后清除标志位 */if (rt_event_recv(&event_test, EVENT2_FLAG | EVENT1_FLAG , RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,  // 补充RT_EVENT_FLAG_CLEARRT_WAITING_FOREVER, &recved) == RT_EOK){/* 接收到事件后,慢速闪烁LED(0.5Hz) */  // 修正频率描述LED_toggle(1000);rt_event_send(&event_test, EVENT3_FLAG);}}
}/*** @brief  线程2入口函数(低优先级,接收事件触发LED快速闪烁)* @param  param:线程参数(未使用,设为RT_NULL)*/
void thread_2_entry(void* param)
{   rt_uint32_t recved = 0;while(1){/* 接收事件:等待EVENT3_FLAG,AND逻辑,永久等待,接收后清除标志位 */  // 修正笔误if (rt_event_recv(&event_test, EVENT3_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER , &recved) == RT_EOK){rt_event_send(&event_test, EVENT2_FLAG);LED_toggle(100);rt_thread_delay(500); LED_toggle(100);rt_event_send(&event_test, EVENT1_FLAG);}}
}/*** @brief  初始化事件集并创建启动线程(先初始化事件集,再启动线程)*/
void ThreadStart(void)
{rt_base_t level = rt_hw_interrupt_disable();  /* 静态初始化事件集 */rt_event_init(&event_test,            /* 事件集对象 */"event_test",           /* 事件集名称(用于finsh调试) */RT_IPC_FLAG_FIFO        /* 等待队列按FIFO排序 */);rt_event_send(&event_test, EVENT3_FLAG);/* 动态创建并启动线程1 */thread_1_handle = rt_thread_create("thread_1",				/* 线程名称 */thread_1_entry,			/* 线程入口函数 */RT_NULL,				/* 线程参数 */THREAD_1_STACK_SIZE,	/* 线程栈大小 */THREAD_1_PRIORITY,		/* 线程优先级 */THREAD_1_TIMESLICE  	/* 线程时间片 */);if (thread_1_handle != RT_NULL)rt_thread_startup(thread_1_handle);  /* 动态创建并启动线程2 */thread_2_handle = rt_thread_create("thread_2",				/* 线程名称 */thread_2_entry,			/* 线程入口函数 */RT_NULL,				/* 线程参数 */THREAD_2_STACK_SIZE,	/* 线程栈大小 */THREAD_2_PRIORITY,		/* 线程优先级 */THREAD_2_TIMESLICE  	/* 线程时间片 */);if (thread_2_handle != RT_NULL)rt_thread_startup(thread_2_handle);  rt_hw_interrupt_enable(level);  
}

3.2 代码执行流程

  1. 初始化:初始化事件集(rt_event_init)并发送 EVENT3_FLAG,启动线程 1(高优)、线程 2(低优);
  2. 线程 1 阻塞:线程 1 等EVENT1+EVENT2,不满足→阻塞;
  3. 线程 2 执行:接收EVENT3(清标志)→发EVENT2→快速闪烁→发EVENT1
  4. 线程 1 唤醒EVENT1+EVENT2满足→抢占 CPU→慢速闪烁→发EVENT3
  5. 闭环循环:线程 1 阻塞→线程 2 再执行,重复步骤 3-4。

3.3 核心同步效果

  • 标志位无残留:RT_EVENT_FLAG_CLEAR自动清位;
  • LED 规律:线程 2→5Hz 快速闪,线程 1→0.5Hz 慢速闪;
  • 优先级生效:线程 1 仅阻塞时,线程 2 才执行。

四、关键设计注意事项

  1. 事件标志位的复用:事件集的标志位被接收后默认不清除,需通过 rt_event_recv 的返回值手动记录状态,或在接收时通过 RT_EVENT_FLAG_CLEAR 选项自动清除;
  2. 中断中安全使用rt_event_send 可在中断服务程序中调用(无阻塞操作),但 rt_event_recv 禁止在中断中使用(可能导致阻塞);
  3. 避免事件丢失:若多个线程发送同一事件,标志位会被重复设置(无累计计数),需确保接收线程能及时处理;
  4. 初始化时机:静态事件集必须在线程启动前完成初始化,避免线程接收未初始化的事件集;
  5. 逻辑关系选择:根据场景选择 RT_EVENT_FLAG_AND(需全部事件满足)或 RT_EVENT_FLAG_OR(任意事件满足),避免逻辑错误导致线程无法唤醒。

总结

本文详细解析了 RT-Thread 事件集的核心特性、API 函数与实操示例,重点说明了多事件标志位如何实现线程间灵活同步。通过静态初始化事件集、双线程交替触发 LED 闪烁的案例,验证了事件集在复杂同步场景中的有效性。实际开发中,需结合业务需求设计标志位逻辑,合理使用超时机制与清除选项,确保系统同步的可靠性与实时性。

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

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

相关文章

常见问题处理 --- phpstudy启动mysql失败

常见问题处理 --- phpstudy启动mysql失败服务名冲突 net stop MySQL sc delete MySQL 进入mysql安装目录下执行 mysqld --install MySQL5 net start MySQL5端口冲突 net stop MySQL5 重新启动mysql

10.18 CSP-S 模拟赛

Contest CSP-ST1 只考虑连 \(a_u \leq a_v\) 的边,把所有边按照边权从小到大排序,跑一遍 dfs 求出最长路即可。 T2 你发现这种要求满足限制的题,且可以通过 \(x_r - x_l = d_i\) 构造关系。直接考虑差分约束,如果说…

高级语言程序设置第二次作业

3.11 编程练习 1.整数上溢浮点数上溢浮点数下溢2.3.4.5.6.7.8.

20232422 2025-2026-1 《网络与系统攻防技术》实验三实验报告

1.实验内容 本周实验关于免杀原理与实践,要掌握多种恶意代码免杀技术并验证效果。实操msfvenom生成多格式恶意文件并使用编码器优化,借助 Veil 工具生成免杀载荷,用 C 语言嵌入 Shellcode 编程并结合加壳工具增强隐…

20232404 2025-2026-1 《网络与系统攻防技术》实验三实验报告

1.实验内容 1.1具体内容 本周主要学习了恶意代码的检测原理和免杀技术实现方法,包括:理解杀毒软件的检测机制和免杀基本原理 掌握Msfvenom、Veil等工具的使用方法 尝试进行压缩加壳和加密加壳处理 编写C语言加载器实…

P14309 【MX-S8-T2】配对题解

题目链接 题目大意 给定\(n\)个点的树,每条边有边权,每个点有一个参数\(c_i\),若\(c_i =1\),表示被用于配对,每个点只能配对一次,若能配对,则必须配对。每一次配对,会给\(r\)加上两个点之间的距离。可以交换一…

魔改sunpinyin

魔改sunpinyinapt source libsunpinyin3v5 到当前目录下。 src/sunpinyin-dictgen是个有shebang的Makefile:#!/usr/bin/make -f W3M = wget -q -O - DL_LIST = https://sourceforge.net/projects/open-gram/files/ DL_…

20232308 2025-2026-1 《网络与系统攻防技术》实验三实验报告

1.实验内容 1.1 实践内容 (1)正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧正确使用msf编码器,使用msfvenom生成如jar之类的其他文件 veil,加壳工具 使用C + shellcode编程 (2)通过组合应…

「WC2014-紫荆花之恋」题解

题解记录P3920 [WC2014] 紫荆花之恋 sol 首先如果不带修的话就是点分治板子,带修的话就是动态点分树板子。 由于写过一篇动态点分树的博客,这里就对动态点分树部分不详细讲解了,主要讲一下信息维护吧。不会的话可以…

2025.10.27C 城堡考古 题解

有同学让我造福人类,所以来写一篇。考虑显然没有什么通项公式可以利用的,但是注意到 \(m\) 仅仅只有小小的 \(6\),考虑状压 \(dp\) 的思路。设 \(dp_{i,j}\) 表示当前已经排了 \(i\) 列,状态为 \(j\) 的方案数,其…

【密码学实战】openHiTLS PKCS12命令行程序: PKCS12文件生成与解析

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

[xp] GVim v9.0.494 (or thereabouts) is the last version known to support Windows XP.

下载地址:https://github.com/vim/vim-win32-installer/releases/tag/v9.0.0494If you need the last version of GVim (the GUI build of Vim) that officially supports Windows XP, here’s what I found — and s…

线段树;区间求和优化

线段树;区间求和优化线段树构造: 线段树:4*空间 第一种: #define maxn 100007//元素个数 int SegTree[maxn << 2];//线段树 // int lazy[maxn << 2];//延迟更新 int A[maxn];//原是数组第二种:结构体…

实用指南:2.CSS3.(2).html

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

「CTSC2017-游戏」题解

题解记录P3772 [CTSC2017] 游戏 sol 首先,由期望的线性性,把贡献拆到单点上,对每一场计算其胜利的概率即可。 首先已知的局可以不管,未知的局,显然只与其两侧最近的已知局有关。后面运用的一些概率表达在题面最下…

谢谢你周医生

谢谢你 周敏,张景     南京很美,想起你们我的心也很温暖

想让默认头像不再千篇一律,就顺手复刻了一下 GitHub 的思路

探索如何让默认头像不再千篇一律,我用 Go 复刻了 GitHub 风格的头像生成逻辑,根据输入生成独一无二的方块头像。文章分享了实现原理、效果展示以及未来扩展的思路在各种平台上,初始注册的用户通常都会被分配一个默认…

来源未知

点击查看代码 from PIL import Image# 定义字符集合 ascii_char = list("$@B%8&W#*oahkbdpqwmZO0QLCJYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`. ")def get_char(r, g, b, alpha=256):if a…

10.27(补)

继承和多态的动手动脑整理的word补上 链接:[https://files.cnblogs.com/files/blogs/847692/20243732张博学课后作业4.zip?t=1761562926&download=true]

vue3 vue3-form-element表单生成工具 输入框增加后缀

JSON schema数据{"title": "测试注册表单","description": "A simple form example.","type": "object","properties": {"firstName&quo…