RT-Thread之事件集使用示例
一、事件集概述
事件集(Event)是 RT-Thread 中用于線程間同步的輕量級(jí) IPC 機(jī)制,核心特性是 “多標(biāo)志位” 與 “靈活觸發(fā)”:通過 32 個(gè)事件標(biāo)志位(bit0~bit31)表示不同事件狀態(tài),支持線程按 “邏輯與(AND)” 或 “邏輯或(OR)” 關(guān)系等待多個(gè)事件,實(shí)現(xiàn)一對多、多對多的線程同步。
典型應(yīng)用場景:
- 線程等待多個(gè)外部觸發(fā)條件(如按鍵按下 + 傳感器數(shù)據(jù)就緒),滿足任意條件或全部條件時(shí)執(zhí)行;
- 狀態(tài)機(jī)切換同步(如設(shè)備初始化完成 + 網(wǎng)絡(luò)連接成功后,觸發(fā)業(yè)務(wù)線程啟動(dòng));
- 中斷與線程間通信(中斷服務(wù)程序發(fā)送事件,線程等待事件處理異步通知)。
二、事件集核心 API 函數(shù)
事件集的操作圍繞 “創(chuàng)建 / 初始化 / 發(fā)送事件 / 接收事件 / 刪除 / 脫離” 展開,需區(qū)分動(dòng)態(tài)創(chuàng)建(依賴內(nèi)存堆)與靜態(tài)初始化(基于全局 / 靜態(tài)變量),核心函數(shù)與參數(shù)說明如下。
2.1 事件集的創(chuàng)建與初始化
用于完成事件集的初始化工作,動(dòng)態(tài)創(chuàng)建會(huì)從堆中分配內(nèi)存,靜態(tài)初始化需提前定義全局 / 靜態(tài)事件集對象。
2.1.1 動(dòng)態(tài)創(chuàng)建
通過函數(shù)從內(nèi)存堆中分配事件集資源,返回事件集句柄用于后續(xù)操作。
rt_event_t rt_event_create(const char *name, rt_uint8_t flag);
- name:事件集名稱。
- flag:等待隊(duì)列排序方式,可選值為
RT_IPC_FLAG_FIFO(先進(jìn)先出)或RT_IPC_FLAG_PRIO(按線程優(yōu)先級(jí)排序)。
2.1.2 靜態(tài)初始化
基于已定義的全局 / 靜態(tài)事件集對象進(jìn)行初始化,無需分配堆內(nèi)存。
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);
- event:提前定義的全局 / 靜態(tài)事件集對象(如
struct rt_event event_test)。 - name:事件集名稱,功能同動(dòng)態(tài)創(chuàng)建。
- flag:等待隊(duì)列排序方式,功能同動(dòng)態(tài)創(chuàng)建。
2.2 事件的發(fā)送與接收
發(fā)送事件用于設(shè)置事件集中的標(biāo)志位,接收事件用于等待指定標(biāo)志位滿足條件,支持按邏輯關(guān)系觸發(fā)。
2.2.1 發(fā)送事件
設(shè)置事件集中的一個(gè)或多個(gè)標(biāo)志位,可喚醒等待該事件的線程。
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
- event:目標(biāo)事件集句柄 / 對象。
- set:待設(shè)置的事件標(biāo)志位(如
0x01表示 bit0,0x03表示 bit0 + bit1)。 - 注意:可在中斷服務(wù)程序(ISR)中調(diào)用,用于通知線程處理異步事件。
2.2.2 接收事件
等待事件集中的指定標(biāo)志位,滿足邏輯關(guān)系(AND / OR)時(shí)喚醒線程,支持超時(shí)機(jī)制。
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:目標(biāo)事件集句柄 / 對象。
- set:待等待的事件標(biāo)志位(如
0x03表示等待 bit0 或 bit1)。 - option:對標(biāo)志位的配置,
RT_EVENT_FLAG_AND(全部標(biāo)志位滿足)或RT_EVENT_FLAG_OR(任意標(biāo)志位滿足),也可以選用RT_EVENT_FLAG_CLEAR,表示接收事件后自動(dòng)清除已滿足的標(biāo)志位。 - timeout:等待時(shí)間(單位:tick),可選值為
RT_WAITING_FOREVER(永久等待)、0(無等待)或具體數(shù)值。 - recved:輸出參數(shù),用于返回實(shí)際接收到的事件標(biāo)志位(可設(shè)為 RT_NULL)。
2.3 事件集的刪除與脫離
用于回收事件集占用的系統(tǒng)資源,動(dòng)態(tài)創(chuàng)建的事件集需刪除,靜態(tài)初始化的事件集需脫離。
2.3.1 動(dòng)態(tài)刪除
用于銷毀通過 rt_event_create 動(dòng)態(tài)創(chuàng)建的事件集,釋放其占用的堆內(nèi)存。
rt_err_t rt_event_delete(rt_event_t event);
- event:動(dòng)態(tài)創(chuàng)建的事件集句柄。
- 適用場景:動(dòng)態(tài)創(chuàng)建的事件集不再使用時(shí),需調(diào)用此函數(shù)避免內(nèi)存泄漏。
2.3.2 靜態(tài)脫離
用于脫離通過 rt_event_init 靜態(tài)初始化的事件集,僅注銷其在系統(tǒng)中的登記信息,不釋放內(nèi)存。
rt_err_t rt_event_detach(rt_event_t event);
- event:靜態(tài)初始化的事件集對象。
- 適用場景:靜態(tài)事件集不再使用時(shí),需調(diào)用此函數(shù)釋放系統(tǒng)資源(如等待隊(duì)列)。
三、事件集使用示例
3.1 源代碼
#include "thread_task.h"
#include "main.h"
#include <stdio.h>
#include "rtthread.h"
#include <rthw.h>
/******************************************** 線程 1 ******************************************************/
#define THREAD_1_PRIORITY 4 /* 線程優(yōu)先級(jí)(值越小優(yōu)先級(jí)越高) */
#define THREAD_1_STACK_SIZE 512 /* 線程棧空間大小(單位:字節(jié)) */
#define THREAD_1_TIMESLICE 10 /* 線程時(shí)間片個(gè)數(shù)(單位:tick) */
static struct rt_thread *thread_1_handle; /* 線程句柄 */
/******************************************** 線程 2 ******************************************************/
#define THREAD_2_PRIORITY 5 /* 線程優(yōu)先級(jí)(低于線程1) */
#define THREAD_2_STACK_SIZE 512 /* 線程棧空間大小 */
#define THREAD_2_TIMESLICE 10 /* 線程時(shí)間片個(gè)數(shù) */
static struct rt_thread *thread_2_handle; /* 線程句柄 */
/* 靜態(tài)事件集對象(全局定義,提前分配內(nèi)存) */
struct rt_event event_test;
/* 事件標(biāo)志位定義:bit0 表示線程1觸發(fā)事件,bit1 表示線程2觸發(fā)事件 */
#define EVENT1_FLAG (1 << 0) /* 0x01 */
#define EVENT2_FLAG (1 << 1) /* 0x02 */
#define EVENT3_FLAG (1 << 2) /* 0x04 */
/**
* @brief LED閃爍函數(shù)(固定閃爍6次,即3個(gè)完整周期)
* @param time:每次翻轉(zhuǎn)后的延遲時(shí)間(單位: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入口函數(shù)(高優(yōu)先級(jí),接收事件觸發(fā)LED慢速閃爍)
* @param param:線程參數(shù)(未使用,設(shè)為RT_NULL)
*/
void thread_1_entry(void* param)
{
rt_uint32_t recved = 0;
while(1)
{
/* 接收事件:等待EVENT1+EVENT2,AND邏輯,永久等待,接收后清除標(biāo)志位 */
if (rt_event_recv(&event_test, EVENT2_FLAG | EVENT1_FLAG ,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, // 補(bǔ)充RT_EVENT_FLAG_CLEAR
RT_WAITING_FOREVER, &recved) == RT_EOK)
{
/* 接收到事件后,慢速閃爍LED(0.5Hz) */ // 修正頻率描述
LED_toggle(1000);
rt_event_send(&event_test, EVENT3_FLAG);
}
}
}
/**
* @brief 線程2入口函數(shù)(低優(yōu)先級(jí),接收事件觸發(fā)LED快速閃爍)
* @param param:線程參數(shù)(未使用,設(shè)為RT_NULL)
*/
void thread_2_entry(void* param)
{
rt_uint32_t recved = 0;
while(1)
{
/* 接收事件:等待EVENT3_FLAG,AND邏輯,永久等待,接收后清除標(biāo)志位 */ // 修正筆誤
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 初始化事件集并創(chuàng)建啟動(dòng)線程(先初始化事件集,再啟動(dòng)線程)
*/
void ThreadStart(void)
{
rt_base_t level = rt_hw_interrupt_disable();
/* 靜態(tài)初始化事件集 */
rt_event_init(
&event_test, /* 事件集對象 */
"event_test", /* 事件集名稱(用于finsh調(diào)試) */
RT_IPC_FLAG_FIFO /* 等待隊(duì)列按FIFO排序 */
);
rt_event_send(&event_test, EVENT3_FLAG);
/* 動(dòng)態(tài)創(chuàng)建并啟動(dòng)線程1 */
thread_1_handle = rt_thread_create(
"thread_1", /* 線程名稱 */
thread_1_entry, /* 線程入口函數(shù) */
RT_NULL, /* 線程參數(shù) */
THREAD_1_STACK_SIZE, /* 線程棧大小 */
THREAD_1_PRIORITY, /* 線程優(yōu)先級(jí) */
THREAD_1_TIMESLICE /* 線程時(shí)間片 */
);
if (thread_1_handle != RT_NULL)
rt_thread_startup(thread_1_handle);
/* 動(dòng)態(tài)創(chuàng)建并啟動(dòng)線程2 */
thread_2_handle = rt_thread_create(
"thread_2", /* 線程名稱 */
thread_2_entry, /* 線程入口函數(shù) */
RT_NULL, /* 線程參數(shù) */
THREAD_2_STACK_SIZE, /* 線程棧大小 */
THREAD_2_PRIORITY, /* 線程優(yōu)先級(jí) */
THREAD_2_TIMESLICE /* 線程時(shí)間片 */
);
if (thread_2_handle != RT_NULL)
rt_thread_startup(thread_2_handle);
rt_hw_interrupt_enable(level);
}
3.2 代碼執(zhí)行流程
- 初始化:初始化事件集(rt_event_init)并發(fā)送 EVENT3_FLAG,啟動(dòng)線程 1(高優(yōu))、線程 2(低優(yōu));
- 線程 1 阻塞:線程 1 等
EVENT1+EVENT2,不滿足→阻塞; - 線程 2 執(zhí)行:接收
EVENT3(清標(biāo)志)→發(fā)EVENT2→快速閃爍→發(fā)EVENT1; - 線程 1 喚醒:
EVENT1+EVENT2滿足→搶占 CPU→慢速閃爍→發(fā)EVENT3; - 閉環(huán)循環(huán):線程 1 阻塞→線程 2 再執(zhí)行,重復(fù)步驟 3-4。
3.3 核心同步效果
- 標(biāo)志位無殘留:
RT_EVENT_FLAG_CLEAR自動(dòng)清位; - LED 規(guī)律:線程 2→5Hz 快速閃,線程 1→0.5Hz 慢速閃;
- 優(yōu)先級(jí)生效:線程 1 僅阻塞時(shí),線程 2 才執(zhí)行。
四、關(guān)鍵設(shè)計(jì)注意事項(xiàng)
- 事件標(biāo)志位的復(fù)用:事件集的標(biāo)志位被接收后默認(rèn)不清除,需通過
rt_event_recv的返回值手動(dòng)記錄狀態(tài),或在接收時(shí)通過RT_EVENT_FLAG_CLEAR選項(xiàng)自動(dòng)清除; - 中斷中安全使用:
rt_event_send可在中斷服務(wù)程序中調(diào)用(無阻塞操作),但rt_event_recv禁止在中斷中使用(可能導(dǎo)致阻塞); - 避免事件丟失:若多個(gè)線程發(fā)送同一事件,標(biāo)志位會(huì)被重復(fù)設(shè)置(無累計(jì)計(jì)數(shù)),需確保接收線程能及時(shí)處理;
- 初始化時(shí)機(jī):靜態(tài)事件集必須在線程啟動(dòng)前完成初始化,避免線程接收未初始化的事件集;
- 邏輯關(guān)系選擇:根據(jù)場景選擇
RT_EVENT_FLAG_AND(需全部事件滿足)或RT_EVENT_FLAG_OR(任意事件滿足),避免邏輯錯(cuò)誤導(dǎo)致線程無法喚醒。
總結(jié)
本文詳細(xì)解析了 RT-Thread 事件集的核心特性、API 函數(shù)與實(shí)操示例,重點(diǎn)說明了多事件標(biāo)志位如何實(shí)現(xiàn)線程間靈活同步。通過靜態(tài)初始化事件集、雙線程交替觸發(fā) LED 閃爍的案例,驗(yàn)證了事件集在復(fù)雜同步場景中的有效性。實(shí)際開發(fā)中,需結(jié)合業(yè)務(wù)需求設(shè)計(jì)標(biāo)志位邏輯,合理使用超時(shí)機(jī)制與清除選項(xiàng),確保系統(tǒng)同步的可靠性與實(shí)時(shí)性。

事件集(Event)是 RT-Thread 中用于線程間同步的輕量級(jí) IPC 機(jī)制,核心特性是 “多標(biāo)志位” 與 “靈活觸發(fā)”:通過 32 個(gè)事件標(biāo)志位(bit0~bit31)表示不同事件狀態(tài),支持線程按 “邏輯與(AND)” 或 “邏輯或(OR)” 關(guān)系等待多個(gè)事件,實(shí)現(xiàn)一對多、多對多的線程同步。
浙公網(wǎng)安備 33010602011771號(hào)