FreeRTOS_事件标志组

目录

1. 事件标志组简介

2. 创建事件标志组

2.1 函数 xEventGroupCreate()

2.2 函数 xEventGroupCreateStatic()

3. 设置事件位

3.1 函数 xEventGroupClearBits()

3.2 函数 xEventGroupClearBitsFromISR()

3.3 函数 xEventGroupSetBits()   

3.4 函数 xEventGroupSetBitsFromISR() 

4. 获取事件标志组值

4.1 函数 xEventGroupGetBits() 

4.2 函数 xEventGroupGetBitsFromISR() 

5. 等待指定的事件位

6. 事件标志组实验

6.1 main.c

6.2 exti.c

6.3 实验现象


        信号量可以来完成同步,但是使用信号量来同步的话任务只能与单个的事件或任务进行同步。但是有时候某个任务可能会需要与多个事件或任务进行同步,此时信号量就无能为力了。FreeRTOS 为此提供了一个解决方法,就是事件标志组。

1. 事件标志组简介

事件位(事件标志):

        事件位用来表明某个事件是否发生,事件位通常用作事件标志,比如下面这几个例子:

  •         当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置 1,当队列中没有消息需要处理的时候就可以将这个位(标志)置 0。
  •         当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置 1,当没有数据需要从网络发送出去的话就将这个位(标志)置 0.
  •         现在需要向网络中发送一个心跳信息,将某个位(标志)置 1,现在不需要向网络中发送心跳信息,这个位(标志)置 0.

事件组:

        一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:

  •         事件标志组的 bit0 表示队列中的消息是否处理掉。
  •         事件标志组的 bit1 表示是否有消息需要从网络中发送出去。
  •         事件标志组的 bit2 表示现在是否需要向网络发送心跳信息。

事件标志组和事件位的数据类型:

        事件标志组的数据类型为 EventGroupHandle_t ,当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组可以存储 8 个事件位,当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组存储 24 个事件位。

        事件标志组中的所有事件位都存储在一个无符号的 EventBits_t 类型的变量中,EventBits_t 在 event_groups.h 中有如下的定义:

typedef TickType_t     EventBits_t;

        数据类型 TickType_t 在文件 portmacro.h 中有如下定义:

#if( configUSE_16_BIT_TICKS == 1 ) typedef uint16_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffff 
#else typedef uint32_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffffffffUL #define portTICK_TYPE_IS_ATOMIC 1 
#endif通过该程序可以看出:当 configUSE_16_BIT_TICKS 等于 0 的时候,0xffffffffUL 表示TickType_t 是一个 32 位的数据
类型,因此 EventBits_t 也是一个 32 位的数据类型。EventBits_t 类型的变量可以存储 24 个事件位,另外的那高 8 位有其他用处。事件 0 存放在这个变量的 bit0 上,变量的 bit1 就是事件位 1,以此类推。

        对于 STM32 来说一个事件标志组最多可以存储 24 个事件位

2. 创建事件标志组

        FreeRTOS 提供了两个用于创建事件标志组的函数。

函数:

        xEventGroupCreate()                使用动态方法创建事件标志组。

        xEventGroupCreateStatic()       使用静态方法创建事件标志组。

2.1 函数 xEventGroupCreate()

        此函数用来创建一个事件标志组,所需要的内存通过动态内存管理方法分配。由于内部处理的原因,事件标志组可用的 bit 数取决于 configUSE_16_BIT_TICKS,当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组有 8 个可用的位(bit 0 ~ bit 7),当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组有 24 个可用的位(bit 0 ~ bit 23)。EventBits_t 类型的变量用来存储事件标志位组中的各个事件位,函数原型如下:

EventGroupHandle_t xEventGroupCreate(void)

参数:

        无

返回值:

        NULL:                事件标志组创建失败。

        其他值:              创建成功的事件标志组句柄。

