freertos学习笔记10--个人自用-第14章 事件组(event group)

事件组 (Event Group) 是 FreeRTOS 中一种非常灵活且强大的同步机制。

事件组就是 FreeRTOS 中的“多功能状态指示灯”或“全局任务清单”。

一:核心概念:它是什么?

在 FreeRTOS 中,信号量(Semaphore)和队列(Queue)通常是一对一或者一对多的通信,而且往往是“消耗性”的(读走了就没有了)。

事件组本质上是一个整数(Integer),其中的每一位(Bit)代表一个特定的事件。

  • 0:代表事件未发生。

  • 1:代表事件发生了。

它的特殊能力:

  1. 多对多同步: 多个任务可以同时等待同一个事件组。

  2. 逻辑判断: 任务可以等待“某个特定事件发生”(OR 逻辑),也可以等待“所有指定事件都发生”(AND 逻辑)才被唤醒。

1. 生活类比:火箭发射倒计时

2. 核心原理:就是一串二进制位

3. 为什么需要事件组?(对比信号量)

二:事件组函数

1. 创建事件组:xEventGroupCreate

/* 创建一个事件组,返回它的句柄。
 * 此函数内部会分配事件组结构体 
 * 返回值: 返回句柄,非NULL表示成功
 */
EventGroupHandle_t xEventGroupCreate( void );

/* 创建一个事件组,返回它的句柄。
 * 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的指针
 * 返回值: 返回句柄,非NULL表示成功
 */
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );

2. 发送信号(设置位):xEventGroupSetBits

/* 设置事件组中的位
 * xEventGroup: 哪个事件组
 * uxBitsToSet: 设置哪些位? 
 *              如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
 *              可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
 * 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)
 */
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
                                    const EventBits_t uxBitsToSet );

/* 设置事件组中的位
 * xEventGroup: 哪个事件组
 * uxBitsToSet: 设置哪些位? 
 *              如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
 *              可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
 * pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有, pdFALSE-没有
 * 返回值: pdPASS-成功, pdFALSE-失败
 */
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
									  const EventBits_t uxBitsToSet,
									  BaseType_t * pxHigherPriorityTaskWoken );

3. 等待信号(等待位):xEventGroupWaitBits (最重要!)

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

4. 实战:怎么判断 xEventGroupWaitBits 的返回值?

5.示范代码

/* 定义事件位 */
#define EVENT_FUEL    (1 << 0)  // 0001
#define EVENT_ENGINE  (1 << 1)  // 0010

/* 创建句柄 */
EventGroupHandle_t xLaunchEventGroup;

/* 任务1:负责加燃料 */
void vTaskFuel(void *pvParameters) {
    // ... 模拟加燃料的操作 ...
    printf("燃料加注完毕!\n");
    
    // 设置 Bit 0 为 1
    xEventGroupSetBits(xLaunchEventGroup, EVENT_FUEL);
    vTaskDelete(NULL); // 任务完成自杀
}

/* 任务2:负责检查引擎 */
void vTaskEngine(void *pvParameters) {
    // ... 模拟检查引擎 ...
    printf("引擎检查正常!\n");
    
    // 设置 Bit 1 为 1
    xEventGroupSetBits(xLaunchEventGroup, EVENT_ENGINE);
    vTaskDelete(NULL);
}

/* 任务3:发射主控 (等待者) */
void vTaskLaunch(void *pvParameters) {
    const EventBits_t xBitsToWaitFor = (EVENT_FUEL | EVENT_ENGINE);
    EventBits_t xEventGroupValue;

    // 等待 Bit 0 和 Bit 1 同时变高
    xEventGroupValue = xEventGroupWaitBits(
            xLaunchEventGroup,
            xBitsToWaitFor,
            pdTRUE,        // 退出时清除这些位(重置状态)
            pdTRUE,        // pdTRUE 表示逻辑 AND,必须两个都准备好
            portMAX_DELAY  // 一直等,不见不散
        );

    if ((xEventGroupValue & (EVENT_FUEL | EVENT_ENGINE)) == (EVENT_FUEL | EVENT_ENGINE)) {
        printf("所有条件满足,火箭发射!!!\n");
    }
}

6.如何理解

((xEventGroupValue & (EVENT_FUEL | EVENT_ENGINE)) == (EVENT_FUEL | EVENT_ENGINE))


三: 同步点(Synchronization Point)

1.什么是“同步点” (Synchronization Point)?

在 FreeRTOS 中,同步点通常被称为 “汇合点” (Rendezvous)

1. 生活类比:旅游团集合

想象一个旅游团有 3 个游客(任务 A、B、C),导游规定:“大家自由活动,下午 5 点在西门集合,人齐了才能一起上车去吃饭。”

  • 游客 A 4:50 到了 -> 必须等

  • 游客 B 4:55 到了 -> 必须等

  • 游客 C 5:00 到了 -> 只有当 C 到达的那一瞬间,3 个人同时解除等待,一起上车

这就是同步点:多任务互相等待,直到所有参与者都到达指定位置,然后同时恢复运行。

2. 核心函数:xEventGroupSync()

这个函数做的事情非常精妙,它在一个函数里完成了两步操作:

  1. 举手:表明“我到了”(设置自己的位)。

  2. 等待:盯着看“大家都到了没”(等待所有的位)。

EventBits_t xEventGroupSync( 
                 EventGroupHandle_t xEventGroup, 
                 const EventBits_t uxBitsToSet,       // 我是谁?(我到了要设哪一位)
                 const EventBits_t uxBitsToWaitFor,   // 我在等谁?(所有人齐了是哪几位)
                 TickType_t xTicksToWait );           // 等多久

3、 代码实战:三任务同步

假设我们需要采集 温度湿度气压。必须三个数据都采集好了,才能打包发送给服务器。

定义三个位:

  • TASK_A_BIT (1<<0)

  • TASK_B_BIT (1<<1)

  • TASK_C_BIT (1<<2)

  • 全家福ALL_SYNC_BITS (TASK_A_BIT | TASK_B_BIT | TASK_C_BIT)

任务 A 的代码(任务 B 和 C 逻辑一模一样,只是改一下自己的位)

void vTaskA( void *pvParameters ) {
    for( ;; ) {
        // 1. 干活:采集温度
        CollectTemperature();
        
        // 2. 到了同步点!
        // 意思:我是 A,我到了!我要等 A、B、C 都到齐了才能走!
        xEventGroupSync( 
            xEventGroup, 
            TASK_A_BIT,      // 设置我自己的位 (Set)
            ALL_SYNC_BITS,   // 等待所有人的位 (Wait)
            portMAX_DELAY    // 死等,不见不散
        );

        // 3. 当代码运行到这里时,说明 B 和 C 一定也都干完活了!
        // 可以开始下一步协同工作了
        printf("任务 A: 大家都齐了,我继续运行...\n");
    }
}

逻辑流程图解

  1. Task A 先跑完,调用 Sync

    • 事件组变成 001

    • 等待 111。条件不满足 -> Task A 阻塞(睡觉)

  2. Task B 接着跑完,调用 Sync

    • 事件组变成 011

    • 等待 111。条件不满足 -> Task B 阻塞(睡觉)

  3. Task C 最后跑完,调用 Sync

    • 事件组变成 111

    • 条件满足!

    • 瞬间发生的事: Task A、Task B、Task C 全部被唤醒。事件组自动清零(这很重要!)。三个任务几乎同时进入下一行代码。

4.总结

5.提问

第一问:ALL_SYNC_BITS 是什么意思?

第二问:WaitBits (AND模式) 和 Sync 不都是“等人齐”吗?有什么区别?

九文章定位末尾

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值