FreeRTOS基础学习(六)事件组

目录

一.概念

1.什么是事件组?

2.事件组机制的原理

3.常见使用场景

二.常用函数

1.xEventGroupCreate()创建事件组

(1)函数原型

(2)返回值

(3)示例

(4)注意事项

2.xEventGroupSetBits() 设置事件组中某些位(设为1)

(1)函数原型

(2)返回值

(3)示例

(4)使用场景

(5)注意事项

3.xEventGroupClearBits() —— 清除指定事件位

(1)函数原型

(2)返回值

(3)示例

(4)使用场景

(5)注意:

4.xEventGroupWaitBits —— 任务阻塞等待事件组中某些位变为1

(1)函数原型

(2)参数讲解

(3)返回值

(4)示例

(5)使用场景

5.xEventGroupSync() 任务同步屏障函数

(1)函数原型

(2)参数解释

(3)返回值

(4)示例

(5)使用场景

6.xEventGroupClearBitsFromISR()

(1)函数原型

(2)参数解释

(3)返回值

(4)示例

(5)使用场景

(6)注意事项

7.xEventGroupSetBitsFromISR()在中断中设置事件位

(1)函数原型

(2)参数解释

(3)返回值

(3)示例

8.xEventGroupGetBits 获取当前事件组的全部位值

(1)函数原型

(2)参数解释

(4)使用场景

9.xEventGroupGetBitsFromISR()从中断中读取事件组

(1)函数原型

(2)参数解释

(3)返回值

(4)示例

(5)使用场景

10.vEventGroupDelete()删除事件组对象

(1)函数原型

(2)参数解释

(3)示例

(4)使用场景

(5)注意


一.概念
1.什么是事件组?

事件组是 FreeRTOS 提供的一个机制,用来管理和同步多个事件标志(每个位代表一个事件)。

每个事件位是 uint32_t 类型的一个位(共32位),可以用来表示不同事件的发生状态。

你可以把事件组理解为一个共享的 32位变量,每一位(bit)代表一个“事件”的状态(0或1),

任务之间可以设置、清除、等待这些位,来完成类似“谁完成了什么事”、“等谁先完成”这种协调。

在 FreeRTOS 的事件组(EventBits_t) 中,事件位是从右往左数的(低位在右,高位在左)。

2.事件组机制的原理

事件组其实是一个结构体,包含:

  • 32 位的事件变量(bit pattern);

  • 等待该事件组的任务列表;

  • 同步机制:等待、唤醒、触发事件都通过 FreeRTOS 的内核调度实现;

3.常见使用场景
场景
说明
任务同步
等待多个任务或条件都满足后再执行下一步
异步事件通知
某个任务或中断设置一个事件位,通知其他任务处理
资源管理
用某些位标志资源的状态(空闲/占用)
网络状态同步
多个子系统依赖网络连接状态,某任务设置 BIT_NET_READY后多个任务继续
多按键状态检测
多个按键分别对应一个位,检测任意一个被按下触发响应任务
二.常用函数
1.xEventGroupCreate()创建事件组

用于动态地创建一个新的事件组,并返回它的句柄(EventGroupHandle_t),你可以用这个句柄在后续对这个事件组进行各种操作,比如设置、等待、清除等。

(1)函数原型
EventGroupHandle_t xEventGroupCreate( void );
(2)返回值
  • 返回一个 事件组句柄(EventGroupHandle_t),表示成功创建;

  • 如果返回 NULL,表示创建失败(例如内存不足)。

(3)示例
EventGroupHandle_t event_group;

void app_main(void)
{
    event_group = xEventGroupCreate();  // 创建事件组

    if (event_group == NULL)
    {
        ESP_LOGE("EVENT", "事件组创建失败!");
    }
    else
    {
        ESP_LOGI("EVENT", "事件组创建成功!");
    }
}
(4)注意事项
  1. 事件组占用 RAM,默认是动态分配。FreeRTOS 会从 heap 里分配一个结构体存储 32 位的事件位图和任务等待列表。

  2. 不能在中断中调用!这是一个创建对象的函数,只能在任务上下文中调用,不能在 ISR 中使用。

  3. 句柄很重要,你必须把返回的 EventGroupHandle_t 保存在全局或共享变量中,后面设置、等待事件都要用这个句柄。

