事件组等待(Event Wait)

事件组等待(Event Wait)

官方函数文档 304 6.3 xEventGroupCreate() [[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=304&selection=0,3,4,19|FreeRTOS_Reference_Manual_V10.0.0, page 304]] F

官方函数文档

304 6.3 xEventGroupCreate()

[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=304&selection=0,3,4,19|FreeRTOS_Reference_Manual_V10.0.0, page 304]]


FreeRTOS中事件组的目的和功能

FreeRTOS中的事件组时一种用于任务间同步和通信的机制,它允许多个任务通过设置和等待特定的事件来协调它们的行为。

例子:家庭照料

现在有三个任务,它们分别是:

  1. 妈妈:准备早餐
  2. 爸爸:送孩子上学
  3. 孩子:去上学

事件组的功能:

  1. 创建事件组:
    • 事件组类似于一个信号集合,每个信号表示一个特定的事件,例如“早餐准备好了”,“孩子准备好了”。
  2. 设置事件:
    • 当妈妈准备好早餐后,她会发出一个信号表示“早餐准备好了”。
    • 当孩子准备好去上学之后,他会发出一个信号表示“孩子准备好了”。
  3. 等待事件:
    • 爸爸需要等待早餐准备好并且孩子准备好,再去送孩子上学。

在FreeRTOS中的实现

  • 创建事件组:
EventGroupHandle_t eventGroup = xEventGroupCreate();
  • 事件定义:
    • 假设用事件位 0 ,表示“早餐准备好了”
    • 用事件位 1 ,表示“孩子准备好了”
#define EVENT_BIT_BREAKFAST_READY (1 << 0)
#define EVENT_BIT_CHILD_READY     (1 << 1)
  • 设置事件:
  1. 妈妈准备好早餐,设置事件位 0
xEventGroupSetBits(eventGroup, EVENT_BIT_BREAKFAST_READY);
  1. 孩子准备好上学,设置事件位 1
xEventGroupSetBits(eventGroup, EVENT_BIT_CHILD_READY);
  • 等待事件:
    • 爸爸任务等待“早餐准备好了”和“孩子准备好了”
xEventGroupWaitBits(eventGroup, 
				    EVENT_BIT_BREAKFAST_READY | EVENT_BIT_CHILD_READY,
				    pdTRUE,
				    pdTRUE,
				    portMAX_DELAY);

优点

  • 同步:事件组可以同步多个任务的行为,确保任务安特定顺序执行。
  • 灵活:一个事件组可以包含多个事件位,灵活控制不同的事件发生。
  • 简化代码:事件组机制是的任务之间的同步变得简单和直观,避免复杂的条件判断。

xEventGroupCreate()

#include "FreeRTOS.h"
#include "event_groups.h"

EventGroupHandle_t xEventGroupCreate( void );

函数说明:

  • 创建一个新的事件组。
  • RAM needed.
  • 事件组数据存储在EventGroupHandle_t中。设置configUSE_16_BIT_TICKS1则事件组有8位,设置为0则有24位。

参数:

  • None

返回值:

  • NULL:堆内存不足,创建事件组失败。
  • Any other value:返回一个事件组句柄。

xEventGroupSetBits()

#include "FreeRTOS.h"
#include "event_groups.h"

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );

函数说明:

  • 为RTOS中的事件组设置事件位。

参数:

  • xEventGroup:事件组句柄。
  • uxBitsToSet:要设置的事件位。

返回值:

  • None

xEventGroupWaitBits()

#include "FreeRTOS.h"
#include "event_groups.h"

EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup,
								 const Eventbits_t uxBitsToWaitFor,
								 const BaseType_t xClearOnExit,
								 const BaseType_t xWaitForAllBits,
								 TickType_t xTicksToWait );

函数说明:

  • 读取RTOS事件组中的位,可以选择进入阻塞状态(带有超时),以等待某个位或一组位被设置。
  • 用来一个任务用来检查当前事件组相应的位 是否被设置 或者 是否被清除。如果已经被设置,就继续向下运行;如果没有被设置,就(可选)阻塞当前运行的任务。
  • 不能在中断中调用。

