文章目录
- 事件组结构体
- 创建事件组
- 事件组等待位
- 事件组设置位
事件组结构体
// 路径:Source/event_groups.c
typedef struct xEventGroupDefinition
{
EventBits_t uxEventBits;
List_t xTasksWaitingForBits;
} EventGroup_t;
uxEventBits 中的每一位表示某个事件是否发生。 具体事件由程序员来确定,大小为uint32_t。
xTasksWaitingForBits 为等待事件链表。 若某个事件检测到uxEventBits中所需要的位没有置位,就会将自己保存在该链表中等待。
创建事件组
使用 xEventGroupCreate 函数进行事件组创建
EventGroupHandle_t xEventGroupCreate( void )
// 1、申请事件组结构体 EventGroup_t 空间
-> pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
// 2、初始化结构体中uxEventBits 事件位
-> pxEventBits->uxEventBits = 0;
// 3、初始化等待事件组链表
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
事件组等待位
使用xEventGroupWaitBits 函数进行事件等待,函数定义如下:
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 :在退出的时候是否对等待的位进行清除
第四参数 const BaseType_t xWaitForAllBits:等待的位是同时满足还是只要一个成立就满足(等待位是 与的关系 还是 或的关系 )
第五参数 TickType_t xTicksToWait :等待的时间
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
// 1、关闭调度器,后面有详解
-> vTaskSuspendAll();
{
// 2、检查等待条件是否已经满足
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
// 3、等待条件已经满足,所以没有必要阻塞
if( xWaitConditionMet != pdFALSE )
{
// 4、判断退出前xClearOnExit 是否清零
if( xClearOnExit != pdFALSE )
pxEventBits->uxEventBits &= ~uxBitsToWaitFor; // 清零
}
// 判断是否愿意等待
else if( xTicksToWait == ( TickType_t ) 0 )
uxReturn = uxCurrentEventBits; // 不愿意等待,退出
else
{
// 记录标志位
if( xClearOnExit != pdFALSE )
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
if( xWaitForAllBits != pdFALSE )
uxControlBits |= eventWAIT_FOR_ALL_BITS;
}
// 将调用任务正在等待的比特存储在任务的事件中列表项,以便内核知道何时找到匹配项。然后进入阻塞状态
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
// 把当前任务加入xEventListItem 链表中
-> listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );
-> vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) );
// 把当前任务从就绪链表移到等待链表中
-> prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
}
vTaskSuspendAll函数 功能是关闭调度器。在之前的队列操作(包含信号量和互斥量操作)中无论是读还是写操作,第一步就是关中断。而在操作事件组的时候第一步却不需要直接关闭中断,只需要关闭调度器,这两者有什么区别呢?
对于队列操作,存在两种情况:
1、多个任务同时操作队列
2、中断 过程中可以对队列进行读写操作
考虑到第一种情况,多任务之间操作队列会相互影响,可以采取关闭调度器来解决,但是不能屏蔽掉中断中对队列的读写操作。如果只关闭调度器的话,中断对队列的操作同样会对正在的任务有影响。因此任务在对队列进行操作时,第一步一定是要关闭中断。
对于事件组操作,只存在任务之间的操作,中断过程中不会对事件组进行操作,因此只需要关闭调度器就可以实现任务之间对事件组操作的互斥。
事件组设置位
使用xEventGroupSetBits 函数进行事件设置
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
-> vTaskSuspendAll(); // 关闭调度器
{
// 1、设置对应的位
pxEventBits->uxEventBits |= uxBitsToSet;
// 2、唤醒所有满足条件的任务
while( pxListItem != pxListEnd )
{
if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
{
if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
xMatchFound = pdTRUE;
else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
xMatchFound = pdTRUE;
// 从链表中移除
vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
}
}