2.xEventGroupSetBits() 设置事件组中某些位(设为1)

将事件组中你指定的某几位设置为 1,如果有任务正在等待这些位,就会立刻唤醒它们。

(1)函数原型
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);
//参数1:xEventGroup:要设置的事件组句柄;
//参数2:EventGroupHandle_t:要设置为 1 的位,多个位可以用,类型是EventBits_t(32位无符号整数);
(2)返回值
  • 返回设置前的事件位值(非设置后的值)。

  • 返回“设置前”的值,可以:用来判断先前有哪些事件已经发生;避免 race condition(竞争条件);有时用于调试或逻辑判断。

(3)示例
// 假设事件组已创建为 eventGroup

// 任务A
void taskA(void *pvParam) {
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
        xEventGroupSetBits(eventGroup, BIT0);  // 设置第 0 位
        ESP_LOGI("TASK A", "Set BIT0");
    }
}

// 任务B
void taskB(void *pvParam) {
    while (1) {
        EventBits_t bits = xEventGroupWaitBits(
            eventGroup, BIT0, pdTRUE, pdFALSE, portMAX_DELAY);
        if (bits & BIT0) {
            ESP_LOGI("TASK B", "BIT0 received!");
        }
    }
}
(4)使用场景
  • 任务之间的同步 :如任务A设置事件位,任务B等待事件位,被唤醒后继续执行

  • 多个任务等待不同事件:如每个任务等一个或多个特定位,被相应任务设置时唤醒 ;

(5)注意事项
  • xEventGroupSetBits()的返回值是你设置位之前的事件组里的原始状态。就像你打开灯之前,先告诉你屋里有几盏灯已经亮着;

  • 中断中不能使用 xEventGroupSetBits(),需要用 xEventGroupSetBitsFromISR();

3.xEventGroupClearBits() —— 清除指定事件位

这个函数用于清除事件组中的某些位(即将它们置为0)。它和 xEventGroupSetBits() 相反,它不会触发阻塞在事件位上的任务,只是静默清除某些位。

(1)函数原型
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToClear);
//参数1:xEventGroup事件组句柄(由 xEventGroupCreate()创建)
//参数2:uxBitsToClear:要清除的位(如BIT0,就是代表要清除第0位)
(2)返回值
  • 函数返回调用前事件组中的位值(而不是清除之后的值)。

(3)示例

事件组多任务同步 + 清除事件位:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include <stdio.h>

#define BIT_TASK_A_DONE (1 << 0)
#define BIT_TASK_B_DONE (1 << 1)

static EventGroupHandle_t event_group;

void task_a(void *arg) {
    printf("任务A开始工作...\n");
    vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟工作耗时1秒
    printf("任务A完成,设置事件位 BIT_TASK_A_DONE\n");
    xEventGroupSetBits(event_group, BIT_TASK_A_DONE);
    vTaskDelete(NULL);//只执行一次“工作”,然后销毁自己,释放任务栈和资源。
}

void task_b(void *arg) {
    printf("任务B开始工作...\n");
    vTaskDelay(pdMS_TO_TICKS(1500)); // 模拟工作耗时1.5秒
    printf("任务B完成,设置事件位 BIT_TASK_B_DONE\n");
    xEventGroupSetBits(event_group, BIT_TASK_B_DONE);
    vTaskDelete(NULL);//只执行一次“工作”,然后销毁自己,释放任务栈和资源。
}

