FreeRTOS事件(event)详解及示例用法讲解

文章目录

  • 事件
  • 函数解析
  • 示例
  • 事件

      事件,实际上是一种任务间通信的机制,主要用于实现多任务间的同步其只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。即可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理;同样,也可以是多个任务同步多个事件。
      FreeRTOS中任务可以通过设置事件位来实现事件的触发和等待操作。但FreeRTOS的事件仅用于同步,不提供数据传输功能,其具有如下特点:

  • 事件只与任务相关联事件相互独立,一个32位的事件集合(EventBits_t类型的变量,实际可用与表示事件的只有24位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共24种事件类型;
  • 事件仅用于同步不提供数据传输功能;
  • 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次;
  • 允许多任务对同一事件进行读写操作;
  • 支持事件等待超时机制
  • 应用

      事件在一定程度上可以代替信号量,用于任务与任务间、中断与任务间同步
      与信号量不同之处在于,事件是不可累计的,而信号量是可累计。并且,一个任务使用事件同步时,可以等待多个事件产生才进行同步;而信号量是单一的同步操作,只能与一个中断或任务同步,无法与多个中断或任务同步。


    事件运行机制示意图

      实际使用事件相关函数前,需要将configSUPPORT DYNAMIC ALLOCATION置1。


    函数解析

    前奏

  • 定义事件控制权柄所用的EventGroupHandle_t其实是一个结构体指针,其声明为typedef struct EventGroupDef_t * EventGroupHandle_t
  • 事件控制块EventGroup_t,内部最多含有四个变量,其中两个变量uxEventBitsxTasksWaitingForBits是一直可使用,但变量uxEventGroupNumberucStaticallyAllocated收到FreeRTOS配置管理
  • typedef struct EventGroupDef_t
    {
        EventBits_t uxEventBits;
        List_t xTasksWaitingForBits; 
        
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t uxEventGroupNumber;
        #endif
    
        #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) \
         && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
            uint8_t ucStaticallyAllocated;
        #endif
    } EventGroup_t;
    

       EventBits_t事件标志组存储在EventBits_t 类型的变量中。
       若FreeRTOS配置时,将configUSE_16_BIT_TICKS定义为1,那么EventBits_t就为16位,其中只有8位存储时间组;否则,若configUSE_16_BIT_TICKS定义为0,那么EventBits_t就为32位,其中24位存储事件组。每一位代表一个事件是否发生(事件发生为1,否则为0)。倘若兄弟认为8位或24位事件组不够用,还可以使用与或非等逻辑运算来组成更多事件。🤣🤣🤣
       xTasksWaitingForBits:该变量是一个列表,内部存储等待此事件的所有任务
       uxEventGroupNumber:记录事件组数量。
       ucStaticallyAllocated:记录时间组分配方式,如果事件组是静态分配的,则设置为 pdTRUE,以确保不尝试释放内存。

    动态创建事件

    原函数

    EventGroupHandle_t xEventGroupCreate( void );
    

    参数解析

  • 返回值:类型为EventGroupHandle_t,其为事件的控制权柄。
  • 函数说明
      动态创建一个事件。

    静态创建事件

    原函数

    EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );
    

    参数解析

  • StaticEventGroup_t * pxEventGroupBuffer:为事件组分配的空间;
  • 返回值:类型为EventGroupHandle_t,其为事件的控制权柄
  • 函数说明
       静态创建事件组,创建时需要传入存储空间。

    创建事件

    原函数

    void vEventGroupDelete( EventGroupHandle_t xEventGroup );
    

    参数解析

  • EventGroupHandle_t xEventGroup:传入事件组控制权柄;
  • 函数说明
     &esnp;删除事件组,只要将事件组控制权柄传入即可。删除只能删除已经创建成功的事件组

    事件触发

    原函数

    
    EventBits_t xEventGroupSetBits( 
    								EventGroupHandle_t xEventGroup,
                                    const EventBits_t uxBitsToSet
                                  );
    

    参数解析

  • EventGroupHandle_t xEventGroup:事件组的控制权柄;
  • const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
  • 返回值:返回函数操作的事件组的值;
  • 函数说明
      触发事件组中某个事件(事件对应事件组的位置),其位置由uxBitsToSet的值决定。
      该函数内部有判断机制,即的判断该事件是否创建,只有事件组创建成功以及操作位有效,该函数才能正常运作

    中断中唤醒事件

    原函数

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

    参数解析

  • EventGroupHandle_t xEventGroup:事件组的操作句柄;
  • const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
  • BaseType_t * pxHigherPriorityTaskWoken:在使用之前必须初始化成pdFALSE;
  • 返回值:类型为BaseType_t。若其值为pdFalse,则消息已经发送成功;否则,发送失败。
  • 函数说明
      是xEventGroupSetBits函数的中断版,其功能是中断中唤醒某个事件。

    等待事件唤醒

    原函数

    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:等待的事件,这里可以是多个事件,使用或运算即可连接;
  • const BaseType_t xClearOnExit:当其值为pdTRUE时,一旦监测到匹配的事件,系统就会将删除由该事件引起的标志位;否则,当其值为pdFALSE时,将删除标志位;
  • const BaseType_t xWaitForAllBits:当其值为pdTURE时,相应的uxBitsToWaitFor中多个事件应该使用&连接;否则当其值为pdFALSE时,uxBitsToWaitFor中多个事件应该使用|连接。这样函数返回值才是对应标志位的值,事件才能够触发。
  • TickType_t xTicksToWait:最大超时时间,单位为系统节拍时间,使用portMAX_DELAY即相当于一直阻塞;
  • 返回值:类型为EventBits_t,返回事件中事件标志位,但是返回值很可能并不是用户指定的事件位,需要再进行判断再处理。
  • 函数说明
      xEventGroupWaitBits函数的作用是等待事件触发。若需要等待的事件一直未触发,函数就会阻塞,阻塞时间为最大超时时间xTicksToWait

    清除事件标志位

    原函数

    EventBits_t xEventGroupClearBits( 
    									EventGroupHandle_t xEventGroup, 
    									const EventBits_t uxBitsToClear
    							    );
    

    参数解析

  • EventGroupHandle_t xEventGroup:事件组操作句柄;
  • const EventBits_t uxBitsToClear:时间组中需要清除的位置;
  • 返回值:类型EventBits_t,返回事件清除前的值;
  • 函数说明
      清除事件组中对应位置的标志位。在获取事件时对应标志位未擦除,那么可以使用该函数做显示擦除

    中断中清除事件标志位

    原函数

    BaseType_t xEventGroupClearBitsFromISR( 
    										EventGroupHandle_t xEventGroup,
    										const EventBits_t uxBitsToSet 
    									  );
    

    参数解析

  • EventGroupHandle_t xEventGroup:事件组操作句柄;
  • const EventBits_t uxBitsToClear:时间组中需要清除的位置;
  • 返回值:类型EventBits_t,返回事件清除前的值;
  • 函数说明
      是函数xEventGroupClearBits的中断版,功能上与之一样。


    示例

      声明8个事件,并且创建一个FreeRTOS事件,用于任务同步,再创建两个任务。完成:任务1,等待事件触发,7个事件中触发一个事件后反转对应LED的状态,当事件8触发时,统计其触发次数并且显示到LCD屏上; 任务2,每个1s发送一个事件触发请求,并且反转LED8的状态。

    核心源码

    //任务控制权柄
    TaskHandle_t xHandleTsak[2];
    // 事件控制权柄
    EventGroupHandle_t myxEventGroupHandle_t = NULL;
    // 声明事件
    #define EVENT1 (0x01 << 0)
    #define EVENT2 (0x01 << 1)
    #define EVENT3 (0x01 << 2)
    #define EVENT4 (0x01 << 3)
    #define EVENT5 (0x01 << 4)
    #define EVENT6 (0x01 << 5)
    #define EVENT7 (0x01 << 6)
    #define EVENT8 (0x01 << 7)
    
    int main(void)
    {	
    	//存储创建任务的返回值
    	BaseType_t xReturn[2] ;
    	
    	// 创建事件
    	myxEventGroupHandle_t = xEventGroupCreate();
    	if(myxEventGroupHandle_t == 0)
    		//点亮LED7
    		changeLedStateByLocation(LED8,ON);
    	
    	//动态创建任务
    	xReturn[0] = xTaskCreate(
    						(TaskFunction_t )eventTask1,(const char *)"task1",
    						(uint16_t)128,(void*) NULL,1,&xHandleTsak[0]);
    	xReturn[1] = xTaskCreate(
    						(TaskFunction_t )eventTask2,(const char *)"task2",
    						(uint16_t)128,(void*) NULL,1,&xHandleTsak[1]);
    						
    	if (pdPASS != xReturn[0])
    		//点亮LED6
    		changeLedStateByLocation(LED6,ON);				
    	if (pdPASS != xReturn[1])
    		//点亮LED7
    		changeLedStateByLocation(LED7,ON);				
    	if (pdPASS == xReturn[1] == xReturn[0])
    		vTaskStartScheduler();
    	
    	return 0;
    }
    
    /********************************************
    * 函数功能:事件测试函数1
    * 函数参数:无
    * 函数返回值:无
    ********************************************/
    void eventTask1(void)
    {
    	// 设置变量接收事件
    	EventBits_t r_event;
    	// 保存LED的状态
    	int ledState[] = {0,0,0,0,0,0,0};
    	// 记录LED的位置
    	uint16_t ledLocation[] = {LED1,LED2,LED3,LED4,LED5,LED6,LED7};
    	// 记录EVENT8触发次数
    	uint16_t eventTriggerCount = 0;
    	// 保存显示到LCD中的数据
    	char temp[30];
    	// 用于循环
    	int i = 0;
    	
    	while(1)
    	{
    		//阻塞等待8个事件中任意一个事件
    		r_event = xEventGroupWaitBits(myxEventGroupHandle_t,EVENT1|EVENT2|EVENT3|EVENT4|EVENT5|EVENT6|EVENT7|EVENT8,
    									  pdTRUE,pdFALSE,portMAX_DELAY);
    		//判断事件类型
    		if((r_event&EVENT1) !=0)
    			++ledState[0];
    		else if((r_event&EVENT2) !=0)
    			++ledState[1];
    		else if((r_event&EVENT3) !=0)
    			++ledState[2];
    		else if((r_event&EVENT4) !=0)
    			++ledState[3];
    		else if((r_event&EVENT5) !=0)
    			++ledState[4];
    		else if((r_event&EVENT6) !=0)
    			++ledState[5];
    		else if((r_event&EVENT7) !=0)
    			++ledState[6];
    		else
    		{ 
    			sprintf(temp," Event8 count:%d",++eventTriggerCount);
    			LCD_DisplayStringLine(Line4,(uint8_t*)temp);
    			changeAllLedByStateNumber(OFF);
    		}
    		
    		// 设置每个LED灯状态
    		for(i=0;i<7;++i)
    			// 显示7个LED灯目前的状态
    			changeLedStateByLocation(ledLocation[i],ledState[i]%2);
    	}
    }
    
    /********************************************
    * 函数功能:事件测试函数2
    * 函数参数:无
    * 函数返回值:无
    ********************************************/
    void eventTask2(void)
    {
    	// 记录本次需要触发事件
    	uint16_t event[] = {EVENT1,EVENT2,EVENT3,EVENT4,EVENT5,EVENT6,EVENT7,EVENT8};
    	int	i = -1;
    	// 保存系统时间
    	static portTickType myPreviousWakeTime;
    	// 保存阻塞时间
    	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 1000UL );
    	// 获取当前时间
    	myPreviousWakeTime = xTaskGetTickCount();
    	while(1)
    	{
    		// 每次执行都翻转LED8状态 以保证肉眼看到该任务在运行
    		rollbackLedByLocation(LED8);
    		// 发送事件同步
    		xEventGroupSetBits(myxEventGroupHandle_t,event[++i%8]);
    		// 非阻塞延时1s
    		xTaskDelayUntil( &myPreviousWakeTime,xDelay1000ms );
    	}
    }
    
    

    结果




    事件测试结果图



      小编也有其他的一些相关文章,欢迎各位看官点击观看😉😉😉

  • 【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
  • 【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法
  • 【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法

    最后 ,欢迎大家留言或私信交流,共同进步!😁😁😁

  • 物联沃分享整理
    物联沃-IOTWORD物联网 » FreeRTOS事件(event)详解及示例用法讲解

    发表评论