聊一下FreeRTOS 的 EventGroup

FreeRTOS 的 EventGroup 是啥,怎么用。EventGroup 是 FreeRTOS 提供的一个同步工具,特别适合多个任务之间通过“事件标志”来协调工作。

啥是 EventGroup?

想象你在组织一个饭局,几个朋友得同时准备好(桌子订好、菜点好、人到齐)才能开吃。EventGroup 就像一个“准备清单”,每个任务负责勾一个“准备好了”的标志,等所有标志都勾上,大家才能一起干活。它比信号量(Semaphore)高级点,因为能同时管多个条件。

简单说:EventGroup 是一个“多位标志”的工具,用来通知和等待多个事件。

基本概念 事件组:一个 EventGroup 里有 24 位(ESP32 上,通常是 32 位架构,但 FreeRTOS 限制为 24 位可用),每位是一个“标志位”,可以代表一个条件或事件。 设置标志:某个任务完成时,可以“点亮”某个位(置 1)。 等待标志:其他任务可以等某些位(或所有位)点亮再干活。

怎么用? ESP32 的 FreeRTOS 提供了几个关键函数,咱们边讲边试。

  1. 创建 EventGroup 先得有个“清单”:
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"

EventGroupHandle_t my_event_group;

void app_main() {
    my_event_group = xEventGroupCreate(); // 创建事件组
    if (my_event_group == NULL) {
        printf("创建失败,内存不够!\n");
    }
}

xEventGroupCreate():创建事件组,返回一个句柄。 检查返回值,内存不够会返回 NULL。

2. 设置标志位

任务干完活后,可以点亮某个位:

#define WIFI_CONNECTED_BIT BIT0  // 第0位:WiFi连上了
#define DATA_READY_BIT    BIT1  // 第1位:数据准备好

void wifi_task(void *pvParameters) {
    // 假装连WiFi
    vTaskDelay(2000 / portTICK_PERIOD_MS); // 等2秒
    printf("WiFi 连上了\n");
    xEventGroupSetBits(my_event_group, WIFI_CONNECTED_BIT); // 点亮第0位
    vTaskDelete(NULL);
}

BIT0 是 1 << 0(第0位),BIT1 是 1 << 1(第1位)。 xEventGroupSetBits(事件组, 位):设置指定位为 1。

3. 等待标志位

另一个任务等着条件满足:

void main_task(void *pvParameters) {
    EventBits_t bits;
    const EventBits_t WAIT_BITS = (WIFI_CONNECTED_BIT | DATA_READY_BIT); // 等第0和第1位

    while (1) {
        // 等待所有标志位都亮
        bits = xEventGroupWaitBits(my_event_group, WAIT_BITS, pdTRUE, pdTRUE, portMAX_DELAY);
        printf("收到事件: 0x%x\n", bits);
        printf("WiFi和数据都准备好,开始干活!\n");
    }
}

xEventGroupWaitBits(事件组, 等哪些位, 是否清零, 是否全等, 超时):

等 WAIT_BITS(第0和第1位)。

pdTRUE(清零):等到了自动清标志。

pdTRUE(全等):所有位都得亮才行。

portMAX_DELAY:一直等。

返回亮起的位,比如 0x3(二进制 11)表示第0和第1位都亮。

4. 完整例子

把 WiFi 和数据准备的任务组合起来:

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

#define WIFI_CONNECTED_BIT BIT0
#define DATA_READY_BIT    BIT1

EventGroupHandle_t my_event_group;

void wifi_task(void *pvParameters) {
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    printf("WiFi 连上了\n");
    xEventGroupSetBits(my_event_group, WIFI_CONNECTED_BIT);
    vTaskDelete(NULL);
}

void data_task(void *pvParameters) {
    vTaskDelay(3000 / portTICK_PERIOD_MS);
    printf("数据准备好了\n");
    xEventGroupSetBits(my_event_group, DATA_READY_BIT);
    vTaskDelete(NULL);
}

void main_task(void *pvParameters) {
    EventBits_t bits;
    const EventBits_t WAIT_BITS = (WIFI_CONNECTED_BIT | DATA_READY_BIT);

    while (1) {
        bits = xEventGroupWaitBits(my_event_group, WAIT_BITS, pdTRUE, pdTRUE, portMAX_DELAY);
        printf("所有条件满足: 0x%x\n", bits);
        printf("开始干活!\n");
    }
}

void app_main() {
    my_event_group = xEventGroupCreate();
    xTaskCreate(wifi_task, "WiFi", 2048, NULL, 1, NULL);
    xTaskCreate(data_task, "Data", 2048, NULL, 1, NULL);
    xTaskCreate(main_task, "Main", 2048, NULL, 1, NULL);
}

输出:

(等2秒)
WiFi 连上了
(再等1秒)
数据准备好了
所有条件满足: 0x3
开始干活!

WiFi 任务 2 秒后点亮 BIT0。 数据任务 3 秒后点亮 BIT1。 主任务等到两个位都亮(3秒后),开始干活。

高级用法

1. 等待部分标志

如果只等一个条件,用 pdFALSE(非全等):

bits = xEventGroupWaitBits(my_event_group, WAIT_BITS, pdTRUE, pdFALSE, portMAX_DELAY);

pdFALSE:只要 WAIT_BITS 中任意一位亮就行。 比如只等 WiFi,连上就跑。

2. 不清标志

如果想保留标志位状态:

bits = xEventGroupWaitBits(my_event_group, WAIT_BITS, pdFALSE, pdTRUE, portMAX_DELAY);

pdFALSE(不清零):标志位保持亮,适合重复检查。

3. 加超时

不想一直等:

bits = xEventGroupWaitBits(my_event_group, WAIT_BITS, pdTRUE, pdTRUE, 5000 / portTICK_PERIOD_MS);
if (bits == WAIT_BITS) {
    printf("5秒内条件满足\n");
} else {
    printf("超时了\n");
}

需要掌握啥?

创建和销毁:

xEventGroupCreate() 创建。 vEventGroupDelete() 销毁(用完别忘了)。

设置标志:

xEventGroupSetBits() 点亮位。

等待标志:

xEventGroupWaitBits() 等条件,搞懂参数(清零、全等、超时)。

位操作:

用 BIT0、BIT1 定义标志,懂位运算(|、&)。

实际场景:

比如 WiFi 连接、传感器就绪、用户按键,用 EventGroup 同步。

总结

EventGroup 是 FreeRTOS 的“多条件开关”,比信号量灵活。

核心用法:创建 -> 设置标志 -> 等待标志。

ESP32 上用它能轻松管多个任务的同步,比如网络+数据处理。

文档信息

版权声明:可自由转载(请注明转载出处)-非商用-非衍生

发表时间:2025年7月1日 11:09