void main_task(void *arg) {
    while (1) {
        printf("主任务等待任务A和任务B完成...\n");
        EventBits_t bits = xEventGroupWaitBits(event_group,
                                              BIT_TASK_A_DONE | BIT_TASK_B_DONE,
                                              pdTRUE,    // 等待到后清除事件位
                                              pdTRUE,    // 等待所有位都被设置
                                              portMAX_DELAY);
        if ((bits & (BIT_TASK_A_DONE | BIT_TASK_B_DONE)) == (BIT_TASK_A_DONE | BIT_TASK_B_DONE)) {
            printf("主任务检测到任务A和任务B都已完成!事件位值:0x%02X\n", bits);
            printf("主任务执行后续处理...\n");
        }

        // 模拟后续处理时间
        vTaskDelay(pdMS_TO_TICKS(2000));

        // 清除事件位
        xEventGroupClearBits(event_group, BIT_TASK_A_DONE | BIT_TASK_B_DONE);
        printf("主任务清除了事件位,准备下一轮等待\n");
    }
}

void app_main(void) {
    event_group = xEventGroupCreate();
    if (event_group == NULL) {
        printf("事件组创建失败!\n");
        return;
    }

    xTaskCreate(task_a, "TaskA", 2048, NULL, 5, NULL);
    xTaskCreate(task_b, "TaskB", 2048, NULL, 5, NULL);
    xTaskCreate(main_task, "MainTask", 4096, NULL, 5, NULL);
}
(4)使用场景
  • 手动清除某些状态位:某些任务完成后,你想手动清除它设置的事件位,避免它影响后续逻辑;

  • 防止重复触发:比如中断设置事件位,任务处理完毕后要清零它;

  • 配合 xEventGroupWaitBits() 使用,某些模式中你需要手动清除位;

(5)注意:
  • 此函数是线程安全的(可在任务中使用);

  • 不能在中断中使用,在 ISR 中应使用 xEventGroupClearBitsFromISR()。

  • 它不会唤醒等待这个事件的任务,只改变事件组内部的值;

4.xEventGroupWaitBits —— 任务阻塞等待事件组中某些位变为1

用于等待事件组中某些事件位被设置。它是任务用来挂起自己,直到某些条件满足的方式 ;

(1)函数原型
EventBits_t xEventGroupWaitBits(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToWaitFor,
    const BaseType_t xClearOnExit,
    const BaseType_t xWaitForAllBits,
    TickType_t xTicksToWait
);
(2)参数讲解

序号

参数

类型

说明

注意

1

xEventGroup

EventGroupHandle_t

事件组句柄,即你要等待的事件组。由 xEventGroupCreate()创建获得;

2

uxBitsToWaitFor

const EventBits_t

指定你要“等待”的事件位(位掩码形式)

3

xClearOnExit

const BaseType_t

如果为 pdTRUE,则任务被唤醒后自动清除满足条件的位。

当任务通过 xEventGroupWaitBits() 被成功唤醒(即:等待的事件条件被满足)后:

  • xClearOnExit = pdTRUE,那么 你等待的那些事件位会被自动清除为 0。这意味着:其他任务如果也在等这些位,可能会检测不到它们被设置过。xClearOnExit = pdTRUE 就是:谁用了就清掉,后来的任务可能就“看不到”这位是1了。

  • xClearOnExit = pdFalse那么任务被唤醒后,不会清除任何事件位。其他任务如果也在等待 BIT0,就还能“看到”BIT0 是 1。直到某个任务手动调用 xEventGroupClearBits() 来清除它。

4

xWaitForAllBits

const BaseType_t

pdTRUE表示等待所有指定位都为1;pdFALSE表示任意一个即可。

如果你等待的事件位只有一位(如 BIT0),pdTRUEpdFALSE 没有区别,写哪个都可以。为了代码更清晰、规范,建议仍然显式写明你的意图---如果是想等待“所有”的位被置位,所以最好写pdTURE,当前是只有1位就是等这1位,那如果以后哪天你想等多个位,就不用改结构了。

5

xTicksToWait

TickType_t

