目录
2.xEventGroupSetBits() 设置事件组中某些位(设为1)
3.xEventGroupClearBits() —— 清除指定事件位
4.xEventGroupWaitBits —— 任务阻塞等待事件组中某些位变为1
6.xEventGroupClearBitsFromISR()
7.xEventGroupSetBitsFromISR()在中断中设置事件位
8.xEventGroupGetBits 获取当前事件组的全部位值
9.xEventGroupGetBitsFromISR()从中断中读取事件组
一.概念
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)注意事项
-
事件组占用 RAM,默认是动态分配。FreeRTOS 会从 heap 里分配一个结构体存储 32 位的事件位图和任务等待列表。
-
不能在中断中调用!这是一个创建对象的函数,只能在任务上下文中调用,不能在 ISR 中使用。
-
句柄很重要,你必须把返回的 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() 被成功唤醒(即:等待的事件条件被满足)后:
|
4 | xWaitForAllBits | const BaseType_t | 为pdTRUE表示等待所有指定位都为1;pdFALSE表示任意一个即可。 | 如果你等待的事件位只有一位(如 BIT0),pdTRUE 和 pdFALSE 没有区别,写哪个都可以。为了代码更清晰、规范,建议仍然显式写明你的意图---如果是想等待“所有”的位被置位,所以最好写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)注意
-
不能有任务正在等待这个事件组,否则删除后可能会导致系统行为异常(比如等待的任务进入未知状态)。
-
不能在中断中调用!
-
删除后,该事件组句柄不可再使用,否则会访问无效内存。