【FreeRTOS】事件组的使用

简介: 【FreeRTOS】事件组的使用

前言

事件组与信号量类似,我们可以把事件组理解为一个整数(在FreeRTOSConfig.h文件中),用其中一位来表示一个任务的状态0-1,我们可以通过这些任务的组合来进行任务执行的判断标准;他与信号量不同的是事件组能针对1对多,多对多的任务,例如我们按键按下事件组可以通知多个任务开始执行,或者某个任务可以等待多个任务完成后在执行等;

如果configUSE_16_BIT_TICKS是1,那么这个整数就是16位的,低8位用来表示事件

如果configUSE_16_BIT_TICKS是0,那么这个整数就是32位的,低24位用来表示事件该整数

创建、删除事件

// 创建事件组
EventGroupHandle_t xEventGroupCreate( void );
// 删除事件组
void vEventGroupDelete( EventGroupHandle_t xEventGroup )

事件组的创建非常简单直接调用xEventGroupCreate就可以返回一个事件组的句柄,删除事件组也只需要调用vEventGroupDelete传入事件组句柄即可;

设置、同步事件

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
                                    const EventBits_t uxBitsToSet );

EventGroupHandle_t xEventGroup:指定完成的事件组

const EventBits_t uxBitsToSet:指定完成的事件的标志位(这里一般用移位来表示)

在中断中应该使用xEventGroupSetBitsFromISR来设置;

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

EventGroupHandle_t xEventGroup:指定完成的事件组

const EventBits_t uxBitsToWaitFor:等待的事件的标志位(这里可以使用或符号APP1|APP2表示这两个事件都能唤醒这个等待)

const BaseType_t xClearOnExit:pdTRUE表示事件执行完成后清楚事件标志位,pdFALSE则是保留

const BaseType_t xWaitForAllBits:pdTRUE判断等待的位都为1,pdFALSE判断等待的位某一个为1即可

TickType_t xTicksToWait:可设置为portMAX_DELAY:一定等到成功才返回,可以设置为期望的Tick Count,一般用pdMS_TO_TICKS()把ms转换为Tick Count

EventBits_t xEventGroupSync(    EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet,
                                const EventBits_t uxBitsToWaitFor,
                                TickType_t xTicksToWait );

EventGroupHandle_t xEventGroup:指定完成的事件组

const EventBits_t uxBitsToSet:设置自己的事件标志位

const EventBits_t uxBitsToWaitFor:等待的事件的标志位(这里可以使用或符号APP1|APP2表示这两个事件都能唤醒这个等待)

TickType_t xTicksToWait:可设置为portMAX_DELAY:一定等到成功才返回,可以设置为期望的Tick Count,一般用pdMS_TO_TICKS()把ms转换为Tick Count

示例

等待多个事件用法

我们创建三个线程app1为优先级1,app2为优先级2,app3为优先级3;在这里如果我们给每个任务延时切换时间的话理论上是app3->app2->app1的执行顺序,但是当我们的任务三在特定情况下需要等任务1和任务2的某些处理做完了才能执行的话这里就可以用到事件组,这里引用韦东山老师的例子,本示例相当于做饭的一个做成,app1相当于洗菜,app2相当于生火,app3相当于炒菜;当我们菜洗好了锅里才能生火,火生好了后才能继续炒菜,所以最终的执行顺序就是app1->app2->app3;

event_group = xEventGroupCreate( );
    
  /* 创建app_task任务 */
  xTaskCreate((TaskFunction_t )app1_task,"app1",512,NULL,1,NULL);
  xTaskCreate((TaskFunction_t )app2_task,"app2",512,NULL,2,NULL);
  xTaskCreate((TaskFunction_t )app3_task,"app3",512,NULL,3,NULL);
