FreeRTOS_事件标志组

news2024/11/22 19:00:11

目录

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按下,设置事件标志组的第二位为1
                    break;
                case KEY2_PRES:
                    xEventGroupSetBits(EventGroupHandler,EVENTBIT_2);   //KEY2按下,设置事件标志组的第三位为1
                    break;
            }
        }
        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
    
    //配置外部中断线4
    EXTI_InitStructure.EXTI_Line = EXTI_Line4;  //外部中断线4
    EXTI_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;    //外部中断4
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     //使能外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x06;    //抢占优先级6
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;   //子优先级0
    NVIC_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.coloradmin.cn/o/1171726.html

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

相关文章

Linux下input子系统

文章目录 input子系统简单介绍相关的函数input_dev注册过程上报输入事件按键的input子系统实验 input子系统简单介绍 input子系统是管理输入的子系统&#xff0c;和pinctrl和gpio子系统一样&#xff0c;都是Linux内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触…

任务1 部署ChatGLM3-6B大模型并进行对话测试

部署ChatGLM3-6B大模型并进行对话测试 0 介绍&#xff1a;1 趋动云项目创建与环境配置1.1 创建项目&#xff1a;1.2 配置环境1.2.1 进入终端1.2.2 设置镜像源1.2.3 克隆项目,并安装依赖 2 修改代码&#xff0c;改路径以及启动代码3 运行代码3.1 运行gradio界面&#xff1a;3.2 …

JAVA毕业设计109—基于Java+Springboot+Vue的宿舍管理系统(源码+数据库)

基于JavaSpringbootVue的宿舍管理系统(源码数据库)109 一、系统介绍 本系统前后端分离 本系统分为学生、宿管、超级管理员三种角色 1、用户&#xff1a; 登录、我的宿舍、申请调宿、报修申请、水电费管理、卫生检查、个人信息修改。 2、宿管&#xff1a; 登录、用户管理…

python实现从字符串中识别出省市区信息

从字符串中识别出省市区的信息分别存储,是我们经常会碰到的问题。如果用分词的方法去匹配获取比较麻烦,cpca包提供了便捷的调用函数transform。只要把含省市区的信息放进去,即可返回标准的含省市区的数据框。    本文详细阐述如何安装cpca包、transform函数参数定义,以及…

迷宫问题的对比实验研究

对不同的迷宫进行算法问题&#xff0c;分别采用栈、队列、基于红黑树的A*算法、以及图论中的最短路径来解决迷宫问题。 基本要求&#xff1a; &#xff08;1&#xff09; 从文件读入9*9的迷宫&#xff0c;设置入口和出口&#xff0c;分别采用以上方法&#xff0c;输出从入口到出…

CMake引用OSG

从CMake执行find_package(OpenSceneGraph REQUIRED COMPONENTS osgDB osgUtil)这句;情况如下; 当前OSG已经安装好;环境变量添加了OSG_ROOT(其值是OSG安装的根目录),并且 %OSG_ROOT%\bin 添加到了path; 有一个警告,已经done了; Found osgDB: optimized;D:/OSGEarth/l…

网络基础扫盲-多路转发

博客内容&#xff1a;多路转发的常见方式select&#xff0c;poll&#xff0c;epoll 文章目录 一、五种IO模型二、多路转发的常见接口1.select2、poll3、epoll 总结 前言 Linux下一切皆文件&#xff0c;是文件就会存在IO的情况&#xff0c;IO的方式决定了效率的高低。 一、五种…

【Solidity】Remix在线环境及钱包申请

好久没有学习区块链方面的知识了&#xff0c;目前通过自学大致掌握了Fabric联盟链的搭建&#xff0c;链码编写、部署&#xff0c;api调用&#xff0c;可以独立开发出一些基于fabric的应用&#xff0c;感觉开发出去中心化的应用还是很有意思的&#xff0c;因为他与之前开发的ssm…

Git 案例(企业如何使用git开发项目)

