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.mzph.cn/news/130285.shtml

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

相关文章

项目实战:分页功能实战

1、在index.html添加点击事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"scr…

点大商城V2版 2.5.2.1 全开源独立版 多小程序端+unipp安装教程

点大商城V2是一款采用全新界面设计支持多端覆盖的小程序应用&#xff0c;支持H5、微信公众号、微信小程序、头条小程序、支付宝小程序、百度小程序&#xff0c;本程序是点大商城V2独立版&#xff0c;包含全部插件&#xff0c;代码全开源&#xff0c;并且有VUE全端代码。分销&am…

万岳讲堂:抖音小程序开发入门指南

抖音小程序可以将开发者的创意带入这个热门的应用中。本文将带您深入了解抖音小程序的开发入门指南&#xff0c;帮助您开始在这一平台上构建自己的应用。 一、什么是抖音小程序&#xff1f; 抖音小程序是一种轻量级的应用程序&#xff0c;它可以在抖音中直接运行&#xff0c;无…

Java8 Stream API全面解析——高效流式编程的秘诀

文章目录 什么是 Stream Api?快速入门流的操作创建流中间操作filter 过滤map 数据转换flatMap 合并流distinct 去重sorted 排序limit 限流skip 跳过peek 操作 终结操作forEach 遍历forEachOrdered 有序遍历count 统计数量min 最小值max 最大值reduce 聚合collect 收集anyMatch…

python 成绩统计,输出及格率和优

题目描述&#xff1a; 小蓝给学生们组织了一场考试&#xff0c;卷面总分为100分&#xff0c;每个学生的得分都是一个0到100的整数。 如果得分至少是60分&#xff0c;则称为及格。如果得分至少为85分&#xff0c;则称为优秀。 请计算及格率和优秀率&#xff0c;用百分数表示&am…

虚拟机保护工具:Zerto Virtual Replication 10.0 U1 Crack

Zerto虚拟复制是为需要保护虚拟机和应用程序的企业设计的产品。通过通过连接到广域网或云到远程站点的复制来保护虚拟机。Zerto VR 2.0还可以与vCloud Director一起将虚拟机或虚拟机组复制到云端&#xff08;或从云端&#xff09;。 事实上&#xff0c;Zerto与33家云提供商合作…

Unity在Project右键点击物体之后获取到点击物体的名称

Unity在Project右键点击物体之后获取到点击物体的名称 描述&#xff1a; 在Unity的Project右键点击物体之后选择对应的菜单选项点击之后打印出物体的名称 注意事项 如果获取到文件或者预制体需要传递objcet类型&#xff0c;然后使用 GameObject.Instantiate((GameObject)se…

WiFi模块在智能家居中的应用与优化

智能家居技术的迅速发展已经改变了我们对家庭的定义。WiFi模块作为智能设备连接的核心&#xff0c;扮演着连接和控制智能家居生态系统的关键角色。本文将深入研究WiFi模块在智能家居中的应用&#xff0c;同时探讨如何通过优化来提升其性能和用户体验。 1. 智能家居中WiFi模块的…

第二十六章 BEV感知系列三(车道线感知)

前言 近期参与到了手写AI的车道线检测的学习中去&#xff0c;以此系列笔记记录学习与思考的全过程。车道线检测系列会持续更新&#xff0c;力求完整精炼&#xff0c;引人启示。所需前期知识&#xff0c;可以结合手写AI进行系统的学习。 BEV感知系列是对论文Delving into the De…

Jenkins项目部署

使用jenkins部署项目 简易版使用jenkins部署项目 将war包部署到tomcat中 将已有的war包部署到tomcat中(jenkins与tomcat在同一台主机) 点击Jenkins主页的新建任务 输入任务名称 选择构建一个自由风格的软件项目后点击确定 在构建内添加构建步骤&#xff0c;选择执行shell 输入…

回归预测 | Matlab实现MPA-BP海洋捕食者算法优化BP神经网络多变量回归预测(多指标、多图)

回归预测 | Matlab实现MPA-BP海洋捕食者算法优化BP神经网络多变量回归预测&#xff08;多指标、多图&#xff09; 目录 回归预测 | Matlab实现MPA-BP海洋捕食者算法优化BP神经网络多变量回归预测&#xff08;多指标、多图&#xff09;效果一览基本介绍程序设计参考资料 效果一览…

数字IC后端实现 |TSMC 12nm 与TSMC 28nm Metal Stack的区别

下图为咱们社区IC后端训练营项目用到的Metal Stack。 芯片Tapeout Review CheckList 数字IC后端零基础入门Innovus学习教程 1P代表一层poly&#xff0c;10M代表有10层metal&#xff0c;M5x表示M2-M6为一倍最小线宽宽度的金属层&#xff0c;2y表示M7-M8为二倍最小线宽宽度的金…

npm的使用

package.json 快速生成package.json npm init -y “version”: “~1.1.0” 格式为&#xff1a;「主版本号. 次版本号. 修订号」。 修改主版本号是做了大的功能性的改动 修改次版本号是新增了新功能 修改修订号就是修复了一些bug dependencies "dependencies": {&…

redis rdb aof

appendonly yes # appendfsync always appendfsync everysec # appendfsync no E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit appendonly.aof

Jetson NX FFmpeg硬件编解码实现

最近在用Jetson Xavier NX板子做视频处理&#xff0c;但是CPU进行视频编解码&#xff0c;效率比较地下。 于是便考虑用硬解码来对视频进行处理。 通过jtop查看&#xff0c;发现板子是支持 NVENC硬件编解码的。 1、下载源码 因为需要对ffmpeg进行打补丁修改&#xff0c;因此需…

Springboot JSP项目如何以war、jar方式运行

文章目录 一&#xff0c;序二&#xff0c;样例代码1&#xff0c;代码结构2&#xff0c;完整代码备份 三&#xff0c;准备工作1. pom.xml 引入组件2. application.yml 指定jsp配置 四&#xff0c;war方式运行1. 修改pom.xml文件2. mvn执行打包 五&#xff0c;jar方式运行1. 修改…

PPT制作指南

诸神缄默不语-个人CSDN博文目录 文章目录 1. SOP2. PPT的目标3. PPT素材4. 内容框架5. 设计细节本文撰写过程中使用到的参考资料 1. SOP 分析目标→收集素材→明确框架→视觉呈现 2. PPT的目标 演讲型PPT&#xff1a;字少图多 阅读型PPT&#xff1a;需要文字解释 分析维度&…

【深度学习基础】Pytorch框架CV开发(2)实战篇

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

springboot常见网络相关错误及原因解析

在基于spring-boot开发过程尤其是上线后&#xff0c;经常出现网络相关的错误&#xff0c;令人难以琢磨和下手&#xff0c;所以就spring-boot使用过程中可能碰到的网络相关问题进行分析&#xff0c;结合网络转包、日志报错和前端输出&#xff0c;针对网络连接超时、连接被拒绝、…

默认路由配置

默认路由&#xff1a; 在末节路由器上使用。&#xff08;末节路由器是前往其他网络只有一条路可以走的路由器&#xff09; 默认路由被称为最后的关卡&#xff0c;也就是静态路由不可用并且动态路由也不可用&#xff0c;最后就会选择默认路由。有时在末节路由器上写静态路由时…