【ESP-IDF FreeRTOS】事件组

事件组,叫做组,但其实它就是一个数,我们使用的是这个数的位。

我们简单回顾一下信号量,我们执行take和give的时候,是不是一次性只能take一个或是give一个。如果有个资源比较复杂,我需要使用三个信号量来控制,那么就意味着我需要三个信号量同时可以take,可是我们信号量take的话要么失败要么成功,我们没法控制到等三个信号量都能take的时候我们再take。

这时候就需要用到事件组了。事件组是一个数,一个数有很多位,我们可以在位上面做文章。每个位我们都当它们是一个事件,事件发生了,我们就把位置1,否则置0,我们可以等待事件组的某位或者是某几位都置1以此来控制任务间的同步。

那么事件组这个数有多少位呢?

我们可以查看这个宏的值configUSE_16_BIT_TICKS如果是1,那么事件组是16bit,如果是0,那么事件组是32bit。

但无论是16bit还是32bit,我们都要扣掉高八位8bit,因为那是给内核用的。

比如说我这边这个宏是0,那么我的事件组就是32bit,扣掉高八位,能用的是24bit。

听起来事件组好像也就是一个数而已,我们拿一个全局变量按照上面的说法操作是不是也能代替事件组?问题还是在于我们的FreeRTOS是多任务的,很有可能操作到一半的时候就切换任务导致出错,使用事件组可以保证我们的操作属于原子操作。

接下来我们看看ESP-IDF FreeRTOS如何使用事件组。

首先包含头文件。

#include "freertos/event_groups.h"

然后创建一个事件组。

EventGroupHandle_t xEventGroupCreate(void)

创建完之后我们的操作基本就俩,设置事件组的位,等待获取事件组的位。

下面这个函数是设置事件组的位。

EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)

参数俩,一个是事件组句柄,另一个是我们要给事件组置1的位,比如说我要给位一置1,那么传的参数是0x01(0000 0001b),位二就是0x02(0000 0010b),并且可以同时给多个位置1,可以直接传0xFF(1111 1111),把低八位全部置1。

接下来是等待获取事件组的位。

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

第一个参数传入句柄。

第二个参数就是我们要等待的位了,和可以设置多个位一样,我们也可以等待多个位。

第三个参数指定获取之后是否将那些位置0回去,传入pdTRUE或是pdFALSE。

第四个参数指定是否等待全部的位,如果是pdTRUE,那么函数会阻塞到参数二的全部位置1,如果是pdFALSE,那么参数二中只要有一个位被置一了,函数就会解除阻塞。

第五个参数就是传入时钟周期数,也就是函数阻塞的最长时间。

那么我们应该如何判断事件组是获取到了指定位而解除了函数阻塞还是因为超时而解除阻塞的呢?

编程指南里是说这个函数会返回事件组的值,我们通过查看返回值来判断是什么情况,因为如果我们设置了获取到就将位置0,那么因超时而解除阻塞是不会改变原来的值的。

编程指南的解释有点绕(反正一开始我误解了,也可能是翻译的问题),我以为返回值是解除阻塞之后的事件组的值,因此只需要判断返回值中我们等待的位是否被置0即可(这是错误的!!!)。

后来多实验了几次才悟了,返回值返回的是解除阻塞时的事件组的值。如果像我一开始那样理解,那么会出问题的,如果我一直一个位也没置,那么函数就会因为超时而解除阻塞,这时候判断事件组的值,就会误判为等待的位被置0(其实一开始就没有置1一直都是0)。

所以正确的做法是解除阻塞之后再获取一次事件组的值,和这个函数返回值对比一下,相比之下等待的位被置为0了,那才是等待成功。

我们使用下面这个函数来获取当前事件组的值。

xEventGroupGetBits(xEventGroup)

void vEventGroupDelete(EventGroupHandle_t xEventGroup)

删除事件组。

EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)

清除指定位,也就是主动置0。用法和置1一样。

有上面几个函数基本就够用了,接下来来个小例子。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

EventGroupHandle_t egHandle;

void test(void* arg){
    uint32_t data,waittime;
    if(*(int*)arg == 1){
        data = 0x01;
        waittime = 2500;
    }else if(*(int*)arg == 2){
        data = 0x02;
        waittime = 3500;
    }else{
        data = 0x04;
        waittime = 5000;
    }
    while(1){
        uint32_t val = xEventGroupGetBits(egHandle);
        printf("test%d before val is %lx\r\n",*(int*)arg,val);
        xEventGroupSetBits(egHandle,data);
        val = xEventGroupGetBits(egHandle);
        printf("test%d after val is %lx\r\n",*(int*)arg,val);
        vTaskDelay(waittime / portTICK_PERIOD_MS);
    }
}

void app_main(void) {
    egHandle = xEventGroupCreate();
    int t1 = 1, t2 = 2, t3 = 3;
    xTaskCreate(test,"test1",1024*2,(void*)&t1,configMAX_PRIORITIES/2,NULL);
    xTaskCreate(test,"test2",1024*2,(void*)&t2,configMAX_PRIORITIES/2,NULL);
    xTaskCreate(test,"test3",1024*2,(void*)&t3,configMAX_PRIORITIES/2,NULL);
    while (1) {
        xEventGroupWaitBits(egHandle,0x07,pdTRUE,pdTRUE,pdMS_TO_TICKS(10000));
        if(((val & 0x07) == 0x07) && (xEventGroupGetBits(egHandle) & 0x07) == 0x00){
            printf("get event group\r\n");
        }
    }
}

创建一个事件组,在主循环里等待获取事件组的低3位。创建了三个任务分别以不同的时间间隔去将三个不同的位设置为1,可以看到等待获取事件组的函数是在间隔时间最久的那个任务执行过后接触阻塞的。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值