事件组(Event Group)是一种特殊的任务间通信机制,用于一个任务等待多个事件中的任意一个或全部发生。相比用多个信号量逐个等待,事件组用一个整数的每个 bit 表示一个事件,非常节省内存。
基本概念
- 事件标志:一个布尔值(0 或 1),表示某个事件是否发生
- 事件组:多个事件标志的集合,用一个
EventBits_t(通常是uint32_t)的各个 bit 位表示
bit31 bit30 ... bit3 bit2 bit1 bit0
0 0 ... 0 0 0 0
│ │ │ │ │ │
│ │ │ │ │ └── 事件0:初始化完成
│ │ │ │ └─────── 事件1:数据就绪
│ │ │ └──────────── 事件2:ISR 触发
│ │ └───────────────── 事件3...
│ └────────────────────────── 事件30...
└──────────────────────────────── 事件31...
每个 bit:0 = 事件未发生,1 = 事件已发生。
创建事件组
// 动态创建
EventGroupHandle_t xEventGroupCreate(void);
// 静态创建
EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);
设置事件位
// 任务中使用
EventBits_t xEventGroupSetBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet // 要设置的 bit(可以 OR 多个)
);
// ISR 中使用(带 FromISR 后缀)
BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken
);
设置 bit 就是把对应位置 1,表示事件发生了:
#define EVENT_INIT_DONE (1UL << 0) // bit0
#define EVENT_DATA_READY (1UL << 1) // bit1
#define EVENT_ISR_TRIG (1UL << 2) // bit2
// 设置单个 bit
xEventGroupSetBits(xEventGroup, EVENT_INIT_DONE);
// 同时设置多个 bit(OR 组合)
xEventGroupSetBits(xEventGroup, EVENT_DATA_READY | EVENT_ISR_TRIG);
等待事件位
EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor, // 等待哪些 bit
const BaseType_t xClearOnExit, // pdTRUE:满足条件后自动清除这些bit
const BaseType_t xWaitForAllBits, // pdTRUE:所有指定bit都为1才唤醒;pdFALSE:任意一个为1就唤醒
TickType_t xTicksToWait // 超时时间
);
返回值:唤醒时(或超时时)事件组的值。需要手动检查返回值中是否包含期望的 bit。
参数详解
xClearOnExit:
pdTRUE:函数退出(拿到满足条件的事件后)自动清除uxBitsToWaitFor中指定的 bit,适合”消费事件”的场景pdFALSE:不自动清除,bit 保持 1,适合”广播事件”的场景(多个任务都要看到这个事件)
xWaitForAllBits:
pdTRUE:等待所有指定的 bit 都为 1(逻辑 AND)pdFALSE:等待任意一个指定的 bit 为 1(逻辑 OR)
使用示例
EventBits_t uxBits;
uxBits = xEventGroupWaitBits(
xEventGroup,
EVENT_INIT_DONE | EVENT_DATA_READY | EVENT_ISR_TRIG, // 等这三个事件
pdTRUE, // 满足后自动清除
pdTRUE, // 三个都满足才唤醒
portMAX_DELAY // 一直等
);
// 检查返回值,区分是正常唤醒还是超时
if((uxBits & (EVENT_INIT_DONE | EVENT_DATA_READY | EVENT_ISR_TRIG))
== (EVENT_INIT_DONE | EVENT_DATA_READY | EVENT_ISR_TRIG))
{
// 三个事件都发生了,正常处理
}
else
{
// 超时返回,条件未全部满足
}
多任务同步:xEventGroupSync
一个常见的场景是:多个任务都到达同步点后,才能一起继续执行(类似”会合”/rendezvous)。
问题:SetBits 和 WaitBits 是两步操作,有竞态
假设 Task A、B、C 三个任务同步:
1. Task A 和 B 已经到达同步点,调用 WaitBits 等待 C 的 bit
2. Task C 到达:
第一步:SetBits(C 的 bit) → A 和 B 立刻被唤醒,并自动清除了所有 bit!
第二步:WaitBits(A|B|C) → 但 bit 已经被 A/B 的 ClearOnExit 清掉了
→ Task C 永远等不到三个 bit 都为 1!
解决:xEventGroupSync 原子操作
xEventGroupSync() 把”设置自己的 bit”和”等待其他 bit”合并为一个不可分割的原子操作:
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet, // 设置自己的同步 bit
const EventBits_t uxBitsToWaitFor, // 等待哪些 bit
TickType_t xTicksToWait
);
// 每个任务到达同步点时调用:
xEventGroupSync(
xEventGroup,
MY_BIT, // 设置自己的 bit
ALL_BITS, // 等待所有人都到齐
portMAX_DELAY
);
// 到这里说明所有任务都到达同步点了
执行过程:
- 设置自己的 bit
- 立刻登记等待条件(这两步之间不会被其他任务打断)
- 阻塞等待,直到所有等待的 bit 都为 1
- 如果配置了
xClearOnExit,退出时清除 bit(Sync 内部会自动处理清除逻辑)
三任务同步的正确写法
三个任务都调用
xEventGroupSync(),分别设置自己的 bit,等待BIT_A | BIT_B | BIT_C。任何一个任务先到就设置自己的 bit 并阻塞等待;最后一个任务设置自己的 bit 时,所有 bit 凑齐,三个任务同时被唤醒。
清除事件位
// 手动清除指定 bit
EventBits_t xEventGroupClearBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear
);
// ISR 版本
BaseType_t xEventGroupClearBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear
);
通常不需要手动清除
配合
xClearOnExit = pdTRUE使用时,WaitBits 成功返回后会自动清除对应的 bit,不需要手动 Clear。
查询事件位
// 查询当前事件组的值(不改变任何 bit,不阻塞)
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
// ISR 版本
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);
事件组 vs 信号量 vs 队列
| 特性 | 事件组 | 信号量 | 队列 |
|---|---|---|---|
| 等待多个条件 | ✅ 一个调用等多个 bit | ❌ 只能等一个信号量 | ❌ 只能等一个队列 |
| 广播给多个任务 | ✅ 可多个任务等同一组 bit | ❌ 信号量被一个任务拿走就没了 | ❌ 数据被一个任务取走就没了 |
| 传递数据 | ❌ 只有 0/1 标志 | ❌ 只有有/无 | ✅ 可以传任意数据 |
| ISR 中使用 | ✅ SetBitsFromISR | ✅ GiveFromISR | ✅ SendFromISR |
| 典型场景 | 多条件同步、状态广播 | 资源互斥、事件通知 | 数据传递 |
典型使用场景
场景1:系统初始化同步
// 各个初始化任务分别完成后设置 bit
// 主任务等待所有初始化完成才启动业务逻辑
场景2:多条件触发
// 等待"网络连接上" AND "MQTT 连上" AND "配置加载完" 才开始工作
场景3:任务会合
// 多个任务都完成各自的阶段后,同时进入下一阶段
下一步:08-任务通知