freeRTOS学习 — 事件标志组,很好用哦!!!

freeRTOS中还有一种可以用于任务之间同步的手段 — 事件标志组。

假如在项目中,有些任务或者动作需要等到某种条件成立的时候才能被执行,不成立的时候不执行,这种情况就可以考虑使用事件标志组。设定一个事件标志,在项目运行过程中监控某种条件是否成立,如果条件成立了就设置这个事件标志位,另外一个地方如果发现这个标志位有效了,就执行这个任务或者动作,完成这个同步的过程。

1、事件标志组的配置 & 原理

FreeRTOS 中事件标志的实现,是在 FreeRTOSConfig.h 文件中配置的,如下:

1)选择 8 个可用的事件标志组

 #define configUSE_16_BIT_TICKS 	1

配置宏定义 configUSE_16_BIT_TICKS 为 1 时,每创建一个事件标志组,用户可以使用的事件标志是8 个。

2)选择 24 个可用的事件标志组

#define configUSE_16_BIT_TICKS 	0

配置宏定义 configUSE_16_BIT_TICKS 为 0 时,每创建一个事件标志组,用户可以使用的事件标志是24 个。

上面说的 8 个和 24 个事件标志是怎么回事呢?

首选看看宏 configUSE_16_BIT_TICKS 定义了之后做了些什么吧?在文件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

	/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
	not need to be guarded with a critical section. */
	#define portTICK_TYPE_IS_ATOMIC 1
#endif

注意这个 TickType_t 这个类型重声明!!!

以及下面这个类型重定义:

typedef TickType_t EventBits_t;

再看时间标志组的结构体:

typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;			// 看这个、看这个、看这个!!! 嘿嘿!!!
	List_t xTasksWaitingForBits;		/*< List of tasks waiting for a bit to be set. */

	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;
	#endif

} EventGroup_t;

注意看 EventBits_t uxEventBits;

最后看时间标志组的创建过程,代码如下:

EventGroupHandle_t xEventGroupCreate( void )
{
   EventGroup_t *pxEventBits;

	pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
	if( pxEventBits != NULL )
	{
		pxEventBits->uxEventBits = 0;	// 记得看我、记得看我、记得看我!!! 嘻嘻!!
		vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
		traceEVENT_GROUP_CREATE( pxEventBits );
	}
	else
	{
		traceEVENT_GROUP_CREATE_FAILED();
	}

	return ( EventGroupHandle_t ) pxEventBits;
}

看上面代码的 pxEventBits->uxEventBits = 0; 应该就明白了吧!!!

其实freeRTOS的事件标志组的实现原理就是定义了一个变量,如果是 16 位变量,就仅使用了低 8bit
;如果定义了一个 32 位变量,就仅使用了低 24bit。

每一个 bit 用 0 和 1 两种状态来代表事件标志。

2、事件标志组的API管理函数

2.1、事件标志组的创建

函数原型:

EventGroupHandle_t xEventGroupCreate( void );

函数描述:

函数 xEventGroupCreate 用于创建事件标志组。

返回值,如果创建成功,此函数返回事件标志组的句柄,失败会返回 NULL。

2.2、事件标志组的位设置

(1)在任务中设置
函数原型:

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, 	/* 事件标志组句柄 */
    			     			 const EventBits_t uxBitsToSet ); 	/* 事件标志位设置 */

函数描述:

函数 xEventGroupSetBits 用于设置指定的事件标志位为 1。

第 1 个参数是事件标志组句柄。

第 2 个参数表示 24 个可设置的事件标志位,EventBits_t 是定义的 32 位变量,低 24 位用于事件标志设置。变量 uxBitsToSet 的低 24 位的某个位设置为 1,那么被设置的事件标志组的相应位就设置为 1。变量 uxBitsToSet 设置为 0 的位对事件标志相应位没有影响。比如设置变量 uxBitsToSet = 0x0003 就表示将事件标志的位 0 和位 1 设置为1,其余位没有变化。

返回当前的事件标志组数值。

(2)在中断中设置
函数原型:

BaseType_t xEventGroupSetBitsFromISR(
    									EventGroupHandle_t xEventGroup, 	    /* 事件标志组句柄 */
    									const EventBits_t uxBitsToSet, 			/* 事件标志位设置 */
    									BaseType_t *pxHigherPriorityTaskWoken ); /* 高优先级任务是否被唤醒的状态保存 */

函数描述:

函数 xEventGroupSetBits 用于设置指定的事件标志位为 1。

第 1 个参数是事件标志组句柄。

第 2 个参数表示 24 个可设置的事件标志位,EventBits_t 是定义的 32 位变量,低 24 位用于事件标志设置。变量 uxBitsToSet 的低 24 位的某个位设置为 1,那么被设置的事件标志组的相应位就设置为 1。变量 uxBitsToSet 设置为 0 的位对事件标志相应位没有影响。比如设置变量 uxBitsToSet = 0x0003 就表示将事件标志的位 0 和位 1 设置为 1,其余位没有变化。

第3个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值pdTRUE,
说明有高优先级任务要执行,否则没有。

返回值,如果消息成功发送给 daemon 任务(就是 FreeRTOS 的定时器任务)返回 pdPASS,否则
返回 pdFAIL,另外 daemon 任务中的消息队列满了也会返回 pdFAIL。

