一、事件标志组
前面所介绍的队列、信号量,只能实现与单个任务进行同步。而有时候某个任务可能需要与多个事件或任务进行同步,此时,事件标志组的作用就凸显出来
1.1 事件标志组简介
事件标志位:用一个位,来表示事件是否发生
事件标志组:一组事件标志位的集合, 可以简单的理解事件标志组,就是一个(16/32)整数
事件标志组是一种实现任务/中断间通信的机制,主要用于实现多任务间的同步
根据configUSE_16_BIT_TICKS 的宏定义不同,每个事件标志组的位数也就不同
虽然使用了 32 位无符号的数据类型变量来存储事件标志, 但其中的高8位用作存储事件标志组的控制信息,低24位用作存储事件标志 ,所以说一个事件组最多可以存储 24 个事件标志
可以发现,事件标志组与外设的状态寄存器SR非常类似,每一位都代表一个事件是否发生(高8位除外)
1.2 事件标志组与队列、信号量的区别
此外:设置事件组不会阻塞,等待事件标志支持阻塞
二、事件组结构体
typedef struct EventGroupDef_t
{
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
/* 如果事件组是静态分配,则设置为pdTURE,以确保不尝试释放内存 */
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
#endif
} EventGroup_t;
成员 | 说明 |
uxEventBits | EventBits_t类型的变量,其中, 高八位:事件标志组的控制信息 剩余位:存储事件标志(1:发生;2:未发生) |
xTasksWaitingForBits | 链表,等待事件标志位的任务的链表 |
三、相关API
函数 | 描述 |
xEventGroupCreate() | 使用动态方式创建事件标志组 |
xEventGroupCreateStatic() | 使用静态方式创建事件标志组 |
xEventGroupClearBits() | 清零事件标志位 |
xEventGroupClearBitsFromISR() | 在中断中清零事件标志位 |
xEventGroupSetBits() | 设置事件标志位 |
xEventGroupSetBitsFromISR() | 在中断中设置事件标志位 |
xEventGroupWaitBits() | 等待事件标志位 |
xEventGroupSync() | 设置事件标志位,并等待事件标志位 |
2.1 动态创建事件组
使用条件:configSUPPORT_DYNAMIC_ALLOCATION必须在 FreeRTOSConfig.h 中设置为 1,或保留未定义状态(此时默认为 1)
EventGroupHandle_t xEventGroupCreate ( void ) ;
返回值 | 描述 |
NULL | 事件标志组创建失败 |
句柄 | 事件标志组创建成功 |
2.1.1 函数详解
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreate( void )
{
/* 创建一个事件标志组结构体指针:pxEventBits */
EventGroup_t * pxEventBits;
/* 申请结构体空间,并将首地址赋给指针变量:pxEventBits */
pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
/* 申请成功 */
if( pxEventBits != NULL )
{
/* 将结构体中的时间标志组变量初始化为0*/
pxEventBits->uxEventBits = 0;
/* 初始化等待任务链表 */
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
pxEventBits->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
traceEVENT_GROUP_CREATE( pxEventBits );
}
/* 申请失败 */
else
{
traceEVENT_GROUP_CREATE_FAILED();
}
/* 返回结构体的地址 */
return pxEventBits;
}
2.1.2 函数实现总结
(1)创建一个时间标志组的结构体指针变量
(2)申请内存空间
成功:将事件标志组变量初始化为0,并初始化等待事件标志任务列表
失败:定义但未实现的宏
(3)返回:事件标志组的结构体指针变量
2.2 清除事件标志位
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待清零的事件标志位 |
返回值 | 描述 |
整数 | 清零事件标志位之前事件组中事件标志位的值 |
注:要清零的事件标志位,可以是一位,也可以是多位
0x08: 清除bit3
0x09:清除bit3和bit0
2.3 设置事件标志位
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待设置的事件标志位 |
返回值 | 描述 |
整数 | 函数返回时,事件组中的事件标志位值 |
注:要设置的事件标志位,可以是一位,也可以是多位
0x08: 设置bit3
0x09:设置bit3和bit0
2.4 获取事件标志组值
2.5 等待事件标志位
等待事件标志组的某些位被设置,选择性的进入阻塞状态,中断中无法调用此函数
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
形参 | 描述 |
xEvenrGroup | 等待的事件标志组句柄 |
uxBitsToWaitFor | 等待的事件标志位,可以用逻辑或等待多个事件标志位 |
xClearOnExit | 成功等待到事件标志位后,清除事件组中对应的事件标志位, pdTRUE :清除uxBitsToWaitFor指定位; pdFALSE:不清除 |
xWaitForAllBits | 等待 uxBitsToWaitFor 中的所有事件标志位(逻辑与) pdTRUE:等待的位,全部为1 逻辑与 pdFALSE:等待的位,某个为1 逻辑或 |
xTicksToWait | 等待的阻塞时间 |
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回所等待到的事件标志位置1的事件标志组的值 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
2.4.1 函数详解
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
{
/**
* uxReturn:返回值(32位时间标志组变量)
* uxControlBits:记录控制位信息(高八位)
* xWaitConditionMet:记录等待条件是否已满足
* xAlreadyYielded:记录调度器的状态
*/
EventGroup_t * pxEventBits = xEventGroup;
EventBits_t uxReturn, uxControlBits = 0;
BaseType_t xWaitConditionMet, xAlreadyYielded;
BaseType_t xTimeoutOccurred = pdFALSE;
/* 检查用户没有去尝试等待内核本身使用的位(高8位),并且至少请求了一个位(低24位)。*/
configASSERT( xEventGroup );
configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
configASSERT( uxBitsToWaitFor != 0 );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
/* 挂起调度器 */
vTaskSuspendAll();
{
/* 取出结构体的32位变量,赋值给uxCurrentEventBits
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
/* 检查是否以满足等待条件 */
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
/* 等待条件已满足 */
if( xWaitConditionMet != pdFALSE )
{
/* 将32变量赋给uxReturn,并将等待时间设置为0 */
uxReturn = uxCurrentEventBits;
xTicksToWait = ( TickType_t ) 0;
/* 判断是否需要清除标志位 */
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 等待条件不满足,并且未设置等待时间 */
else if( xTicksToWait == ( TickType_t ) 0 )
{
/* 返回当前32位变量,并将超时标志置为:pdTURE */
uxReturn = uxCurrentEventBits;
xTimeoutOccurred = pdTRUE;
}
/* 等待条件不满足 */
else
{
/* 需要清除标志位,将第25位置1 */
if( xClearOnExit != pdFALSE )
{
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 需要等待所有标志位 or 至少一个标志位,将第26位置1 */
if( xWaitForAllBits != pdFALSE )
{
uxControlBits |= eventWAIT_FOR_ALL_BITS;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 此函数的功能:
** (1)控制位 | 等待的事件标志位 的结果,赋给列表项的值(用于升序排列)
** (2)将任务添加到等待事件组的列表中
** (3)将任务添加到阻塞列表中,任务进入阻塞状态
*/
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
/* 返回值赋为0 */
uxReturn = 0;
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
}
}
/* 恢复调度器 */
xAlreadyYielded = xTaskResumeAll();
if( xTicksToWait != ( TickType_t ) 0 )
{
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* The task blocked to wait for its required bits to be set - at this
* point either the required bits were set or the block time expired. If
* the required bits were set they will have been stored in the task's
* event list item, and they should now be retrieved then cleared. */
uxReturn = uxTaskResetEventItemValue();
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
{
taskENTER_CRITICAL();
{
/* The task timed out, just return the current event bit value. */
uxReturn = pxEventBits->uxEventBits;
/* It is possible that the event bits were updated between this
* task leaving the Blocked state and running again. */
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
{
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
xTimeoutOccurred = pdTRUE;
}
taskEXIT_CRITICAL();
}
else
{
/* The task unblocked because the bits were set. */
}
/* The task blocked so control bits may have been set. */
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
}
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
/* Prevent compiler warnings when trace macros are not used. */
( void ) xTimeoutOccurred;
return uxReturn;
}
2.6 同步函数
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait)
形参 | 描述 |
xEventGroup | 等待事件标志所在事件组 |
uxBitsToSet | 达到同步点后,要设置的事件标志 |
uxBitsToWaitFor | 等待的事件标志 |
xTicksToWait | 等待的阻塞时间,等待uxBitsToWaitFor参数值指定的 所有位设置完成的最长时间 |
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回等待到的事件标志位 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
特点:通过设置某一位或多位(uxBitsToSet) ,等待标志位(uxBitsToWaitFor),即设置了uxBitsToSet,还需要等待uxBitsToWaitFor被设置才可以进行后续操作