Skip to content

事件组

7.1 事件组的本质

事件组可以实现一次唤醒多个任务。

image-20250117110914446

关于事件组:

  1. 事件组用一个整数来表示,高八位留给内核使用,其他的位每一个Bit代表一个事件。
    • 如果configUSE_16_BIT_TICKS是1,那么这个整数就是16位的,低8位用来表示事件。
    • 如果configUSE_16_BIT_TICKS是0,那么这个整数就是32位的,低24位用来表示事件。
    • configUSE_16_BIT_TICKS是用来表示Tick Count,整数位基于效率来考虑,比如configUSE_16_BIT_TICKS是1,就表示该处理器使用16位更高效。
  2. 有一个链表用来链接任务。
  3. 任务有自己需要等待的事件,可以是多个,任务中的高八位决定等待的多个事件是 “or” 还是 “and” 关系。

例如,任务A、B中等待的事件均是 “or” 关系,有一个任务C写事件:
① 若C写 Bit0 ,则A、B均被唤醒。
② 若C写 Bit1 ,则只有B被唤醒。
③ 若C写 Bit7 ,则只有A被唤醒。

7.2 事件组函数

7.2.1 创建事件组

C
EventGroupHandle_t xEventGroupCreate( void );

7.2.2 删除事件组

C
void xEventGroupDelete( EventGroupHandle_t xEventGroup );

7.2.3 设置事件

C
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
C
BaseType_t xEventGroupSetBitsFromISR( 
        EventGroupHandle_t xEventGroup, 
        const EventBits_t uxBitsToSet, 
        BaseType_t *pxHigherPriorityTaskWoken );

7.2.4 等待事件

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

7.3 事件组示例

7.3.1 示例 1

car1运行到终点后,会设置bit0事件;car2、car3都等待bit0事件。

定义事件组句柄

C
static EventGroupHandle_t g_xEventCarHandle;

创建事件组

C
void MX_FREERTOS_Init(void) {
    /* USER CODE BEGIN Init */
//    g_xSemaphoreHandle = xSemaphoreCreateMutex();
//    xSemaphoreGive(g_xSemaphoreHandle);
//    GameInit();
    g_xEventCarHandle = xEventGroupCreate();
    /* USER CODE END Init */
    ...
}

设置和等待事件

C
void OLED_PrintTask1(void *params) {
    Car *car = params;
    ShowCar(car);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
        vTaskDelay(100);
        if (car->x == 120) {
            /* 设置事件0 */
            xEventGroupSetBits(g_xEventCarHandle, (1<<0));
            vTaskDelete(NULL);
        }
    }
}

void OLED_PrintTask2(void *params) {
    Car *car = params;
    ShowCar(car);
    /* 等待事件0 */
    xEventGroupWaitBits(g_xEventCarHandle, (1<<0), pdTRUE, pdTRUE, portMAX_DELAY);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
//        vTaskDelay(100);
        HAL_Delay(100);
        if (car->x == 120) {
            vTaskDelete(NULL);
        }
    }
}

void OLED_PrintTask3(void *params) {
    Car *car = params;
    ShowCar(car);
    /* 等待事件0 */
    xEventGroupWaitBits(g_xEventCarHandle, (1<<0), pdTRUE, pdTRUE, portMAX_DELAY);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
//        vTaskDelay(100);
        HAL_Delay(100);
        if (car->x == 120) {
            vTaskDelete(NULL);
        }
    }
}

此时现象为,car1先到达终点然后,car3优先级高且无阻塞先行,到达终点后,car2运行;可以修改优先级让car3和car2同时运行。

为什么事件 0 会表达为 (1<<0),这里可以看FreeRTOS的源码示例:

C
* Example usage:
   <pre>
   #define BIT_0    ( 1 << 0 )
   #define BIT_4    ( 1 << 4 )

   void aFunction( EventGroupHandle_t xEventGroup )
   {
   EventBits_t uxBits;
   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;

        // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
        // the event group.  Clear the bits before exiting.
        uxBits = xEventGroupWaitBits(
                    xEventGroup,    // The event group being tested.
                    BIT_0 | BIT_4,  // The bits within the event group to wait for.
                    pdTRUE,         // BIT_0 and BIT_4 should be cleared before returning.
                    pdFALSE,        // Don't wait for both bits, either bit will do.
                    xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
        ...
}

7.3.2 示例 2

car1运行到终点后,会设置bit0事件;car2运行到终点后,会设置bit1事件;car3等待bit0、bit1的所有事件。

C
void OLED_PrintTask1(void *params) {
    Car *car = params;
    ShowCar(car);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
        vTaskDelay(120);
        if (car->x == 120) {
            /* 设置事件 */
            xEventGroupSetBits(g_xEventCarHandle, (1<<0));
            vTaskDelete(NULL);
        }
    }
}

void OLED_PrintTask2(void *params) {
    Car *car = params;
    ShowCar(car);
//    xEventGroupWaitBits(g_xEventCarHandle, (1<<0), pdTRUE, pdTRUE, portMAX_DELAY);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
        vTaskDelay(100);
        if (car->x == 120) {
            xEventGroupSetBits(g_xEventCarHandle, (1<<1));
            vTaskDelete(NULL);
        }
    }
}

void OLED_PrintTask3(void *params) {
    Car *car = params;
    ShowCar(car);
    xEventGroupWaitBits(g_xEventCarHandle, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);
    while (1) {
        HideCar(car);
        car->x += 1;
        if (car->x > 128) { car->x = 120; }
        ShowCar(car);
//        vTaskDelay(100);
        HAL_Delay(100);
        if (car->x == 120) {
            vTaskDelete(NULL);
        }
    }
}

xEventGroupWaitBits(g_xEventCarHandle, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY)的第四个参数为控制等待 事件0 或 事件1 还是 事件0 和 事件1

C
/**
 * @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then
 * xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor
 * are set or the specified block time expires.  If xWaitForAllBits is set to
 * pdFALSE then xEventGroupWaitBits() will return when any one of the bits set
 * in uxBitsToWaitFor is set or the specified block time expires.  The block
 * time is specified by the xTicksToWait parameter.
 */