13-事件标志组.md 7.5 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第13章 事件标志组" author: "尚硅谷研究院" date: 2026-07-17

created: 2026-07-17

事件标志组

用生活理解:事件标志组就像任务清单上的多个"待办事项"打勾。你可以等"所有事项都完成"(AND逻辑)才行动,也可以等"任意一个完成"(OR逻辑)就行动。打勾后还可以选择"自动擦掉"(自动清除)或"留着标记"(手动清除)。

Concept

Basic concept

事件标志组(Event Group)是 FreeRTOS 中用于任务间事件通知和同步的轻量级机制。每个标志位代表一个特定的状态或事件。

  • 24 个可用位:高 8 位保留给内核(32 位系统中)
  • AND 模式:等待多个位全部置 1 才解除阻塞
  • OR 模式:等待多个位任意一个置 1 即解除阻塞
  • 自动清除:等待成功后自动清零标志位
  • 手动清除:等待成功后标志位保持不变,需手动 xEventGroupClearBits()

Working principle

事件标志组中的所有事件位存储在 EventBits_t 类型的单个无符号整数变量中。位 0 存储事件 0,位 1 存储事件 1,以此类推。

事件标志组 vs 信号量:

事件标志组 信号量
事件通知和同步 资源控制和同步
每个位代表不同事件 计数器表示可用资源数量
任务可等待多个位同时/任一个发生 任务等待计数值 > 0

Core API

Function Params Description
xEventGroupCreate() 动态创建事件标志组
xEventGroupSetBits() xEventGroup, uxBitsToSet 设置事件标志位
xEventGroupClearBits() xEventGroup, uxBitsToClear 清零事件标志位
xEventGroupWaitBits() xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait 等待事件标志位
xEventGroupSync() xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait 设置并等待(原子操作)
xEventGroupGetBits() xEventGroup 获取当前事件标志位值

Experiment

Requirements

学习使用 FreeRTOS 事件标志组:

  • task1:KEY1 → 置位 EVENTBIT_0;KEY2 → 置位 EVENTBIT_1
  • task2:等待 EVENTBIT_0 和 EVENTBIT_1 置位后打印,自动清除

Code Implementation

File: P15/15_事件标志组/Core/Src/freertos_demo.c

#include "event_groups.h"                   /* 事件标志组头文件 */

/* 任务配置 */
#define START_TASK_PRIORITY     1
#define START_TASK_STACK_DEPTH  128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

#define TASK1_PRIORITY          2
#define TASK1_STACK_DEPTH       128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

#define TASK2_PRIORITY          3
#define TASK2_STACK_DEPTH       128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);

EventGroupHandle_t  eventgroup_handle;      /* 事件标志组句柄 */

#define EVENTBIT_0  (1 << 0)                /* 定义事件位0 */
#define EVENTBIT_1  (1 << 1)                /* 定义事件位1 */

void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}

/* 启动任务:创建事件标志组和工作任务 */
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL();

    /* 创建事件标志组 */
    eventgroup_handle = xEventGroupCreate();
    if (eventgroup_handle != NULL) {
        printf("事件标志组创建成功\r\n");
    }

    xTaskCreate((TaskFunction_t)Task1,
                (char *)"Task1",
                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIORITY,
                (TaskHandle_t *)&task1_handler);

    xTaskCreate((TaskFunction_t)Task2,
                (char *)"Task2",
                (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)TASK2_PRIORITY,
                (TaskHandle_t *)&task2_handler);

    vTaskDelete(NULL);
    taskEXIT_CRITICAL();
}

/* task1:KEY1 -> EVENTBIT_0,KEY2 -> EVENTBIT_1 */
void Task1(void *pvParameters)
{
    uint8_t key = 0;

    while (1) {
        key = Key_Detect();
        if (key == KEY1_PRESS) {
            /* 将事件标志组的bit0位置1 */
            xEventGroupSetBits(eventgroup_handle, EVENTBIT_0);
        } else if (key == KEY2_PRESS) {
            /* 将事件标志组的bit1位置1 */
            xEventGroupSetBits(eventgroup_handle, EVENTBIT_1);
        }
        vTaskDelay(10);
    }
}

/* task2:等待bit0和bit1都置位 */
void Task2(void *pvParameters)
{
    EventBits_t event_bit = 0;

    while (1) {
        /* 等待事件标志组的bit0和bit1位都置1,成功后自动清除,永久等待 */
        event_bit = xEventGroupWaitBits(
                        eventgroup_handle,          /* 事件标志组句柄 */
                        EVENTBIT_0 | EVENTBIT_1,    /* 等待bit0和bit1 */
                        pdTRUE,                     /* 成功等待后自动清除标志位 */
                        pdTRUE,                     /* 等待所有位都置1(AND) */
                        portMAX_DELAY);             /* 永久等待 */

        printf("等待到的事件标志位值=%#x\r\n", event_bit);
    }
}

Key Config

/* FreeRTOSConfig.h — 无额外配置,事件标志组默认使能 */

Core API Reference Table

Function Parameters Return Description
EventGroupHandle_t xEventGroupCreate() NULL=失败 动态创建事件标志组
EventBits_t xEventGroupSetBits(xEventGroup, uxBitsToSet) 句柄, 待置位掩码 设置后的值 设置事件位
EventBits_t xEventGroupClearBits(xEventGroup, uxBitsToClear) 句柄, 待清除掩码 清除后的值 清除事件位
EventBits_t xEventGroupWaitBits(xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait) 句柄, 等待掩码, 退出时清除?, 等待所有?, 超时 等待结果 阻塞等待事件位
EventBits_t xEventGroupSync(xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait) 句柄, 设置位, 等待位, 超时 同步结果 设置并等待(原子)
EventBits_t xEventGroupGetBits(xEventGroup) 句柄 当前值 获取当前所有位值
BaseType_t xEventGroupSetBitsFromISR(xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken) 句柄, 置位掩码, 唤醒标记 pdPASS/pdFAIL ISR中设置位

Common Issues & Pitfalls

  1. 只有 24 个可用位:32 位系统中高 8 位被内核占用。EVENTBIT_23 是最大可用位。
  2. AND 与 OR 的区别xWaitForAllBits = pdTRUE 是 AND(等待所有),pdFALSE 是 OR(等待任一)。初学时容易混淆。
  3. 自动清除 vs 手动清除xClearOnExit = pdTRUE 表示等待成功后自动清除已匹配的位。如果多个任务等待同一位,建议手动清除以免误清。
  4. ISR 中设置位:使用 xEventGroupSetBitsFromISR() → 不要直接在 ISR 中调用 xEventGroupSetBits()
  5. 事件标志组创建后初始值为 0:所有位默认清零,首次 WaitBits 会立即返回或阻塞取决于位是否已置位。