2.2 函数 xEventGroupCreateStatic()

        此函数用于创建一个事件标志组,所需要的内存需要由用户自行分配,此函数原型如下:

EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer)

参数:

        pxEventGroupBuffer:                参数指向一个 StaticEnentGroup_t 类型的变量,用来保存事件标志组结构体。

返回值:

        NULL:                        事件标志组创建失败。

        其他值:                       创建成功的事件标志组句柄。

3. 设置事件位

        FreeRTOS 提供了 4 个函数用来设置事件标志组中事件位(标志),事件位(标志)的设置包括清零和置 1 两种操作

函数:

        xEventGroupClearBits()                        将指定的事件位清零,用在任务中。

        xEventGroupClearBitsFromISR()          将指定的事件位清零,用在中断服务函数中。

        xEventGroupSetBits()                            将指定的事件位置 1 ,用在任务中。

        xEventGroupSetBitsFromISR()              将指定的事件位置 1,用在中断服务函数中。

3.1 函数 xEventGroupClearBits()

        将事件标志组中的指定事件位清零,此函数只能用在任务中,不能用在中断服务函数中!中断服务函数中有其他的 API 函数。函数原型如下:

EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,const EventBits_t  uxBitsToClear);

参数:

        xEventGroup:                要操作的事件标志组的句柄。

        uxBitsToClear:               要清零的事件位,比如要清除 bit3 的话就设置为 0x08。可以同时清除多个 bit,如设置为 0x09 的话就是同时清除 bit3 和 bit0。

返回值:

        任何值:                           将指定事件位清零之前的事件组值。

3.2 函数 xEventGroupClearBitsFromISR()

        此函数为函数 xEventGroupClearBits() 的中断级版本,也是将指定的事件位(标志)清零。此函数用在中断服务函数中,此函数原型如下:

BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t  uxBitsToSet);

参数:        

        xEventGroup:                要操作的事件标志组的句柄。

        uxBitsToClear:               要清零的事件位,比如要清除 bit3 的话就设置为 0x08。可以同时清除多个 bit,如设置为 0x09 的话就是同时清除 bit3 和 bit0。

返回值:

        pdPASS:                        事件位清零成功。

        pdFALSE:                      事件位清零失败。

3.3 函数 xEventGroupSetBits()   

        设置指定的事件位为 1,此函数只能用在任务中,不能用于中断服务函数,此函数原型如下:

EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,const EventBits_t  uxBitsToSet);

参数:        

        xEventGroup:                要操作的事件标志组的句柄。

        uxBitsToClear:               指定要置 1 的事件位,比如要将 bit3 置 1 的话就设置为 0x08。可以同时将多个 bit 置 1,如设置为 0x09 的话就是同时将 bit3 和 bit0 置 1。

返回值:

        任何值:                           在将指定事件位置 1 后的事件组值。

3.4 函数 xEventGroupSetBitsFromISR() 

        此函数也用于将指定的事件位置 1 ,此函数是 xEventGroupSetBits() 的中断版本,用在中断服务函数中,函数原型如下:

EventBits_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t  uxBitsToSet,BaseType_t*        pxHigherPriorityTaskWoken);

参数:        

        xEventGroup:                要操作的事件标志组的句柄。

        uxBitsToClear:               指定要置 1 的事件位,比如要将 bit3 置 1 的话就设置为 0x08。可以同时将多个 bit 置 1,如设置为 0x09 的话就是同时将 bit3 和 bit0 置 1。

        pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值函数会自动的设置,用户不用进行设置。用户只需要提供一个变量来保存这个值就可以了。当此值

                                                        为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:

        pdPASS:                        事件位置 1 成功。        

        pdFALSE:                      事件位置 1 失败。

4. 获取事件标志组值

        可以通过 FreeRTOS 提供的 API 函数查询事件标志组值。

函数:

        xEventGroupGetBits()                        获取当前事件标志组的值(各个事件位的值),用在任务中。

        xEventGroupGetBitsFromISR()          获取当前事件标志组的值,用在中断服务函数中。