一、企业中我们是如何开发 1) 入职第一天,管理人员分配/git账号密码 2) 开发人员下载代码即文档/ 根据文档将环境搭建成功 3) 团队一般会给你讲讲项目相关的支持 4) 你接到第一个需求(或者某个功能,一般要经过沟通,分析,设计...等过程) 5) 创建feature分支(一般一个需求对应…

【计算机网络实验/wireshark】tcp建立和释放

wireshark开始捕获后&#xff0c;浏览器打开xg.swjtu.edu.cn&#xff0c;网页传输完成后&#xff0c;关闭浏览器&#xff0c;然后停止报文捕获。 若捕获不到dns报文&#xff0c;先运行ipconfig/flushdns命令清空dns缓存 DNS报文 设置了筛选条件&#xff1a;dns 查询报文目的…

【Java对象】一文读懂 Java 对象庐山真面目及指针压缩

文章目录 版本及工具介绍Java 对象结构对象头mark word 标记字mark word 标记字解析Lock Record class point 类元数据指针 实例数据对齐填充为什么需要对齐填充 常见 Java 数据类型对象分析ArrayListLongStringByteBoolean 其它指针压缩前置知识&#xff1a;32位操作系统为什么…

AI写作软件哪个好?这3个AI写作神器用了都说好!

随着信息时代的快速发展&#xff0c;AI写作早已成为人们创作内容的重要途径之一&#xff0c;在使用AI软件进行创作之前&#xff0c;当然要选择一个优质的写作软件&#xff0c;不过只要你拥有了这3款写作神器&#xff0c;你就能轻松创作出高质量的文章&#xff0c;我们一起来看看…

【uniapp】uview1.x 的 u-upload 上传点击删除隐藏 modal 提示框

uview1.x 版本的 upload 默认在图片成功上传后&#xff0c;再点击右上角删除按钮时会弹出提示框&#xff0c;如图&#xff1a; 但是有时又不需要&#xff0c;想要直接提示删除成功即可&#xff0c;由于官网没有给出点击删除按钮时所调用的钩子函数&#xff0c;又无法操作 DOM&…

【unity小技巧】实现由滑动条控制音量的大小

文章目录 前言开始1.配置BGM2.滑动条3.文本组件4.新增音量控制脚本 完结 前言 这期来一个比较基础的课程&#xff0c;也是比较常用的&#xff0c;unity使用滑动条控制音量的大小 开始 1.配置BGM 2.滑动条 3.文本组件 4.新增音量控制脚本 public class VolumeController : M…

数据库实验:SQL的数据视图

目录 视图概述视图的概念视图的作用 实验目的实验内容实验要求实验过程 视图概述 视图是由数据库中的一个表或多个表导出的虚拟表&#xff0c;其作用是方便用户对数据的操作 视图的概念 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一…

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

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

树莓派4无法进入桌面模式(启动后出现彩色画面,然后一直黑屏,但是可以正常启动和ssh)

本文记录了这段比较坎坷的探索之路&#xff0c;由于你的问题不一定是我最终解决方案的&#xff0c;可能是前面探索路上试过的&#xff0c;所以建议按顺序看排除前置问题。 双十一又买了个树莓派 4B&#xff0c;插上之前树莓派 4B 的 TF 卡直接就能使用&#xff08;毕竟是一样规…

Java 8 新特性 Stream 的使用场景(不定期更新)

方便在写代码的过程中直接使用&#xff0c;好记性不如好文章&#xff0c;直接 CV 改了直接用。提高 办&#xff08;摸&#xff09;公&#xff08;鱼&#xff09;效&#xff08;时&#xff09;率&#xff08;间&#xff09;&#xff0c; 不然就直接问 GPT 也不是说不行。 只符合…

win10 + cmake3.17 编译libpng-1.6.34

需要预先编译zlib库当前的根目录为&#xff1a;D:\Depend_3rd_party\libpngx64\ 1. 下载并解压libpng-1.6.34&#xff0c;得到 D:\Depend_3rd_party\libpngx64\libpng-1.6.34 2. 创建build文件夹&#xff0c;install文件夹&#xff0c;得到 D:\Depend_3rd_party\libpngx64\i…

数据库--数据库约束/聚合查询/分组查询/联合查询

前言 逆水行舟&#xff0c;不进则退&#xff01;&#xff01;&#xff01; 目录 数据库约束 聚合查询 分组查询 联合查询 联合查询---内连接与外连接 补充 联合查询用到的代码 数据库约束 not null 约束&#xff1a;在创建表的时候&#xff0c;可以指定列…