参数:

  • xEventGroup:事件组句柄。
  • uxBitsToWaitFor:设置这个值可以检查多个Bit是否被置位。
    • 设置为0x05则意味着检查 bit 0,并且/或者 bit 2。因为 0x05 --> 0101。
    • 设置为0x07则意味着检查 bit 0 并且/或者 bit 1 并且/或者 bit 3。因为 0x07 --> 0111。
    • 这个值不能设置为 0 。
  • xClearOnExit:如果此形参被设置为pdTRUE,是否在退出时清零对应的位。
  • xWaitAllBits:要求同时满足还是某一个条件满足。设置为pdTRUE,则意味着要满足所有位是否符合;设置为pdFALSE则意味着只要要有一个 Bit 被触发。
  • xTicksToWait:阻塞时间。

返回值:

  • Any Value:等待的事件位被设置或阻塞时间到期时事件组的值。如果在调用任务离开阻塞状态和退出此函数之间,优先级更高的任务或者中断更改了事件位的值,则此函数的返回值将不同于事件组的返回值。

示例代码

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"

static EventGroupHandle_t event_group;     // 事件组句柄
static SemaphoreHandle_t recursive_mutex;  // 递归互斥锁句柄

#define EVENT_BIT_BREAKFAST_READY (1 << 0) // 早餐准备好事件信号
#define EVENT_BIT_CHILD_READY     (1 << 1) // 孩子准备好事件信号

void mom_task(void *pvPara)
{
    xSemaphoreTakeRecursive(recursive_mutex, portMAX_DELAY); // 获取递归互斥锁

    printf("[MOM TASK]: Begin.\n");
    xEventGroupSetBits(event_group, EVENT_BIT_BREAKFAST_READY); // 向事件组发送事件信号,即“早餐准备好了”
    vTaskDelay(pdMS_TO_TICKS(2000));

    printf("[MOM TASK]: Breakfast ready.\n");
    vTaskDelay(pdMS_TO_TICKS(1000));

    xSemaphoreGiveRecursive(recursive_mutex); // 释放递归互斥锁

    vTaskDelete(NULL);
}

void child_task(void *pvPara)
{
    xSemaphoreTakeRecursive(recursive_mutex, portMAX_DELAY); // 获取递归互斥锁

    printf("[CHILD TASK]: Begin.\n");
    xEventGroupSetBits(event_group, EVENT_BIT_CHILD_READY); // 向事件组发送事件信号,即“孩子准备好了”
    vTaskDelay(pdMS_TO_TICKS(2000));

    printf("[CHILD TASK]: Child ready.\n");
    vTaskDelay(pdMS_TO_TICKS(2000));

    xSemaphoreGiveRecursive(recursive_mutex); // 释放递归互斥锁

    vTaskDelete(NULL);
}

void father_task(void *pvPara)
{
    xSemaphoreTakeRecursive(recursive_mutex, portMAX_DELAY); // 获取递归互斥锁

    printf("[FATHER TASK]: Begin.\n");
    xEventGroupWaitBits(event_group, 0x03, pdTRUE, pdTRUE, portMAX_DELAY); // 向事件组发送信号,即“可以去学校了”
    vTaskDelay(pdMS_TO_TICKS(2000));

    printf("[FATHER TASK]: All ready, go to school.\n");
    vTaskDelay(pdMS_TO_TICKS(2000));


    xSemaphoreGiveRecursive(recursive_mutex); // 释放递归互斥锁

    vTaskDelete(NULL);
}

void app_main(void)
{
    event_group = xEventGroupCreate();
    recursive_mutex = xSemaphoreCreateRecursiveMutex();
    
    vTaskSuspendAll();

    xTaskCreate(mom_task, "Mon task", 2048, NULL, 2, NULL);
    xTaskCreate(child_task, "Child task", 2048, NULL, 2, NULL);
    xTaskCreate(father_task, "Father task", 2048, NULL, 1, NULL);

    xTaskResumeAll();
}

代码中同时应用了递归互斥锁来对任务非阻塞。

Comment