13.FreeRTOS使用事件标记组

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 的事件标记组可以实现多任务之间的同步和通信。然而,尽管事件标记组在某些情况下可能非常有用,但在事件标记组的操作下(如设置、清除、等待位等)可能会比其他同步机制(如互斥锁或信号量)更复杂。如果你的应用程序不需要事件标记组提供的灵活性,那么使用更简单的同步机制可能会更好。

相关推荐
GY35091 小时前
AC7801时钟配置流程
单片机·嵌入式硬件·车载系统·车载mcu·系统时钟
LaoZhangGong1231 小时前
51单片机第22步_interrupt和using关键字
单片机·嵌入式硬件·51单片机
LaoZhangGong1231 小时前
51单片机第27步_单片机工作在睡眠模式
单片机·嵌入式硬件·51单片机
如图所示z1 小时前
单片机IO
单片机·嵌入式硬件
sqzrzq3 小时前
STM32——GPIO(点亮LED)
stm32·单片机·嵌入式硬件
qq_392399903 小时前
STM32 SWD烧写
stm32·单片机·嵌入式硬件
岁心5 小时前
高性价比模块:LSYT201B语音模块学习使用
stm32·单片机·嵌入式硬件·学习·智能家居·语音识别
guaizaiguaizai6 小时前
STM32之五:TIM定时器(2-通用定时器)
stm32·单片机·嵌入式硬件·输入捕获·pwm·输出比较
LaoZhangGong12315 小时前
51单片机第2步_printf()函数应用
单片机·嵌入式硬件·51单片机
嵌入式详谈16 小时前
基于STM32的智能仓储温湿度监控系统
stm32·单片机·嵌入式硬件