最多等待多长时间(Tick)。0 表示立即检查一次后不等待,portMAX_DELAY表示一直等

它的单位是 FreeRTOS 系统节拍(tick),而不是毫秒;如果你想用毫秒,可以用宏 pdMS_TO_TICKS(毫秒数) 转换。

xTicksToWait规定时间内变成你想要的状态(比如BIT1和BIT2都是1),那任务就会被唤醒,然后执行xEventGroupWaitBits

这行函数下面的程序;

(3)返回值
  • 返回当任务被唤醒时事件组当前所有的事件位值(包括你等待的位和没等待的位)。

  • 如果超时未满足条件,则返回当时的事件位值(不一定满足等待条件)。

(4)示例
#define BIT_0 (1 << 0)//0x01
#define BIT_1 (1 << 1)//0x02

EventGroupHandle_t my_event_group;//事件组句柄

void task_A(void *pvParameters)
{
    while (1)
    {
        EventBits_t bits = xEventGroupWaitBits(
            my_event_group,
            BIT_0 | BIT_1,  // 等待任意一个
            pdTRUE,         // 满足条件后自动清除这些位
            pdFALSE,        // 只要任意一个满足即可
            portMAX_DELAY   // 一直等
        );
        if (bits & BIT_0)
        {
            printf("任务A:事件 BIT_0 被设置了!\n");
        }
        if (bits & BIT_1)
        {
            printf("任务A:事件 BIT_1 被设置了!\n");
        }
        //情况1:BIT_0 被设置了,BIT_1 没被设置:TaskA被唤醒,(bits & BIT_0)->(0x01 & 0x01)成立,进入第1个if;
        //情况2:BIT_0 没被设置,BIT_1 被设置了:TaskA被唤醒,(bits & BIT_1)->(0x02 & 0x02)成立,进入第2个if;
        //情况3:BIT_0和BIT_1 都被设置了:TaskA被唤醒,(bits & BIT_1)->(0x03 & 0x01)成立,(0x03 & 0x02)也成立;
    }
}
(5)使用场景
  • 多任务同步:多个任务等待同一事件组中不同的位;

  • 等待多个外设 :比如任务等待“串口接收完成”和“DMA完成”两个事件;

  • 实现类似 “任务屏障” :所有任务都等某位设好后才继续;

  • 替代 ulTaskNotify 实现多状态同步 :比如任务需要等待多个输入来源状态;

(6)注意:

  • 中断中不能用 xEventGroupWaitBits() ,只能在任务上下文中使用。

  • 配合 xEventGroupSetBitsFromISR() 可在中断中设置事件位,任务等待响应。

  • 如果 xClearOnExit = pdTRUE,注意其他任务等待相同事件时也会被影响。

  • 等待多个事件时,xWaitForAllBits = pdTRUE 可实现“所有条件满足再唤醒”。

5.xEventGroupSync() 任务同步屏障函数

这是一个专门用于任务之间同步(同步屏障)的函数,让多个任务“在某个点上集合”,所有人都到了之后再一起继续。假设你有 3 个任务:A、B、C;

  • 每个任务调用 xEventGroupSync() 时,会设置自己的位(通过 uxBitsToSet);

  • 系统会等到 uxBitsToWaitFor 指定的所有位都被设置,才唤醒所有调用这个函数的任务;

  • 如果等待超时,函数提前返回(但返回值可以查看当前事件位);

(1)函数原型
EventBits_t xEventGroupSync(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet,
    const EventBits_t uxBitsToWaitFor,
    TickType_t xTicksToWait
);
(2)参数解释
  • 参数1:xEventGroup:事件组句柄;

  • 参数2:uxBitsToSet:当前任务“到达同步点”时,要设置的事件位;

  • 参数3:uxBitsToWaitFor :等待的事件位(即:多少人集合齐才能继续);

  • 参数4:xTicksToWait:最长等待时间(tick);

