【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
    }
}

结尾

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


相关文章
|
6天前
|
存储 API
|
6天前
|
消息中间件 算法 调度
|
6天前
|
存储
FreeRTOS事件组
FreeRTOS事件组
19 0
|
6天前
【FreeRTOS】任务通知的使用
【FreeRTOS】任务通知的使用
|
6天前
|
API
FreeRTOS软件定时器的原理以及使用实例
FreeRTOS软件定时器的原理以及使用实例
27 0
|
6天前
|
存储
FreeRTOS入门教程(事件组概念和函数使用)
FreeRTOS入门教程(事件组概念和函数使用)
43 0
|
6天前
|
API 调度
FreeRTOS深入教程(中断管理)
FreeRTOS深入教程(中断管理)
115 0
|
5月前
|
监控 安全 API
7.1 Windows驱动开发:内核监控进程与线程回调
在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。
64 0
7.1 Windows驱动开发:内核监控进程与线程回调
|
7月前
|
API 调度
FreeRTOS学习笔记—任务创建和删除
本文学习了如何创建和删除任务。最后,分析解决了遇到的问题。
51 0