4.1 函数 xEventGroupGetBits() 

        此函数用于获取当前事件标志组的值,也就是各个事件位的值。此函数用在任务中,不能用在中断服务函数中。此函数是个宏,真正执行的是函数 xEventGroupClearBits(),函数原型如下:

EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)

参数:

        xEventGroup:                        要获取的事件标志组的句柄。

返回值:

        任何值:                                  当前事件标志组的值。

4.2 函数 xEventGroupGetBitsFromISR() 

        获取当前事件标志组的值,此函数是 xEventGroupGetBits() 的中断版本,函数原型如下:

EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup)

参数:

        xEventGroup:                要获取的事件标志组的句柄。

返回值:

        任何值:                          当前事件标志组的值。

5. 等待指定的事件位

        某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标志),使用函数 xEventGroupWaitBits() 可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置 1 或清零)的话任务就会进入阻塞态,直到阻塞时间到达或者所等待的事件位准备好。函数原型如下:

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

参数:

        xEventGroup:                        指定要等待的事件标志组。

        uxBitsToWaitFor:                   指定要等待的事件位,比如要等待 bit0 和 bit2 的时候此参数就是 0x05,如果要等待 bit0 和 bit1 和 bit2 的时候此参数就是 0x07,以此类推。

        xClearOnExit:                        此参数要是为 pdTRUE 的话,那么在退出此函数之前由参数 uxBitsToWaitFor 所设置的这些事件位就会清零。如果设置位 pdFALSE 的话这些事件

                                                         位就不会改变。

        xWaitForAllBits:                      此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1 ,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits() 才会返回。

                                                         当此函数为 pdFALSE 的话,只要 uxBitsToWaitFor 所设置的这些事件位其中的任意一个置 1,或者指定的阻塞时间到的话函数 xEventGroupWaitBits() 就

                                                         会返回。

        xTicksToWait:                         设置阻塞时间,单位为节拍数。

返回值:

        任何值。                                   返回当所等待的事件位置 1 以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置 1 了。如果函数因为阻塞时间到而返回的话

                                                         那么这个返回值就不代表任何的含义。 

6. 事件标志组实验

        本实验设计四个任务:start_task、eventsetbit_task、eventgroup_task 和 eventquery_task 这四个任务的任务功能如下:

        start_task:用来创建其他三个任务和事件标志组。

        eventsetbit_task:读取按键值,根据不同的按键值将事件标志组中相应的事件位置 1 ,用来模拟事件的发生。

        eventgroup_task:同时等待事件标志组中的多个事件位,当这些事件位都置 1 的话就执行相应的处理,例程中的处理是刷新 LCD 指定区域的背景色。

        eventquery_task:查询事件组的值,也就是各个事件位的值。获取到事件组值以后就将其显示到 LCD 上,并且也通过串口打印出来。

        实验还创建了一个事件标志组:EventGroupHandler,实验中用到了这个事件标志组的三个事件位,分别位 bit0,bit1 和 bit2。

        实验中用到了三个按键:KEY0、KEY1 和 KEY2,其中按键 KEY1 和 KEY2 为普通的输入模式。按键 KEY0 为中断输入模式,KEY0 用来演示如何在中断服务程序调用事件标志组的 API 函数。

6.1 main.c

