FreeRTOS 事件标记组
什么是事件标记组?
事件标记组是一个24(在 FreeRTOS 中,默认情况下,事件标记组的位数是 24 位)位的变量,每一位(bit)可以独立地被设置或清除。任务可以等待一个或多个事件标记被设置,并且可以选择等待所有指定的事件标记都被设置或任意一个被设置。当某个事件发生时,任务可以设置对应的事件标记,通知等待这些标记的任务。
创建事件标记组
首先,在 Arduino 代码中,你需要创建一个事件标记组。在 setup()
函数中调用 xEventGroupCreate
来创建一个事件标记组:
cpp
#include <Arduino.h>
#include <FreeRTOS.h>
#include <event_groups.h>
EventGroupHandle_t xEventGroup;
void setup() {
Serial.begin(115200);
xEventGroup = xEventGroupCreate();
if (xEventGroup == NULL) {
// 事件标记组创建失败,可能是因为内存不足
while (1);
}
}
设置和清除事件标记
然后,你可以在任务中设置和清除事件标记。例如,你可以使用 xEventGroupSetBits
来设置事件标记,使用 xEventGroupClearBits
来清除事件标记:
cpp
#define EVENT_BIT_0 (1 << 0)
#define EVENT_BIT_1 (1 << 1)
void task1(void *pvParameters) {
for (;;) {
// 模拟某些工作
vTaskDelay(pdMS_TO_TICKS(500));
// 设置事件标记
xEventGroupSetBits(xEventGroup, EVENT_BIT_0);
}
}
void task2(void *pvParameters) {
for (;;) {
// 清除事件标记
xEventGroupClearBits(xEventGroup, EVENT_BIT_0);
// 模拟某些工作
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
等待事件标记
最后,在另一个任务中,你可以等待事件标记的设置。使用 xEventGroupWaitBits
函数来等待事件标记:
cpp
void task3(void *pvParameters) {
EventBits_t uxBits;
const TickType_t xTicksToWait = pdMS_TO_TICKS(1000);
for (;;) {
uxBits = xEventGroupWaitBits(
xEventGroup,
EVENT_BIT_0,
pdTRUE, // 等待后清除事件标记
pdFALSE, // 等待任意一个事件标记被设置
xTicksToWait
);
if ((uxBits & EVENT_BIT_0) != 0) {
// 事件标记被设置,执行相关操作
}
}
}
将任务添加到调度器中
最后,将创建的任务添加到 FreeRTOS 的任务调度器中:
cpp
void setup() {
// 创建事件标记组
xEventGroup = xEventGroupCreate();
if (xEventGroup == NULL) {
// 事件标记组创建失败,可能是因为内存不足
while (1);
}
// 创建任务
xTaskCreate(task1, "Task 1", 10000, NULL, 1, NULL);
xTaskCreate(task2, "Task 2", 10000, NULL, 1, NULL);
xTaskCreate(task3, "Task 3", 10000, NULL, 1, NULL);
// 启动任务调度器
vTaskStartScheduler();
}
完整示例
在这个示例中,我们将创建两个任务:一个任务会定期地设置事件标记,另一个任务会等待事件标记的设置,并在事件标记被设置时输出消息到串口。
cpp
#include <Arduino.h>
#include <FreeRTOS.h>
#include <event_groups.h>
// 定义事件标记位
#define EVENT_BIT_0 (1 << 0)
// 声明事件标记组句柄
EventGroupHandle_t xEventGroup;
// Task1: 定期设置事件标记
void task1(void *pvParameters) {
for (;;) {
// 模拟某些工作
delay(1000);
// 设置事件标记
xEventGroupSetBits(xEventGroup, EVENT_BIT_0);
}
}
// Task2: 等待事件标记的设置并输出消息
void task2(void *pvParameters) {
EventBits_t uxBits;
const TickType_t xTicksToWait = pdMS_TO_TICKS(1000);
for (;;) {
// 等待事件标记
uxBits = xEventGroupWaitBits(
xEventGroup,
EVENT_BIT_0,
pdTRUE, // 等待后清除事件标记
pdFALSE, // 等待任意一个事件标记被设置
xTicksToWait
);
if ((uxBits & EVENT_BIT_0) != 0) {
// 事件标记被设置,输出消息到串口
Serial.println("Event bit 0 is set!");
}
}
}
void setup() {
Serial.begin(115200);
// 创建事件标记组
xEventGroup = xEventGroupCreate();
if (xEventGroup == NULL) {
// 事件标记组创建失败,可能是因为内存不足
while (1);
}
// 创建任务
xTaskCreate(task1, "Task 1", 10000, NULL, 1, NULL);
xTaskCreate(task2, "Task 2", 10000, NULL, 1, NULL);
// 启动任务调度器
vTaskStartScheduler();
}
void loop() {
// loop 函数为空,不需要额外的代码
}
在这个示例中,Task1 每隔 1 秒就会设置事件标记,而 Task2 每隔 1 秒就会检查事件标记是否被设置,如果事件标记被设置,它就会输出一条消息到串口。这样,我们就实现了两个任务之间的简单同步通信。
API汇总
当使用 FreeRTOS 中的事件标记组(Event Groups)时,有几个关键的 API 函数需要理解。
1. xEventGroupCreate
cpp
EventGroupHandle_t xEventGroupCreate(void)
- 功能:创建一个事件标记组。
- 参数:无。
- 返回值:如果创建成功,返回一个事件标记组的句柄;如果创建失败,返回 NULL。
- 说明:该函数用于在内存中动态分配一个事件标记组,并返回该事件标记组的句柄,以供其他函数使用。如果内存不足或其他原因导致创建失败,将返回 NULL。
2. xEventGroupWaitBits
cpp
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToWaitFor, BaseType_t xClearOnExit, BaseType_t xWaitForAllBits, TickType_t xTicksToWait)
- 功能:等待事件标记组中的特定事件标记被设置。
- 参数 :
xEventGroup
:要等待的事件标记组的句柄。uxBitsToWaitFor
:一个位掩码,指定要等待的事件标记的组合。xClearOnExit
:如果为 pdTRUE,则在函数返回时自动清除已经设置的事件标记;如果为 pdFALSE,则不清除。xWaitForAllBits
:如果为 pdTRUE,则只有当所有指定的事件标记都被设置时,函数才会返回;如果为 pdFALSE,则只要任何一个指定的事件标记被设置,函数就会返回。xTicksToWait
:等待的最长时间(以 FreeRTOS 的 Tick 单位表示)。
- 返回值:如果等待时间超时,则返回0;否则返回一个位掩码,表示已经设置的事件标记。
- 说明:该函数用于在任务中等待指定的事件标记被设置。可以选择是等待所有指定的事件标记都被设置,还是只等待任意一个事件标记被设置。还可以选择在等待结束时自动清除已经设置的事件标记。
3. xEventGroupSetBits
cpp
BaseType_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
- 功能:设置事件标记组中的指定事件标记。
- 参数 :
xEventGroup
:要设置事件标记的事件标记组的句柄。uxBitsToSet
:一个位掩码,指定要设置的事件标记。
- 返回值:如果设置成功,则返回 pdPASS;否则返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。
- 说明:该函数用于设置事件标记组中的指定事件标记,使其被置位。多个事件标记可以通过位掩码的方式同时设置。
4. xEventGroupClearBits
cpp
BaseType_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)
- 功能:清除事件标记组中的指定事件标记。
- 参数 :
xEventGroup
:要清除事件标记的事件标记组的句柄。uxBitsToClear
:一个位掩码,指定要清除的事件标记。
- 返回值:如果清除成功,则返回 pdPASS;否则返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。
- 说明:该函数用于清除事件标记组中的指定事件标记,使其被清零。多个事件标记可以通过位掩码的方式同时清除。
xEventGroupSync()
是 FreeRTOS 中的一个高级事件标记组 API 函数,它允许多个任务在特定的事件发生时同时被唤醒,以便它们可以同时执行相关操作。
5. xEventGroupSync
c
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait
);
参数:
xEventGroup
:要操作的事件标记组的句柄。uxBitsToSet
:要设置的事件标记的位掩码。uxBitsToWaitFor
:要等待的事件标记的位掩码。xTicksToWait
:等待的最长时间(以 FreeRTOS 的 Tick 单位表示)。
返回值:
函数将等待 uxBitsToWaitFor
中的所有位都被设置,并在等待结束后将 uxBitsToSet
中的位设置为 1。如果在超时之前所有 uxBitsToWaitFor
的位都被设置,则返回事件标记组的当前状态;否则,在超时时返回 0。
功能:
xEventGroupSync
函数在FreeRTOS中用于同步事件标记组。这个函数的工作方式是:首先设置指定的位,然后等待其他的位被设置。
当你调用xEventGroupSync
函数时,它会首先设置uxBitsToSet
指定的位,然后等待uxBitsToWaitFor
指定的所有位都被设置。如果在xTicksToWait
指定的时钟节拍数内,所有的位都被设置,那么函数就会返回。否则,它会继续等待。
因此,对于xEventGroupSync
函数来说,是先设置位,然后等待位。
注意使用 xEventGroupSync()
时要小心,确保正确设置和等待事件标记,以避免死锁或其他同步问题。
结论
在 ESP32 上使用 FreeRTOS 的事件标记组可以实现多任务之间的同步和通信。然而,尽管事件标记组在某些情况下可能非常有用,但在事件标记组的操作下(如设置、清除、等待位等)可能会比其他同步机制(如互斥锁或信号量)更复杂。如果你的应用程序不需要事件标记组提供的灵活性,那么使用更简单的同步机制可能会更好。