2025最新超详细FreeRTOS入门教程:第七章 FreeRTOS事件组

摘要

在前面几章中,我们学习了 信号量与互斥量,它们可以解决任务间同步和资源保护的问题。但在一些复杂系统中,我们需要让多个任务 等待多个事件的组合,例如:

  • 一个任务需要等 多个外设都准备好 才能继续运行
  • 一个通信任务需要等到 接收完成 + 校验成功 后才能继续处理
  • 一个控制任务需要等到 多个信号同时到达 才能执行逻辑

这时候,使用 事件组(Event Group) 是最佳选择。
事件组 允许我们以“位”的方式管理多个事件,任务可以等待某些事件的 任意组合(OR关系)全部满足(AND关系)

2025最新超详细FreeRTOS入门教程


一、事件组的基本概念

1. 什么是事件组

  • 事件组本质上是一个 32位变量(最多 24 位可用,低 8 位由系统保留)
  • 每一位(bit)代表一个事件
  • 任务可以等待 一个或多个事件位 置位后再继续执行

2. 应用场景

  • 多任务同步:多个任务等待同一个事件
  • 多事件同步:一个任务等待多个事件组合
  • 中断与任务同步:ISR 设置事件,任务响应事件
WiFi+MQTT均准备好
任务A: WiFi连接
事件组
任务B: MQTT连接
任务C: 数据发送
开始业务处理

二、事件组 API

1. 创建与删除

EventGroupHandle_t xEventGroupCreate(void);
void vEventGroupDelete(EventGroupHandle_t xEventGroup);

2. 设置事件位

EventBits_t xEventGroupSetBits(
   EventGroupHandle_t xEventGroup,
   const EventBits_t uxBitsToSet
);

ISR 中使用:

BaseType_t xEventGroupSetBitsFromISR(
   EventGroupHandle_t xEventGroup,
   const EventBits_t uxBitsToSet,
   BaseType_t *pxHigherPriorityTaskWoken
);

3. 等待事件位

EventBits_t xEventGroupWaitBits(
   EventGroupHandle_t xEventGroup,
   const EventBits_t uxBitsToWaitFor,
   const BaseType_t xClearOnExit,
   const BaseType_t xWaitForAllBits,
   TickType_t xTicksToWait
);

参数说明:

  • uxBitsToWaitFor:等待的事件位(如 BIT_0 | BIT_1
  • xClearOnExit:是否在退出时自动清除事件位
  • xWaitForAllBits:是否等待所有位满足(AND)
  • xTicksToWait:阻塞时间

4. 清除事件位

EventBits_t xEventGroupClearBits(
   EventGroupHandle_t xEventGroup,
   const EventBits_t uxBitsToClear
);

三、事件组使用示例

示例:等待 WiFi 和 MQTT 同时连接成功

#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"

#define WIFI_CONNECTED_BIT  (1 << 0)
#define MQTT_CONNECTED_BIT  (1 << 1)

EventGroupHandle_t xEventGroup;

void vTaskWiFi(void *pvParameters)
{
    vTaskDelay(2000);
    printf("WiFi 连接成功\n");
    xEventGroupSetBits(xEventGroup, WIFI_CONNECTED_BIT);
    vTaskDelete(NULL);
}

void vTaskMQTT(void *pvParameters)
{
    vTaskDelay(3000);
    printf("MQTT 连接成功\n");
    xEventGroupSetBits(xEventGroup, MQTT_CONNECTED_BIT);
    vTaskDelete(NULL);
}

void vTaskMain(void *pvParameters)
{
    EventBits_t uxBits;
    printf("等待WiFi和MQTT连接...\n");

    uxBits = xEventGroupWaitBits(
        xEventGroup,
        WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT,
        pdTRUE,  // 清除位
        pdTRUE,  // 等待所有位
        portMAX_DELAY
    );

    if((uxBits & (WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT)) ==
       (WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT))
    {
        printf("网络环境就绪,开始业务处理\n");
    }

    vTaskDelete(NULL);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();

    xEventGroup = xEventGroupCreate();

    xTaskCreate(vTaskWiFi, "WiFi", 128, NULL, 2, NULL);
    xTaskCreate(vTaskMQTT, "MQTT", 128, NULL, 2, NULL);
    xTaskCreate(vTaskMain, "Main", 128, NULL, 1, NULL);

    vTaskStartScheduler();
    while(1) {}
}

运行结果

  • WiFi 和 MQTT 任务分别连接
  • Main 任务阻塞,直到两者都完成
  • Main 任务开始执行业务逻辑

四、事件组的工作机制

OR模式
AND模式
无法满足
等待事件
部分满足
继续运行
全部满足
超时
  • OR模式:任意一个事件满足即可继续
  • AND模式:所有事件满足才继续

五、事件组的应用场景

场景 描述 示例
多任务同步 多个任务等待相同事件 多个消费者等待生产者事件
多事件组合 一个任务等待多个事件同时满足 WiFi+MQTT+传感器就绪
ISR 通知任务 中断触发事件,任务等待事件位 按键中断触发任务执行

六、调试与监控

1. 获取事件组当前值

EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);

2. ISR 中获取

EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);

七、事件组与信号量的比较

特性 信号量 事件组
单一事件 ❌(可多位)
多事件组合
资源保护
ISR 使用
内部机制 队列 位图

八、常见问题与解决方法

问题 可能原因 解决方法
任务一直阻塞 等待模式设置错误 检查 xWaitForAllBits 参数
事件位丢失 设置时清除位 确认 xClearOnExit 使用正确
ISR 中报错 使用了非 ISR API 改用 xEventGroupSetBitsFromISR
占用内存过多 创建多个事件组 尽量合并事件位,节省资源

九、经验总结

📌 开发建议

  1. 对于 需要多个事件联合触发 的场景,优先使用事件组
  2. 避免过度依赖事件组来传递数据,事件组只适合布尔型事件
  3. 建议将事件位定义为宏,避免混淆
  4. 配合信号量和队列使用,可以构建更复杂的任务调度体系

十、总结

通过本章学习,你已经掌握:

  • 事件组的基本原理与 API
  • 使用 xEventGroupWaitBits 实现任务等待多个事件
  • 中断中使用 xEventGroupSetBitsFromISR
  • 事件组与信号量的区别

事件组是构建复杂任务同步关系的重要工具,在物联网、通信、传感器融合等场景中非常常见。


🔗 FreeRTOS专栏👉 下一章:2025最新超详细FreeRTOS入门教程:第八章 FreeRTOS任务通知 ——我们将学习更轻量级的任务间通信方式:任务通知。


Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