事件组 (Event Group) 是 FreeRTOS 中一种非常灵活且强大的同步机制。
事件组就是 FreeRTOS 中的“多功能状态指示灯”或“全局任务清单”。
一:核心概念:它是什么?
在 FreeRTOS 中,信号量(Semaphore)和队列(Queue)通常是一对一或者一对多的通信,而且往往是“消耗性”的(读走了就没有了)。
而事件组本质上是一个整数(Integer),其中的每一位(Bit)代表一个特定的事件。
-
0:代表事件未发生。
-
1:代表事件发生了。
它的特殊能力:
-
多对多同步: 多个任务可以同时等待同一个事件组。
-
逻辑判断: 任务可以等待“某个特定事件发生”(OR 逻辑),也可以等待“所有指定事件都发生”(AND 逻辑)才被唤醒。
1. 生活类比:火箭发射倒计时

2. 核心原理:就是一串二进制位

3. 为什么需要事件组?(对比信号量)

二:事件组函数


1. 创建事件组:xEventGroupCreate
/* 创建一个事件组,返回它的句柄。
* 此函数内部会分配事件组结构体
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );
/* 创建一个事件组,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );

2. 发送信号(设置位):xEventGroupSetBits
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有, pdFALSE-没有
* 返回值: pdPASS-成功, pdFALSE-失败
*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken );

3. 等待信号(等待位):xEventGroupWaitBits (最重要!)
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );


4. 实战:怎么判断 xEventGroupWaitBits 的返回值?



5.示范代码
/* 定义事件位 */
#define EVENT_FUEL (1 << 0) // 0001
#define EVENT_ENGINE (1 << 1) // 0010
/* 创建句柄 */
EventGroupHandle_t xLaunchEventGroup;
/* 任务1:负责加燃料 */
void vTaskFuel(void *pvParameters) {
// ... 模拟加燃料的操作 ...
printf("燃料加注完毕!\n");
// 设置 Bit 0 为 1
xEventGroupSetBits(xLaunchEventGroup, EVENT_FUEL);
vTaskDelete(NULL); // 任务完成自杀
}
/* 任务2:负责检查引擎 */
void vTaskEngine(void *pvParameters) {
// ... 模拟检查引擎 ...
printf("引擎检查正常!\n");
// 设置 Bit 1 为 1
xEventGroupSetBits(xLaunchEventGroup, EVENT_ENGINE);
vTaskDelete(NULL);
}
/* 任务3:发射主控 (等待者) */
void vTaskLaunch(void *pvParameters) {
const EventBits_t xBitsToWaitFor = (EVENT_FUEL | EVENT_ENGINE);
EventBits_t xEventGroupValue;
// 等待 Bit 0 和 Bit 1 同时变高
xEventGroupValue = xEventGroupWaitBits(
xLaunchEventGroup,
xBitsToWaitFor,
pdTRUE, // 退出时清除这些位(重置状态)
pdTRUE, // pdTRUE 表示逻辑 AND,必须两个都准备好
portMAX_DELAY // 一直等,不见不散
);
if ((xEventGroupValue & (EVENT_FUEL | EVENT_ENGINE)) == (EVENT_FUEL | EVENT_ENGINE)) {
printf("所有条件满足,火箭发射!!!\n");
}
}
6.如何理解
((xEventGroupValue & (EVENT_FUEL | EVENT_ENGINE)) == (EVENT_FUEL | EVENT_ENGINE))




三: 同步点(Synchronization Point)
1.什么是“同步点” (Synchronization Point)?
在 FreeRTOS 中,同步点通常被称为 “汇合点” (Rendezvous)。
1. 生活类比:旅游团集合
想象一个旅游团有 3 个游客(任务 A、B、C),导游规定:“大家自由活动,下午 5 点在西门集合,人齐了才能一起上车去吃饭。”
-
游客 A 4:50 到了 -> 必须等。
-
游客 B 4:55 到了 -> 必须等。
-
游客 C 5:00 到了 -> 只有当 C 到达的那一瞬间,3 个人同时解除等待,一起上车。
这就是同步点:多任务互相等待,直到所有参与者都到达指定位置,然后同时恢复运行。
2. 核心函数:xEventGroupSync()
这个函数做的事情非常精妙,它在一个函数里完成了两步操作:
-
举手:表明“我到了”(设置自己的位)。
-
等待:盯着看“大家都到了没”(等待所有的位)。
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet, // 我是谁?(我到了要设哪一位)
const EventBits_t uxBitsToWaitFor, // 我在等谁?(所有人齐了是哪几位)
TickType_t xTicksToWait ); // 等多久
3、 代码实战:三任务同步
假设我们需要采集 温度、湿度、气压。必须三个数据都采集好了,才能打包发送给服务器。
定义三个位:
-
TASK_A_BIT(1<<0) -
TASK_B_BIT(1<<1) -
TASK_C_BIT(1<<2) -
全家福:
ALL_SYNC_BITS(TASK_A_BIT | TASK_B_BIT | TASK_C_BIT)
任务 A 的代码(任务 B 和 C 逻辑一模一样,只是改一下自己的位)
void vTaskA( void *pvParameters ) {
for( ;; ) {
// 1. 干活:采集温度
CollectTemperature();
// 2. 到了同步点!
// 意思:我是 A,我到了!我要等 A、B、C 都到齐了才能走!
xEventGroupSync(
xEventGroup,
TASK_A_BIT, // 设置我自己的位 (Set)
ALL_SYNC_BITS, // 等待所有人的位 (Wait)
portMAX_DELAY // 死等,不见不散
);
// 3. 当代码运行到这里时,说明 B 和 C 一定也都干完活了!
// 可以开始下一步协同工作了
printf("任务 A: 大家都齐了,我继续运行...\n");
}
}
逻辑流程图解
-
Task A 先跑完,调用
Sync。-
事件组变成
001。 -
等待
111。条件不满足 -> Task A 阻塞(睡觉)。
-
-
Task B 接着跑完,调用
Sync。-
事件组变成
011。 -
等待
111。条件不满足 -> Task B 阻塞(睡觉)。
-
-
Task C 最后跑完,调用
Sync。-
事件组变成
111。 -
条件满足!
-
瞬间发生的事: Task A、Task B、Task C 全部被唤醒。事件组自动清零(这很重要!)。三个任务几乎同时进入下一行代码。
-
4.总结
5.提问
第一问:ALL_SYNC_BITS 是什么意思?

第二问:WaitBits (AND模式) 和 Sync 不都是“等人齐”吗?有什么区别?




2633

被折叠的 条评论
为什么被折叠?



