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

相关推荐
电子科技圈3 小时前
芯科科技出展CES 2026并展出如何加速互联智能的未来
科技·嵌入式硬件·mcu·物联网·iot
点灯小铭8 小时前
基于单片机的多路热电偶温度监测与报警器
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
wzfj123458 小时前
FreeRTOS 学习方法
freertos
tianyue10012 小时前
STM32G431 ADC 多个channel 采集
stm32·单片机·嵌入式硬件
longson.14 小时前
怎样避免空间碎片而且高效的分配空间
嵌入式硬件·缓存
清风66666614 小时前
基于单片机的水泵效率温差法测量与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
焦糖码奇朵、14 小时前
课设:基于Arduino的无线LED开关控制系统
嵌入式硬件·物联网·arduino·信息与通信·信号处理
z203483152015 小时前
定时器练习报告
单片机·嵌入式硬件
zk0015 小时前
内容分类目录
单片机·嵌入式硬件
安生生申16 小时前
STM32 ESP8266连接ONENET
c语言·stm32·单片机·嵌入式硬件·esp8266