使用前一定要保证事件标志已经通过函数 xEventGroupCreate 创建了。同时要在 FreeRTOSConfig.h
文件中使能如下三个宏定义:

#define INCLUDE_xEventGroupSetBitFromISR 		1
#define configUSE_TIMERS 						1
#define INCLUDE_xTimerPendFunctionCall 		1

3、事件标志组的位清除

(1)在任务中设置

EventBits_t xEventGroupClearBits(
                                 EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToClear );

函数描述:

xEventGroup:需要操作的事件标志组;

uxBitsToClear:要清零的事件位,比如要清楚bit2就设置为0x04。

返回值:将指定事件位清零之前的事件组值。

(2)在中断中设置

BaseType_t xEventGroupClearBitsFromISR(
                                EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToClear );

xEventGroup:需要操作的事件标志组;

uxBitsToClear:要清零的事件位,比如要清楚bit2就设置为0x04。

返回值:将指定事件位清零之前的事件组值。

4、等待时间标志组中的指定位被设置

(1)在任务中等待
函数原型:

EventBits_t xEventGroupWaitBits(
                    				const EventGroupHandle_t xEventGroup,  /* 事件标志组句柄 */
                    				const EventBits_t uxBitsToWaitFor,     /* 等待被设置的事件标志位 */
                    				const BaseType_t xClearOnExit,         /* 选择是否清零被置位的事件标志位 */
                    				const BaseType_t xWaitForAllBits,      /* 选择是否等待所有标志位都被设置 */
                    				TickType_t xTicksToWait );             /* 设置等待时间 */

函数描述:

函数 xEventGroupWaitBits 等待事件标志被设置。

第 1 个参数是事件标志组句柄。

第 2 个参数表示等待 24 个事件标志位中的指定标志,EventBits_t 是定义的 32 位变量,低 24 位用于事件标志设置。比如设置变量 uxBitsToWaitFor = 0x0003 就表示等待事件标志的位 0 和位 1 设置为 1。此参数切不可设置为 0。

第 3 个参数选择是否清除已经被置位的事件标志,如果这个参数设置为 pdTRUE,且函数
xEventGroupWaitBits 在参数 xTicksToWait 设置的溢出时间内返回,那么相应被设置的事件标志
位会被清零。如果这个参数设置为 pdFALSE,对已经被设置的事件标志位没有影响。

第 4 个参数选择是否等待所有的标志位都被设置,如果这个参数设置为 pdTRUE,要等待第 2 个参
数 uxBitsToWaitFor 所指定的标志位全部被置 1,函数才可以返回。当然,超出了在参数
xTicksToWait 设置的溢出时间也是会返回的。如果这个参数设置为 pdFALSE,第 2 个参数
uxBitsToWaitFor 所指定的任何标志位被置 1,函数都会返回,超出溢出时间也会返回。

第 5 个参数设置等待时间,单位时钟节拍周期。如果设置为 portMAX_DELAY,表示永久等待。
返回值:由于设置的时间超时或者指定的事件标志位被置 1,导致函数退出时返回的事件标志组数值。

5、删除事件标志组

void vEventGroupDelete( EventGroupHandle_t xEventGroup );
xEventGroup:要删除的时间标志组

6、事件标志组的示例demo

代码中创建了两个任务和一个24bit可用的时间标志组,一个任务通过按键扫描设置事件标志组的值,通过按键按三下设置事件标志组的值为0x00000008,然后另外一个任务阻塞等待,直到检测到事件标志组被设置为0x00000008之后翻转LED的闪烁。

EventGroupHandle_t FlagEventGroup;  //事件标志组
FlagEventGroup = xEventGroupCreate();   //创建事件标志组 24bit可用

//task1任务函数 
void task1_task(void *pvParameters) //prio = 2
{
    u8 keyVal = 0;
    EventBits_t FlagVal;
    u32 setVal = 0x0001;
    while(1)
    {
        keyVal = KEY_Scan(0);
        if(keyVal == KEY0_PRES) 
        {
           FlagVal = xEventGroupSetBits(FlagEventGroup, setVal); 
           printf("事件标志组设置值为:%d\r\n",FlagVal);
           xEventGroupClearBits(FlagEventGroup, setVal);	//清除标志位
           setVal <<= 1;
        }
        LED1 ^= 1;
        vTaskDelay(200);   //延时n个时钟节拍	
    }
}

//task2任务函数  
void task2_task(void *pvParameters) //prio = 3
{
    u32 i,j;
    u32 TxStr[3] = {0};
	while(1)
	{
        xEventGroupWaitBits(FlagEventGroup,	//事件标志组句柄
                            0x0008,			//等待 24 个事件标志位中的指定标志
                            pdFALSE,			//是否清除已经被置位的事件标志,pdTRUE - 清除,pdFALSE - 不清除
                            pdFALSE,			//是否等待所有的标志位都被设置,pdTRUE - 是,pdFALSE - 不是
                            portMAX_DELAY 		//等待时间,单位时钟节拍周期
                           );
        
        LED0 ^= 1;

        vTaskDelay(500);       //延时n个时钟节拍	
	}
}

如果你也对嵌入式技术感兴趣,欢迎关注微信公众号“嵌入式之入坑笔记”,一起学习讨论啊!
在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式之入坑笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值