事件组

事件位:用于指示事件是否发生,事件位通常称为事件标志;
事件组:就是一组事件位。事件组中的事件位通过位编号来引用;

![[Pasted image 20250518153023.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

//创建一个事件组,返回事件组句柄,失败返回NULL
EventGroupHandle_t xEventGroupCreate(void);

//等待事件组中某个标志位,用返回值以确定哪些位已完成设置
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup,//事件组句柄
const EventBits_t uxBitsToWaitFor,//哪些位需要等待
const BaseType_t xClearOnExit,//是否自动清除标志位
const BaseType_t xWaitForAllBits,//是否等待的标志位都成功了才返回
TickType_t xTicksToWait//最大阻塞时间
);

//设置标志位
EventBits xEventGroupSetBits(
EventGroupHandle_t xEventGroup,//事件组句柄
const EventBits_t uxBitsToSet//设置哪个位
)

//清除标志位
EventBits xEventGroupClearBits(
EventGroupHandle_t xEventGroup,//事件组句柄
const EventBits_t uxBitsToClear//清除的标志位
)

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "esp_log.h"

// 定义事件位(位掩码)
#define NUM0_BIT BIT0 // 即 0x01,事件位0
#define NUM1_BIT BIT1 // 即 0x02,事件位1

// 定义一个事件组句柄
static EventGroupHandle_t test_event;

// 任务A:每隔1秒设置一次事件位
void taskA(void *param)
{
while (1)
{
// 设置事件位 BIT0
xEventGroupSetBits(test_event, NUM0_BIT);
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒

// 设置事件位 BIT1
xEventGroupSetBits(test_event, NUM1_BIT);
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒
}
}

// 任务B:等待事件位的触发
void taskB(void *param)
{
EventBits_t ev;
while (1)
{
// 等待事件位 BIT0 或 BIT1 被设置,超时为5000毫秒
// pdTRUE 表示接收到事件后清除这些位
// pdFALSE 表示“或”逻辑,只要任意一位被置位就满足条件
ev = xEventGroupWaitBits(test_event, NUM0_BIT | NUM1_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(5000));

// 检查是哪一个事件位被设置了
if (ev & NUM0_BIT)
{
ESP_LOGI("ev", "get BIT0 event");
}
if (ev & NUM1_BIT)
{
ESP_LOGI("ev", "get BIT1 event");
}
}
}

// 主函数
void app_main(void)
{
// 创建事件组
test_event = xEventGroupCreate();

// 创建任务A:设置事件位
xTaskCreatePinnedToCore(taskA, "taskA", 2048, NULL, 3, NULL, 1);

// 创建任务B:等待事件位
xTaskCreatePinnedToCore(taskB, "taskB", 2048, NULL, 3, NULL, 1);
}

内容 解释
EventGroupHandle_t 事件组类型,用于管理多个事件位
xEventGroupSetBits() 设置一个或多个事件位
xEventGroupWaitBits() 等待某些事件位被置位
pdTRUE(清除标志) 指定等待完成后是否自动清除这些事件位
pdFALSE(等待所有位) 指定是否等待“所有位”置位(pdTRUE)或“任意一位”置位(pdFALSE)
![[Pasted image 20250518155912.png]]

直达任务通知

每个RTOS任务都有一个任务通知组。每条任务通知 都有“挂起”或“非挂起”的通知状态,以及一个32位通知值。
直达任务通知是直接发送至任务的事件,而不是通过中间对象(如队列、事件组或信号量)间接发送至任务的事件。
向任务发送“直达任务通知”会将目标任务通知设为“挂起”状态(此挂起不是挂起任务)。

用直达任务通知的时候,无需再初始化或新建其他对象句柄,直接使用任务句柄来操作通知的发送。
说白了就是两个任务间可以直接通信,而不是像队列一样通过变量间接完成。

![[Pasted image 20250518154256.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//用于将通知直接诶发送到RTOS任务并可能取消该任务的阻塞状态
BaseType_t xTaskNotify(
TaskHandle_t xTaskToNotify,//要通知的任务句柄
uint32_t ulValue,//携带的通知值
eNotifyAction eAction,//执行的操作
);


//等待接收任务通知
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,//进入函数清除的通知值位
uint32_t ulBitsToClearOnExit,//退出函数清除的通知值位
uint32_t * pulNotificationValue,//通知值
TickType_t xTicksToWait//等待时长
);

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h" // 此处未使用 event_groups,可省略
#include "freertos/task.h"
#include "esp_log.h"

// 声明两个任务句柄
static TaskHandle_t taskA_handle;
static TaskHandle_t taskB_handle;

// 任务A:定时向任务B发送任务通知
void taskA(void *param)
{
uint32_t value = 0; // 通知的值
vTaskDelay(pdMS_TO_TICKS(200)); // 启动延时 200ms,确保任务B已经创建

while (1)
{
// 向任务B发送通知,附带 value 值
// 使用 eSetValueWithoutOverwrite 模式:如果任务B未接收上次通知,则不覆盖
xTaskNotify(taskB_handle, value, eSetValueWithoutOverwrite);

vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒发送一次通知
value++; // 通知值递增
}
}

// 任务B:等待并接收任务通知值,打印日志
void taskB(void *param)
{
uint32_t value; // 接收到的通知值

while (1)
{
// 等待任务通知,阻塞时间为 portMAX_DELAY(即永久阻塞直到收到通知)
// 第一个参数:不清除任何已有的通知位
// 第二个参数:清除所有通知位
// 第三个参数:接收通知值
xTaskNotifyWait(0x00, ULONG_MAX, &value, portMAX_DELAY);

// 打印接收到的通知值
ESP_LOGI("ev", "notify wait value:%lu", value);
}
}

// 主函数
void app_main(void)
{
// 创建任务A,并将任务句柄赋值给 taskA_handle
xTaskCreatePinnedToCore(taskA, "taskA", 2048, NULL, 3, &taskA_handle, 1);

// 创建任务B,并将任务句柄赋值给 taskB_handle(必须,任务A要用到)
xTaskCreatePinnedToCore(taskB, "taskB", 2048, NULL, 3, &taskB_handle, 1);
}

![[Pasted image 20250518163208.png]]