freeRTOS使用:事件标志组的介绍和使用
freeRTOS中还有一种可以用于任务之间同步的手段 — 事件标志组。
假如在项目中,有些任务或者多种动作需要等到某种条件成立的时候才能被执行,不成立的时候不执行,这种情况就可以考虑使用事件标志组。
设定一个事件标志,在项目运行过程中监控某种条件是否成立,如果条件成立了就设置这个事件标志位,另外一个地方如果发现这个标志位有效了,就执行这个任务或者动作,完成这个同步的过程。
0、事件标志组的配置 & 原理
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 个。
1、8个和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;
再看时间标志组的结构体:
ypedef 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、事件标志组的创建
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。
xEventGroup:是事件标志组句柄。uxBitsToSet:表示 24 个可设置的事件标志位,EventBits_t 是定义的 32 位变量,低 24 位用于事件标志设置。变量 uxBitsToSet 的低 24 位的某个位设置为 1,那么被设置的 事件标志组的相应位就设置为 1。变量 uxBitsToSet 设置为 0 的位对事件标志相应位没有影响。比如设置变量 uxBitsToSet = 0x0003 就表示将事件标志的位 0 和位 1 设置为 1,其余位没有变化。pxHigherPriorityTaskWoken:用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是pdTRUE, 说明有高优先级任务要执行,否则没有。返回值:如果消息成功发送给 daemon 任务(就是 FreeRTOS 的定时器任务)返回 pdPASS,否则 返回 pdFAIL,另外 daemon 任务中的消息队列满了也会返回 pdFAIL。
使用前一定要保证事件标志已经通过函数 xEventGroupCreate 创建了。同时要在 FreeRTOSConfig.h 文件中使能如下三个宏定义:
#define INCLUDE_xEventGroupSetBitFromISR 1 #define configUSE_TIMERS 1 #define INCLUDE_xTimerPendFunctionCall 1
3、事件标志组的位清除
3.1、在任务中设置
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
函数描述:
xEventGroup:需要操作的事件标志组;uxBitsToClear:要清零的事件位,比如要清楚bit2就设置为0x04。返回值:将指定事件位清零之前的事件组值。
3.2、在中断中设置
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
函数描述:
xEventGroup:需要操作的事件标志组;uxBitsToClear:要清零的事件位,比如要清楚bit2就设置为0x04。返回值:将指定事件位清零之前的事件组值。
4、等待时间标志组中的指定位被设置
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 等待事件标志被设置。
xEventGroup:是事件标志组句柄。uxBitsToWaitFor:表示等待 24 个事件标志位中的指定标志。EventBits_t 是定义的 32 位变量,低 24 位用于事件标志设置。比如设置变量 uxBitsToWaitFor = 0x0003 就表示等待事 件标志的位 0 和位 1 设置为 1。此参数切不可设置为 0。xClearOnExit:选择是否清除已经被置位的事件标志。如果这个参数设置为 pdTRUE,且函数 xEventGroupWaitBits 在参数 xTicksToWait 设置的溢出时间内返回,那么相应被设置的事件标志 位会被清零。如果这个参数设置为 pdFALSE,对已经被设置的事件标志位没有影响。xWaitForAllBits:选择是否等待所有的标志位都被设置。如果这个参数设置为 pdTRUE,要等待第 2 个参 数 uxBitsToWaitFor 所指定的标志位全部被置 1,函数才可以返回。当然,超出了在参数 xTicksToWait 设置的溢出时间也是会返回的。如果这个参数设置为 pdFALSE,第 2 个参数 uxBitsToWaitFor 所指定的任何标志位被置 1,函数都会返回,超出溢出时间也会返回。xTicksToWait:设置等待时间,单位时钟节拍周期。如果设置为 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个时钟节拍 }}
作者简介:
本人95后技术男,从事嵌入式软件开发,专注于技术成长和技术分享。目标是每天进一步一点点,通过技术改变自己的生活,创造自己的美好未来!如果你也对嵌入式感兴趣,欢迎关注我呀!
声明:
创作者:嵌入式之入坑笔记
文章版权归作者所有,转载请注明出处!