#include "stm32f4xx.h"  
#include "FreeRTOS.h" //这里注意必须先引用FreeRTOS的头文件,然后再引用task.h
#include "task.h"     //存在一个先后的关系
#include "LED.h"
#include "LCD.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
#include "string.h"
#include "beep.h"
#include "malloc.h"
#include "timer.h"
#include "exti.h"   
#include "event_groups.h"//任务优先级
#define START_TASK_PRIO     1       //用于创建其他任务
//任务堆栈大小
#define START_STK_SIZE      256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define EVENTSETBIT_TASK_PRIO     2       //读取按键值,根据不同的按键值将事件标志组中相应的事件位置 1 ,用来模拟事件的发生。
//任务堆栈大小 
#define EVENTSETBIT_STK_SIZE      256
//任务句柄
TaskHandle_t EventSetBit_Handler;
//任务函数
void eventsetbit_task(void *pvParameters);//任务优先级
#define EVENTGROUP_TASK_PRIO     3       //同时等待事件标志组中的多个事件位,当这些事件位都置 1 的话就执行相应的处理,例程中的处理是刷新 LCD 指定区域的背景色。
//任务堆栈大小 
#define EVENTGROUP_STK_SIZE      256
//任务句柄
TaskHandle_t EventGroupTask_Handler;
//任务函数
void eventgroup_task(void *pvParameters);//任务优先级
#define EVENTQUERY_TASK_PRIO     4       //查询事件组的值,也就是各个事件位的值。获取到事件组值以后就将其显示到 LCD 上,并且也通过串口打印出来。
//任务堆栈大小 
#define EVENTQUERY_STK_SIZE      256
//任务句柄
TaskHandle_t EventQueryTask_Handler;
//任务函数
void eventquery_task(void *pvParameters);EventGroupHandle_t EventGroupHandler;   //事件标志组句柄//事件标志组是个类似于数组的形式,数组是以下角标的形式进行定义的
//EVENTBIT_0 操作数组下角标为 0 的位,1左移0位,也就是把数组第一位设置为1
//EVENTBIT_1 操作数组下角标为 1 的位,1左移1位,也就是把数组第二位设置为1
//EVENTBIT_2 操作数组下角标为 2 的位,1左移2位,也就是把数组第三位设置为1
//EVENTBIT_ALL 操作数组下角标为 0 1 2 的位,或表示把数组第一位、第二位和第三位设置为1
#define EVENTBIT_0  (1<<0)          //事件位
#define EVENTBIT_1  (1<<1)
#define EVENTBIT_2  (1<<2)
#define EVENTBIT_ALL  (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2)//LCD刷屏时使用的颜色
int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      GRED,  GBLUE, RED,   MAGENTA,       	 GREEN, CYAN,  YELLOW,BROWN, 			BRRED, GRAY };int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  delay_init(168);uart_init(115200);LED_Init();KEY_Init();EXTIX_Init();               //初始化外部中断LCD_Init();my_mem_init(SRAMIN);        //初始化内部内存池POINT_COLOR=RED;LCD_ShowString(30,10,200,16,16,"ATK STM32F407");LCD_ShowString(30,30,200,16,16,"FreeRTOS Example");LCD_ShowString(30,50,200,16,16,"Event Group");LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,90,200,16,16,"2023/11/04");POINT_COLOR = BLACK;LCD_DrawRectangle(5,130,234,314);   //画一个矩形POINT_COLOR = BLUE;LCD_ShowString(30,110,200,16,16,"Event Group Value:0");//创建开始任务xTaskCreate((TaskFunction_t)start_task,         //任务函数(const char*   )"start_task",       //任务名称(uint16_t      )START_STK_SIZE,     //任务堆栈大小(void*         )NULL,               //传递给任务函数的参数(UBaseType_t   )START_TASK_PRIO,    //任务优先级(TaskHandle_t* )&StartTask_Handler);//任务句柄vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();//创建事件标志组EventGroupHandler=xEventGroupCreate();  //创建事件标志组//创建设置事件位的任务xTaskCreate((TaskFunction_t)eventsetbit_task,         //任务函数(const char*   )"eventsetbit_task",       //任务名称(uint16_t      )EVENTSETBIT_STK_SIZE,     //任务堆栈大小(void*         )NULL,               //传递给任务函数的参数(UBaseType_t   )EVENTSETBIT_TASK_PRIO,    //任务优先级(TaskHandle_t* )&EventSetBit_Handler);//任务句柄//创建事件标志组处理任务xTaskCreate((TaskFunction_t)eventgroup_task,         //任务函数(const char*   )"eventgroup_task",       //任务名称(uint16_t      )EVENTGROUP_STK_SIZE,     //任务堆栈大小(void*         )NULL,               //传递给任务函数的参数(UBaseType_t   )EVENTGROUP_TASK_PRIO,    //任务优先级(TaskHandle_t* )&EventGroupTask_Handler);//任务句柄//创建事件标志组查询任务xTaskCreate((TaskFunction_t)eventquery_task,         //任务函数(const char*   )"eventquery_task",       //任务名称(uint16_t      )EVENTQUERY_STK_SIZE,     //任务堆栈大小(void*         )NULL,               //传递给任务函数的参数(UBaseType_t   )EVENTQUERY_TASK_PRIO,    //任务优先级(TaskHandle_t* )&EventQueryTask_Handler);//任务句柄vTaskDelete(StartTask_Handler);  //删除开始任务taskEXIT_CRITICAL();
}//设置事件位的任务
//读取按键值,根据不同的按键值将事件标志组中相应的事件位置 1 ,用来模拟事件的发生。
void eventsetbit_task(void *pvParameters)
{u8 key;while(1){if(EventGroupHandler!=NULL) //事件标志组创建成功{key = KEY_Scan(0);switch(key){case KEY1_PRES:xEventGroupSetBits(EventGroupHandler,EVENTBIT_1);   //KEY1按下,设置事件标志组的第二位为1break;case KEY2_PRES:xEventGroupSetBits(EventGroupHandler,EVENTBIT_2);   //KEY2按下,设置事件标志组的第三位为1break;}}vTaskDelay(10); //延时10ms,也就是10个时钟节拍}
}//事件标志组处理任务
//同时等待事件标志组中的多个事件位,当这些事件位都置 1 的话就执行相应的处理,例程中的处理是刷新 LCD 指定区域的背景色。
void eventgroup_task(void *pvParameters)
{u8 num;EventBits_t EventValue;while(1){if(EventGroupHandler!=NULL){//等待事件组中相应的事件位EventValue = xEventGroupWaitBits((EventGroupHandle_t    )EventGroupHandler,     //指定要等待的事件组句柄(EventBits_t           )EVENTBIT_ALL,          //指定要等待的事件位,这里设置等待第一位、第二位和第三位(BaseType_t            )pdTRUE,        //此参数要是为 pdTRUE 的话,那么在退出此函数之前由参数 uxBitsToWaitFor 所设置的这些事件位就会清零。(BaseType_t            )pdTRUE,    //此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1 ,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits() 才会返回。(TickType_t            )portMAX_DELAY);    //设置阻塞时间,单位为节拍数//返回值:返回当所等待的事件位置1以后的事件标志组的值,或者阻塞时间到。//        根据这个值我们就知道哪些事件位置 1 了,如果函数因为阻塞时间到而返回的话那么这个返回值不代表任何的含义printf("时间标志组的值:%d\r\n",EventValue);LCD_ShowxNum(174,110,EventValue,1,16,0);num++;LED1=!LED1;LCD_Fill(6,131,233,313,lcd_discolor[num%14]);}else{vTaskDelay(10);  //延时10ms,也就是10个时钟节拍}}
}//事件查询任务
//查询事件组的值,也就是各个事件位的值。获取到事件组值以后就将其显示到 LCD 上,并且也通过串口打印出来。
void eventquery_task(void *pvParameters)
{u8 num=0;EventBits_t NewValue,LastValue;while(1){if(EventGroupHandler!=NULL){NewValue = xEventGroupGetBits(EventGroupHandler);  //获取事件组的值if(NewValue!=LastValue){LastValue = NewValue;printf("事件标志组的值:%d\r\n",NewValue);LCD_ShowxNum(174,110,NewValue,1,16,0);}}num++;if(num==10) //每500msLED0闪烁一次{num=0;LED0=!LED0;}vTaskDelay(50);     //延时50ms,也就是50个时钟节拍}
}

6.2 exti.c

#include "exti.h"
#include "delay.h" 
#include "key.h"
#include "FreeRTOS.h"
#include "event_groups.h"#define EVENTBIT_0 (1<<0)  //此宏定义的意思是:1左移0位,也就是后续有定义EVENTBIT_0的话,直接就是宏定义这个值为1//外部中断初始化程序
//初始化PE4为中断输入
void EXTIX_Init(void)
{NVIC_InitTypeDef    NVIC_InitStructure;EXTI_InitTypeDef    EXTI_InitStructure;KEY_Init();RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);   //使能SYSCFG时钟SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);    //PE4连接到中断线4//配置外部中断线4EXTI_InitStructure.EXTI_Line = EXTI_Line4;  //外部中断线4EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断事件EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;    //外部中断4NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     //使能外部中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x06;    //抢占优先级6NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;   //子优先级0NVIC_Init(&NVIC_InitStructure);
}//事件标志组句柄
extern EventGroupHandle_t EventGroupHandler;//外部中断4服务程序
void EXTI4_IRQHandler(void)
{BaseType_t Result,xHigherPriorityTaskWoken;delay_xms(20);  //消抖if(KEY0==0){Result = xEventGroupSetBitsFromISR(EventGroupHandler,EVENTBIT_0,&xHigherPriorityTaskWoken); //在中断中设置事件标志组的值//要操作的事件标志组的句柄//指定要置1的事件位//标记退出此函数以后是否进行任务切换if(Result!=pdFAIL){portYIELD_FROM_ISR(xHigherPriorityTaskWoken);   //进行一次任务切换}}EXTI_ClearITPendingBit(EXTI_Line4); //清除中断线4上的中断标志位
}

6.3 实验现象

通过上述程序,我们来认真的分析一下串口收到的实验现象:

        首先,我们需要明确,串口标志组是为了一个任务和多个任务实现同步。多个任务体现在串口标志组是一个数组的形式,数组的每一位代表一个任务,同步体现在每个任务都实现,也就是串口标志组的所有位都为1时在执行后续功能,部分任务同步不会执行后续进程。按键0按下,会在外部中断线4将事件标志组的位0设置为1;按键1按下,将事件标志组的位1设置为1;按键2按下,将事件标志组的位2设置为1;

        首先初始化事件标志组的值为0,按下按键0,事件标志组值变为1,这个时候再按下按键2,事件标志组的值会变为3,原因为 0000 0011,后两位为1,表示按键0和按键1控制的这两个任务实现了同步;同理,按键2按下,事件标志组值变为7,0000 0111,表示按键0、按键1和按键2控制的这三个事件实现了同步;这就控制了多个任务实现同步。

//等待事件组中相应的事件位EventValue = xEventGroupWaitBits((EventGroupHandle_t    )EventGroupHandler,     //指定要等待的事件组句柄(EventBits_t           )EVENTBIT_ALL,          //指定要等待的事件位,这里设置等待第一位、第二位和第三位(BaseType_t            )pdTRUE,        //此参数要是为 pdTRUE 的话,那么在退出此函数之前由参数 uxBitsToWaitFor 所设置的这些事件位就会清零。(BaseType_t            )pdTRUE,    //此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1 ,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits() 才会返回。(TickType_t            )portMAX_DELAY);    //设置阻塞时间,单位为节拍数//返回值:返回当所等待的事件位置1以后的事件标志组的值,或者阻塞时间到。//        根据这个值我们就知道哪些事件位置 1 了,如果函数因为阻塞时间到而返回的话那么这个返回值不代表任何的含义

        之所以当三个事件都实现时就会将事件标志组变为0,是因为上述函数的第三个参数,此参数设置为 pdTRUE 时,在退出此函数之前由参数 uxBitsToWaitFor 所设置的这个事件位就会清零。也就是说一旦实现了第二个参数设置的事件位,那么在退出整个函数之前,就会将之前设置的所有事件位都清零。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/180427.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode:387. 字符串中的第一个唯一字符

一、题目 函数原型 int firstUniqChar(char* s) 二、算法 设置一个大小为26的字符数组&#xff0c;位置0 - 25 分别对应字符 a - z 。遍历两次字符串&#xff0c;第一次记录下每个字符出现的次数&#xff0c;第二次检查哪个字符最先遍历到且出现次数为1&#xff0c;返回该字符即…

uniapp新建的vuecli项目启动报错并且打包失败的问题(已解决)

我的项目新建流程如下 运行之后就是如下报错 解决办法&#xff1a; 安装如下依赖&#xff1a; npm i postcss-loader autoprefixer8.0.0 npm run build 编译失败 安装如下依赖&#xff1a; npm install postcss8.2.2 最终package.json文件如下 {"name": "ls…

【Vue】vant上传封装方法,van-uploader上传接口封装

项目场景&#xff1a; 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 在移动端项目中&#xff0c;使用vant组件上传&#xff0c;但是vant没有上传方法&#xff0c;需要自己写。 html代码 <van-uploader v-model"fileList" :max-size"50…

jvm实践

说一下JVM中的分代回收 堆的区域划分 1.堆被分为了两份:新生代和老年代[1:2] 2.对于新生代&#xff0c;内部又被分为了三个区域。Eden区&#xff0c;幸存者区survivor(分成from和to)[8:1:1] 对象回收分代回收策略 1.新创建的对象&#xff0c;都会先分配到eden区 2.当伊园内存…

谷歌推出基于AI的产品图像生成工具;[微软免费课程:12堂课入门生成式AI

&#x1f989; AI新闻 &#x1f680; 谷歌推出基于AI的产品图像生成工具&#xff0c;帮助商家提升广告创意能力 摘要&#xff1a;谷歌推出了一套基于AI的产品图像生成工具&#xff0c;使商家能够利用该工具免费创建新的产品图像。该工具可以帮助商家进行简单任务&#xff08;…

李宏毅机器学习笔记.Flow-based Generative Model(补)

文章目录 引子生成问题回顾&#xff1a;GeneratorMath BackgroundJacobian MatrixDeterminant 行列式Change of Variable Theorem简单实例一维实例二维实例 网络G的限制基于Flow的网络构架G的训练Coupling LayerCoupling Layer反函数计算Coupling Layer Jacobian矩阵计算Coupli…

Windows 开启 Kerberos 的火狐 Firefox 浏览器访问yarn、hdfs

背景&#xff1a;类型为IPA或者MIT KDC&#xff0c;windows目前只支持 firefoxMIT Kerberos客户端的形式&#xff0c;其他windows端浏览器IE、chrome、edge&#xff0c;没有办法去调用MIT Kerberos Windows客户端的GSSAPI验证方式&#xff0c;所以均无法使用 Windows 开启 Kerb…

关于ROS的网络通讯方式TCP/UDP

一、TCP与UDP TCP/IP协议族为传输层指明了两个协议&#xff1a;TCP和UDP&#xff0c;它们都是作为应同程序和网络操作的中介物。 **TCP&#xff08;Transmission Control Protocol&#xff09;协议全称是传输控制协议&#xff0c;是一种面向连接的、可靠的、基于字节流的传输…

VSCode实用远程主机功能

作为嵌入式开发者&#xff0c;经常在各种系统平台或者开发工具之间切换&#xff0c;比如你的代码在Linux虚拟机上&#xff0c;如果不习惯在Linux下用IDE&#xff0c;那么我尝试将Linux的目录通过samba共享出来&#xff0c;在windows下用网络映射盘的方式映射出来&#xff0c;VS…

23种设计模式(创建型、构造型、行为型)

目录 设计模式一、创建型设计模式1.1、简单工厂模式(SimpleFactory)1.2、工厂方法&#xff08;Factory Method&#xff09;1.3、 抽象工厂&#xff08;Abstarct Factory&#xff09;1.4、生成器模式&#xff08;Builder&#xff09;1.5、 原型模式&#xff08;Prototype&#x…

【LeetCode刷题-队列】--933.最近的请求次数

933.最近的请求次数 class RecentCounter {Queue<Integer> queue new LinkedList<>();public RecentCounter() {}public int ping(int t) {queue.offer(t);while(t - queue.peek() > 3000){queue.poll();}return queue.size();} }/*** Your RecentCounter obje…

CDN与WAF防火墙:强强联手,守护您的网站安全

随着互联网的普及&#xff0c;网站安全问题变得愈发重要。恶意攻击、数据泄露和服务中断等问题都可能给网站和用户带来严重损害。在保护网站免受这些威胁的过程中&#xff0c;内容分发网络&#xff08;CDN&#xff09;和Web应用程序防火墙&#xff08;WAF&#xff09;是两个强大…

【数据集处理】基于Python处理EAR5数据

基于Python处理EAR5数据 1 EAR5数据简介2 数据集处理准备工作&#xff1a;xarray库安装2.1 数据预处理-剔除异常值 参考 1 EAR5数据简介 ERA5是ECMWF&#xff08;欧洲中期天气预报中心&#xff09;对1950年1月至今全球气候的第五代大气再分析数据集。 包含了四个基本变量(日…

基于Qt命令行处理XML文件读写

Qt源码在后面,文本介绍Qt国际化语言和XML # XML基础(一) ## 1、概述 ### 1.1 定义(xml是个啥玩意儿?) XML(extensible Markup Language)俗称差妹儿,专业称之为:可拓展标记语言。 (1)何为标记,指的是一种标记语言,或者标签语言,即用一系列的标签来对数据进行…

0008Java安卓程序设计-ssm基于Android平台的健康管理系统

文章目录 **摘要**目录系统实现开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,…

CSS必学:元素之间的空白与行内块的幽灵空白问题

作者:WangMin 格言:努力做好自己喜欢的每一件事 CSDN原创文章 博客地址 &#x1f449; WangMin 我们在开发的过程中&#xff0c;难免会出现一些难以预料的问题。那么其中&#xff0c;CSS空白现象就是非常常见的问题之一。虽然它已经被发现很久&#xff0c;但仍然有许多新手和经…

AI:54-基于深度学习的树木种类识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

Spring Security OAuth 2.0 资源服务器— JWT

目录 一、JWT的最小依赖 二、JWT的最基本配置 1、指定授权服务器 2、初始预期&#xff08;Startup Expectations&#xff09; 3、运行时预期&#xff08;Runtime Expectations&#xff09; 三、JWT认证是如何工作的 四、直接指定授权服务器 JWK Set Uri 五、提供 audie…

让SOME/IP运转起来——SOME/IP系统设计(上)

什么是SOME/IP&#xff1f; SOME/IP&#xff08;Scalable service-Oriented MiddlewarE over IP&#xff09;是AUTOSAR应用层的协议&#xff0c;是基于IP协议的面向服务的可拓展性的中间件。 SOME/IP中主要定义了&#xff1a; 数据的序列化&#xff1a;SOME/IP支持的数据类型…

如何通过智能管理箱实现高效文件管理:关键字轻松修改文件名

在信息化时代&#xff0c;文件管理变得尤为重要。智能管理箱已经成为我们生活中不可或缺的一部分。它可以帮助我们高效地管理各种文件&#xff0c;使得我们的工作和生活更加便捷。是一种高效的文件管理工具&#xff0c;可以帮助我们轻松地整理和分类文件&#xff0c;提高工作效…