(3)返回值
  • 当前事件组中的所有事件位值(不是只看你设置或等待的那些),可以用它来判断“谁到了,谁还没到”。

//你可以通过 位运算(&)来检查你关心的事件位是否已经就绪:
if (result & BIT1) {
    // 说明 BIT1 对应的任务已经到达同步点
}
(4)示例

假设有3个任务,需要大家在初始化完后统一开始工作。

#define TASK_1_BIT    BIT0
#define TASK_2_BIT    BIT1
#define TASK_3_BIT    BIT2

void task1(void *pvParameters) {
    // 初始化完成
    printf("Task 1 ready\n");
    xEventGroupSync(event_group, TASK_1_BIT, TASK_1_BIT | TASK_2_BIT | TASK_3_BIT, portMAX_DELAY);
    printf("Task 1 all ready, start working\n");
    vTaskDelete(NULL);
}

void task2(void *pvParameters) {
    printf("Task 2 ready\n");
    xEventGroupSync(event_group, TASK_2_BIT, TASK_1_BIT | TASK_2_BIT | TASK_3_BIT, portMAX_DELAY);
    printf("Task 2 all ready, start working\n");
    vTaskDelete(NULL);
}

void task3(void *pvParameters) {
    printf("Task 3 ready\n");
    xEventGroupSync(event_group, TASK_3_BIT, TASK_1_BIT | TASK_2_BIT | TASK_3_BIT, portMAX_DELAY);
    printf("Task 3 all ready, start working\n");
    vTaskDelete(NULL);
}
(5)使用场景
  • 1. 多个任务协同启动:在多个任务中都调用 xEventGroupSync(),等到大家全部都准备好后,一起继续工作。 例如多个传感器任务初始化完毕,统一开始采集数据;

  • 2. 实现任务之间阶段交汇点的同步。:比如你有任务 A 和 B,分两个阶段进行:

  • 阶段 1:先初始化 → xEventGroupSync() 等大家都初始化完;

  • 阶段 2:处理 → 继续执行后续逻辑;

  • 3.多任务协同完成:多个任务完成自己的子任务后,用 xEventGroupSync() 同步集合,通知主任务“所有子任务都完成了”。类似于“所有工人都干完了,通知老板验收”;

6.xEventGroupClearBitsFromISR()

在中断服务程序(ISR)中清除事件组的指定事件位,并通知是否需要任务切换。

(1)函数原型
BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,
                                      EventBits_t uxBitsToClear,
                                      BaseType_t *pxHigherPriorityTaskWoken);
(2)参数解释
  • 参数1:xEventGroup事件组句柄

  • 参数2:uxBitsToClear要清除的事件位(位掩码)

  • 参数3:pxHigherPriorityTaskWoken指针,调用后会告诉你是否需要触发上下文切换(任务切换)

(3)返回值

返回是否有事件位被成功清除:

  • 非零(pdTRUE)表示清除成功且可能需要任务切换。

  • 零(pdFALSE)表示没有清除任何事件位。

(4)示例
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"

#define TAG "EventGroupClearISR"

// 定义事件位
#define BIT_EVENT (1 << 0)//0x01

// 事件组句柄
static EventGroupHandle_t event_group;

// 模拟中断服务函数(ISR)
void IRAM_ATTR gpio_isr_handler(void *arg)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    // 在ISR中清除事件位
    xEventGroupClearBitsFromISR(event_group, BIT_EVENT);

    // 通知是否需要切换任务
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void task_wait_event(void *arg)
{
    while (1)
    {
        // 等待事件位 BIT_EVENT 被设置
        EventBits_t bits = xEventGroupWaitBits(event_group, BIT_EVENT, pdTRUE, pdTRUE, portMAX_DELAY);
        if (bits & BIT_EVENT)
        {
            ESP_LOGI(TAG, "Event BIT_EVENT detected in task");
        }
        else
        {
            ESP_LOGI(TAG, "Event BIT_EVENT was cleared");
        }
    }
}