EventGroupHandle_t event_group;
/*
* bit0: app1执行完成
* bit1: app2执行完成
* bit2: app3执行完成
*/
#define APP1  (1<<0)
#define APP2  (1<<1)
#define APP3  (1<<2)
static void app1_task(void *par)
{
    printf("优先级最低:app1\r\n");
    while(1)
    {
        printf("app1_task ok\r\n");
        xEventGroupSetBits(event_group, APP1);                                  /* APP1 执行完成 */
        xEventGroupWaitBits( event_group,APP3,pdTRUE,pdTRUE,portMAX_DELAY);     /* 等待 APP3 执行完成 */
    }
}
static void app2_task(void *par)
{
    printf("优先级中等:app2\r\n");
    while(1)
    {
        xEventGroupWaitBits( event_group,APP1,pdFALSE,pdTRUE,portMAX_DELAY);    /* 等待 APP1 执行完成 */
        printf("app2_task ok\r\n");
        xEventGroupSetBits(event_group, APP2);                                  /* APP2 执行完成 */
    }
}
static void app3_task(void *par)
{
    printf("优先级最高:app3\r\n");
    while(1)
    {
        xEventGroupWaitBits( event_group,APP1|APP2,pdTRUE,pdTRUE,portMAX_DELAY);/* 等待APP1和APP2 执行完成 */
        printf("app3_task ok\r\n");
        xEventGroupSetBits(event_group, APP3);                                  /* APP3 执行完成 */
    }
}

我们可以看到在没获取事件的前提下我们的printf(“优先级最高:app3\r\n”);打印还是按照线程优先级执行的,当进入事件组xEventGroupWaitBits等待的时候就线程就进入阻塞状态切换到其他线程执行,当任务一发出完成事件后开始唤醒两个任务,任务二和任务三进入事件判断,任务二只需要任务一执行完成就可以执行所以满足条件,然而任务三需要任务一和任务二同时满足才能执行所以这里不满足条件不执行,等任务二发出执行完成信号后两个任务开始进行事件判断,优先级高的任务三开始判断任务一和任务二的事件是否执行完成,满足条件开始执行然后发出任务三完成信号然后继续响应判断,如此往复;

同步用法

当我们遇到三个线程需要工程完成后才能执行的情况下使用该种方法,例如任务一是负责煮饭的人、任务二是负责炒菜的人、任务三是负责洗碗的人,如果要吃饭就必须三个人都完成了才满足吃饭条件才能让三个任务的人吃饭,示例代码如下;

/*
* bit0: app1执行完成
* bit1: app2执行完成
* bit2: app3执行完成
*/
#define APP1  (1<<0)
#define APP2  (1<<1)
#define APP3  (1<<2)
static void app1_task(void *par)
{
    printf("优先级最低:app1\r\n");
    while(1)
    {
        #if FLAG
        printf("app1_task ok\r\n");
        xEventGroupSetBits(event_group, APP1);                                  /* APP1 执行完成 */
        xEventGroupWaitBits( event_group,APP3,pdTRUE,pdTRUE,portMAX_DELAY);     /* 等待 APP3 执行完成 */
        #else
        printf("我煮好饭了!\r\n");
        xEventGroupSync(event_group, APP1, APP1 | APP2 |APP3, portMAX_DELAY);
        printf("煮饭人开始吃饭\r\n");
        vTaskDelay(100); /* 等他们吃 */ 
        #endif
    }
}
static void app2_task(void *par)
{
    printf("优先级中等:app2\r\n");
    while(1)
    {
        #if FLAG
        xEventGroupWaitBits( event_group,APP1,pdFALSE,pdTRUE,portMAX_DELAY);    /* 等待 APP1 执行完成 */
        printf("app2_task ok\r\n");
        xEventGroupSetBits(event_group, APP2);                                  /* APP2 执行完成 */
        #else
        printf("我炒好菜了!\r\n");
        xEventGroupSync(event_group, APP2, APP1 | APP2 |APP3, portMAX_DELAY);
        printf("炒菜人开始吃饭\r\n");
        vTaskDelay(100); /* 等他们吃 */ 
        #endif
    }
}
static void app3_task(void *par)
{
    printf("优先级最高:app3\r\n");
    while(1)
    {
        #if FLAG
        xEventGroupWaitBits( event_group,APP1|APP2,pdTRUE,pdTRUE,portMAX_DELAY);/* 等待APP1和APP2 执行完成 */
        printf("app3_task ok\r\n");
        xEventGroupSetBits(event_group, APP3);                                  /* APP3 执行完成 */
        #else
        printf("我洗好碗筷了!\r\n");
        xEventGroupSync(event_group, APP3, APP1 | APP2 |APP3, portMAX_DELAY);
        printf("洗碗人开始吃饭\r\n");
        vTaskDelay(100); /* 等他们吃 */ 
        #endif
    }
}