void app_main()
{
    // 创建事件组
    event_group = xEventGroupCreate();

    // 初始化事件组,将 BIT_EVENT 置1,表示事件存在
    xEventGroupSetBits(event_group, BIT_EVENT);

    // 配置GPIO中断模拟ISR触发,具体GPIO和配置视实际硬件而定
    // 这里只是示例,不配置真实GPIO中断
    // gpio_install_isr_service(0);
    // gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, NULL);

    // 创建等待事件的任务
    xTaskCreate(task_wait_event, "task_wait_event", 2048, NULL, 5, NULL);

    // 模拟延时后,在主任务中模拟ISR清除事件位
    vTaskDelay(pdMS_TO_TICKS(5000));

    ESP_LOGI(TAG, "Simulate ISR clearing event bit...");
    xEventGroupClearBitsFromISR(event_group, BIT_EVENT);
}
(5)使用场景
  • 中断服务函数内清除事件位,比如按钮中断触发,任务需要停止等待某事件。

  • 中断内触发任务切换,确保实时响应。

  • 用于中断与任务间同步。

(6)注意事项
  • 必须传入 pxHigherPriorityTaskWoken,用于判断是否需要切换任务。

  • 只能在 ISR 中调用,不能在普通任务中调用。

7.xEventGroupSetBitsFromISR()在中断中设置事件位

专门用于中断服务函数(ISR)中设置事件位的版本,它的作用和 xEventGroupSetBits() 类似,但可以在中断中安全使用。