结尾

我是凉开水白菜,我们下文见~


相关文章
|
芯片
最详细STM32,cubeMX 按键点亮 led
最详细STM32,cubeMX 按键点亮 led
973 1
|
数据采集 监控 数据管理
《阿里云数据治理方案及案例分享》|学习笔记
快速学习《阿里云数据治理方案及案例分享》
2933 0
|
6月前
|
自然语言处理 安全
Stop Fixating on Prompts: Reasoning Hijacking and Constraint Tightening for Red-Teaming LLM Agents(论文解读)
本文提出的JailAgent框架,通过不修改用户提示词的隐式攻击方式,实现了对LLM智能体推理轨迹与记忆检索的高效劫持,兼具高攻击成功率、强泛化性、高隐蔽性与低计算开销,为LLM智能体的红队测试与安全评估提供了全新范式。
332 2
|
7月前
|
人工智能 机器人 API
10分钟搭建专属AI助手:OpenClaw接入Telegram完整教程(含阿里云轻量服务器部署+常见问题)
OpenClaw(Clawdbot)作为开源AI智能体框架,能通过自然语言指令完成自动化任务,而Telegram是全球流行的即时通讯工具,两者结合可打造跨平台的专属AI助手。本文基于2026年最新稳定版,从阿里云轻量服务器购买到Telegram机器人接入,再到新手避坑指南,全程图文并茂、代码可直接复制,助力零基础用户快速搭建AI助手,实现24小时在线响应、远程控制等功能。
1232 8
|
8月前
|
机器学习/深度学习 存储 弹性计算
刚刚:阿里云服务器2026最新收费价格曝光:年/月/小时价格一目了然
2026年阿里云服务器最新价格表:年付低至38元/年(轻量应用服务器),月付25元起,按量小时计费0.3375元起;覆盖中国大陆及海外多地域,含ECS、GPU(EGS)、轻量服务器全品类,支持带宽与系统盘灵活选配,并享代金券等多重优惠。
2031 9
|
存储 Java 对象存储
轻量级锁
轻量级锁是JVM为提升多线程性能而引入的锁机制,通过CAS操作减少线程阻塞,适用于同步块执行时间短且线程竞争不激烈的场景。其核心在于使用栈帧中的锁记录与CAS操作实现高效加锁,避免用户态与内核态切换带来的性能损耗。当无竞争时,仅需一次CAS即可完成锁获取;若竞争激烈,则可能升级为重量级锁。相比偏向锁和重量级锁,轻量级锁在低竞争环境下具有更高的效率。
439 0
|
机器学习/深度学习 人工智能 自然语言处理
三行代码实现实时语音转文本,支持自动断句和语音唤醒,用 RealtimeSTT 轻松创建高效语音 AI 助手
RealtimeSTT 是一款开源的实时语音转文本库,支持低延迟应用,具备语音活动检测、唤醒词激活等功能,适用于语音助手、实时字幕等场景。
3560 18
三行代码实现实时语音转文本,支持自动断句和语音唤醒,用 RealtimeSTT 轻松创建高效语音 AI 助手
|
网络安全 开发工具 数据安全/隐私保护
自建内网穿透服务器
本文介绍了如何使用FRP实现内网穿透。首先准备一台具有公网IP的云服务器和一台内网服务器,接着在云服务器上安装Docker和FRP服务端,配置`frps.ini`文件并启动服务。在内网服务器上手动安装FRP客户端,配置`frpc.ini`文件并启动服务。最后通过FRP控制台验证连接状态,确保可以通过公网IP访问内网服务。
6339 10
自建内网穿透服务器
|
监控 安全 数据安全/隐私保护
一个典型的DRM系统的工作流程:
【10月更文挑战第30天】个典型的DRM系统的工作流程:
1279 3
|
机器学习/深度学习 人工智能 算法
【AI系统】关键设计指标
本文介绍了AI芯片设计中的关键指标与设计点,涵盖OPS、MACs、FLOPs等计算单位,以及精度、吞吐量、时延、能耗、成本和易用性等六大关键指标。文章还探讨了MACs和PE优化策略,以及通过算术强度和Roofline模型评估AI模型在特定芯片上的性能表现,为AI芯片的性能优化提供了理论依据和实践指导。
1949 1