(1)函数原型
BaseType_t xEventGroupSetBitsFromISR(
    EventGroupHandle_t xEventGroup,
    const EventBits_t uxBitsToSet,
    BaseType_t *pxHigherPriorityTaskWoken
);
(2)参数解释
  • 参数1:xEventGroup事件组句柄

  • 参数2:uxBitsToSet:你想设置的事件位(比如 `BIT0

  • 参数3:pxHigherPriorityTaskWoken:如果有更高优先级的任务因为设置事件位而被唤醒,需要设置为 pdTRUE,这样中断结束后就能立刻切换过去;

(3)返回值
  • pdPASS:设置成功;

  • pdFAIL:设置失败(比如事件组还没初始化);

(3)示例

有一个按键,按下按键时会触发中断,中断中设置事件位,任务中等待处理:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define GPIO_INPUT_IO    0                    // 按键输入引脚
#define GPIO_INPUT_PIN_SEL  (1ULL << GPIO_INPUT_IO)

#define BIT_BUTTON_EVENT  (1 << 0)            // 定义事件位 Bit0
 //事件位0,表示按键事件,这里只是定义,而不是真正触发第0位变成1,
//真正触发需要用函数setbits;
static const char *TAG = "EVENT_GROUP";
static EventGroupHandle_t event_group = NULL;
// 中断服务函数
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    // 设置事件位 BIT_BUTTON_EVENT
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xEventGroupSetBitsFromISR(event_group, BIT_BUTTON_EVENT, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// 等待任务
void button_event_task(void *arg)
{
    while (1)
    {
        // 等待事件组中 BIT_BUTTON_EVENT 被置位,等待后自动清除该位
        EventBits_t bits = xEventGroupWaitBits(
            event_group,
            BIT_BUTTON_EVENT,
            pdTRUE,     // xClearOnExit:退出前清除事件位
            pdTRUE,     // xWaitForAllBits:只等一个事件位,也无所谓
            portMAX_DELAY // 一直等待
        );

        if (bits & BIT_BUTTON_EVENT) {
            ESP_LOGI(TAG, "Button pressed event detected in task.");
            // 执行你想做的事情,比如翻转LED
        }
    }
}

// 初始化按键
void init_button_gpio()
{
    gpio_config_t io_conf = {
        .intr_type = GPIO_INTR_NEGEDGE,  // 按钮按下触发中断
        .mode = GPIO_MODE_INPUT,
        .pin_bit_mask = GPIO_INPUT_PIN_SEL,
        .pull_up_en = 1,
        .pull_down_en = 0,
    };
    gpio_config(&io_conf);

    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, NULL);
}

void app_main(void)
{
    // 创建事件组
    event_group = xEventGroupCreate();
    if (event_group == NULL) {
        ESP_LOGE(TAG, "Failed to create event group");
        return;
    }

    init_button_gpio();

    // 创建任务等待事件位
    xTaskCreate(button_event_task, "button_event_task", 2048, NULL, 10, NULL);
}
8.xEventGroupGetBits 获取当前事件组的全部位值

获取事件组中当前的事件位值。返回的是调用该函数时所有事件位(EventBits)的当前状态,是个uint32_t类型的值,每一位表示一个事件标志。

(1)函数原型
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
(2)参数解释
  • 参数1:xEventGroup事件组句柄

(3)返回值

当前事件组的 32 位状态

(3)示例

EventBits_t bits = xEventGroupGetBits(event_group);

if (bits & BIT_BUTTON_EVENT) {
    ESP_LOGI(TAG, "按钮事件已经触发了!");
}

这段代码不会阻塞,仅查看当前事件位状态;

(4)使用场景
  • 当你想检查某些事件位是否已被设置,但不阻塞等待;比如在某个任务中轮询事件状态,不想卡在 xEventGroupWaitBits() 上。

9.xEventGroupGetBitsFromISR()从中断中读取事件组

从中断服务程序(ISR)中获取事件组的当前事件位状态。

(1)函数原型
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);
(2)参数解释
  • 参数1:xEventGroup事件组句柄;

(3)返回值

当前事件组的 32 位状态;

(4)示例
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    EventBits_t bits = xEventGroupGetBitsFromISR(event_group);
    if (bits & BIT_BUTTON_EVENT) {
        ets_printf("当前按钮事件位为1\n");
    }
}
(5)使用场景

如果有 bits 变量就直接 printf,没有 bits 就调用 getBits,(在中断里只能用 xEventGroupGetBitsFromISR())

使用场景
推荐方式
说明
刚设置完事件位后
直接用 xEventGroupSetBits()的返回值打印出来;
此时返回的是设置后的完整事件组状态
想“随时查当前事件位”
调用 xEventGroupGetBits()
查整个事件组当前状态
中断里要查事件位
xEventGroupGetBitsFromISR()
专为中断服务安全设计
10.vEventGroupDelete()删除事件组对象

释放(删除)一个事件组,将其从内核中移除,并释放相关资源。

(1)函数原型
void vEventGroupDelete(EventGroupHandle_t xEventGroup);//void类型无返回值;
(2)参数解释
  • 参数:xEventGroup你要删除的事件组句柄。

(3)示例
EventGroupHandle_t my_event_group;

void example_task(void *pvParameters)
{
    my_event_group = xEventGroupCreate();

    if (my_event_group == NULL) {
        printf("创建事件组失败!\n");
        vTaskDelete(NULL);
    }

    // 使用事件组做一些事情……
    xEventGroupSetBits(my_event_group, (1 << 0));

    // 等待一会儿
    vTaskDelay(pdMS_TO_TICKS(5000));

    // 删除事件组
    if (my_event_group != NULL) {
    vEventGroupDelete(my_event_group);
    my_event_group = NULL; // 删除后置空,防止误用
}

    printf("事件组已删除\n");

    vTaskDelete(NULL);
}
(4)使用场景

当某个事件组已经不再需要使用,比如:

  • 程序退出前清理资源;

  • 某个模块动态创建了事件组,用完就要销毁;

  • 避免内存泄漏。

(5)注意
  • 不能有任务正在等待这个事件组,否则删除后可能会导致系统行为异常(比如等待的任务进入未知状态)。

  • 不能在中断中调用!

  • 删除后,该事件组句柄不可再使用,否则会访问无效内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值