--FreeRTOS)
前言那什么是事件比如我有一个任务在这里运行需要某个外部的事件来触发它才能继续运行但是有些情况可能不止需要一个事件可能需要多个事件例如3个事件这3个不一样的事件来触发它他才能继续运行但是如果这三个事件都没有满足的话他就会停在这里阻塞等待这三个时间都发生的时候才会继续运行还有一种情况这里还有一个任务只需要这三个事件中的其中一个事件发生就可以继续运行所以事件或者事件集就是这样的一个概念。事件的基本概念事件是一种实现任务间通信的机制或者说任务与中断之间通信的机制或者说同步虽然没有办法传输数据但是多用于同步事件通信只能是事件类型的通信比如这里产生了事件事件发生了它并没有产生数据的传输。与信号量不同的是它可以实现一对多就像前言那里举的例子一样这里要等待3个事件或者4个甚至5个 6个...都是可以的但是如果使用信号量的话不可能在这里等着吧所以用事件是最简单的一个方法信号量是一对一的就是说信号量发生了就告诉你但是事件不同他可以一对多用一个事件来等待多个事件的发生也就是说一个任务可以等待多个事件的发生而且这个事件是有逻辑的通过“逻辑与”就是说3个事件都发生了才继续进行或者说用“逻辑或”就是说有其中一个事件发生事件1发生或者事件2发生或者事件3发生我都可以去继续执行也可以是多个任务同步多个事件也是可以的。事件组/集 存储在一个EventBits_t32位无符号整型变量类型的变量中其中有24个位来实现事件标志组。每一位代表一个事件当一个事件发生了这个位变成1或者说事件2发生了bit2为1或者bit3为1如果没有发生就是0如果事件都发生了就可以获得这些事件并进行同步“逻辑与”被称为关联同步指任务感兴趣的若干时间都发生时才被唤醒而逻辑或就是其中一个事件发生就可以了这些事件发生并不一定是同时发生的。事件的特性FreeRTOS提供的事件具有如下特点1.事件只与任务相关联也就是说一个任务可以等待多个事件事件相互独立一个32位的事件集合实际上可用的只有24位用于标识该任务发生的事件类型其中每一位表示一种事件的类型0就表示这个事件没有发生1就表示这个事件已经发生一共可以最大支持24个事件类型。2.事件仅用于同步不提供数据传输功能。3.事件没有排队性即多次向任务设置同一事件如果任务还没来得及读走等效于只设置一次。比如说事件1 2 3bit1还没有发生事件3已经发生了3次那么bit3位的1设置了多少次还是1bit1位的0始终是0如果事件1发生了的话就会变成1假如事件1发生了很多次他还是1就是说如果任务没来得及处理它就等于设置了一次如果任务处理了之后一般我们会选择将那个位清零再发生了又会被置1当然也可以不清零这根据用户的选择。4.允许多个任务对同一事件进行读写操作。比如说这里有个事件假设有任务5它需要任务1 2 3 4来进行同步那么这个任务1 2 3 4就对这个事件进行读写分别设置它的bit1 bit2 bit3 bit4,当这些任务处理完了之后任务5去读取它就可以知道任务1 2 3 4都设置完了那为什么要这样设置很简单比如说我有一台危险的机器启动往往要进行一些初始化即任务1 2 3 4初始化完当这些机器确认可以启动的时候才去启动它也就是任务5去启动它这是一种场景因此FreeRTOS的事件允许多个任务对同一事件进行读写操作5.支持事件等待超时机制。任务5在这里等如果10s没有启动就不等了去做其他的事情或者要将整个系统进行重启在FreeRTOS事件中每个事件获取的时候用户可以选择感兴趣的事件并且选择读取事件信息标记比如说逻辑与都发生了才去处理、逻辑或、以及是否清除标记就是说读了之后选择是否要清除这个事件那么清除和不清除有什么区别清除的话读了3个事件是我感兴趣的这3个事件都变为0下次回来再获取它的时候又要等着它的发生那么不清除呢就是3个事件独读了之后还是1那么下次回来再获取它的时候立马可以获取到了。这是根据应用场景来选择的。我们可以使用事件只用于做标记而无法传输数据。传输数据的话只能用队列做一些标记的话用事件更好。大家也会有疑问为什么不用一个变量呢其实事件就是一个变量32位无符号整型那为什么不直接用变量与信号量有什么区别刚刚已经讲过信号量是1对一这里是1对多。为什么不用变量变量怎么解决它的超时机制呢总不能一直轮询查询这个变量这是不可能的以周期来查询这也是不可能的如果事件很紧急的话以20ms为一个周期刚好在它离开之后还要过19ms才回来那么我在这1ms时发生了事件这19ms岂不是浪费了根本就查不到这个事件所以要实现怎么样用变量来实现这个超时机制而FreeRTOS中已经有一个变量实现了这个超时同时他是可以支持多个任务对同一事件进行读写的假如使用一个变量任务1可以对它写任务2也可以对它写任务3也可以对它写假如此时任务1对它写的时候正在写任务2突然对它打断了还没写完就打断了打断了之后任务2在这里写岂不是乱套了是吧所以说在系统不要用全局变量来处理用FreeRTOS提供的一些机制处理了会更好而且事件的发送是不可累计的就是说等效于多次发生的事件如果还没来的及处理等效于只发送一次因为它始终都是设置为1再设置还是为1。事件的运作机制接收事件时可以根据感兴趣的事件类型接收事件的单个或多个事件类型。事件接收成功后可根据用户使用场景可以使用xClearOnExit选项来清除已接收到的事件类型否则不会清除已收到的事件这样就需要用户显式清除事件位。还可以自定义通过传入参数xWaitForAllBits选择读取模式是等待所有感兴趣的事件还是等待感兴趣的任意一个事件。设置事件时对指定事件写入指定的事件类型设置事件集合的对应事件位为1可以一次同时写多个事件类型设置事件成功可能会触发任务调度。清除事件时根据参数事件句柄和待清除的事件类型对事件对应位进行清0操作。运作机制图示辅助理解事件标志组的任务控制块事件标志组存储在一个EventBits_t类型的变量中该变量在EventGroup_t事件控制块结构体中定义。//在event_groups.c 105行 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 #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; //在event_groups.h 133行 typedef TickType_t EventBits_t;EventBits_t 是一个32位的变量类型也不一定取决于系统是16位还是32位的。如果宏configUSE_16_BIT_TICKS定义为1那么变量uxEventBits就是16位的其中有8个位用来存储事件组如果宏configUSE_16_BIT_TICKS定义为0那么变量uxEventBits就是32位的其中有24个位用来存储事件组。其实整个控制块只有这一个是有用的还有一个是等待事件的列表xTasksWaitingForBits。后面的宏定义暂时不用管它目前还没有用到。创建事件xEventGroupCreate()用于创建一个事件并返回对应的句柄没有传入的参数。如果使用这个函数要把宏定义configSUPPORT_DYNAMIC_ALLOCATION打开//在event_groups.c 172行 #if( configSUPPORT_DYNAMIC_ALLOCATION 1 ) EventGroupHandle_t xEventGroupCreate( void ) { EventGroup_t *pxEventBits; /* Allocate the event group. */ pxEventBits ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); if( pxEventBits ! NULL ) { pxEventBits-uxEventBits 0; vListInitialise( ( pxEventBits-xTasksWaitingForBits ) ); #if( configSUPPORT_STATIC_ALLOCATION 1 ) { /* Both static and dynamic allocation can be used, so note this event group was allocated statically in case the event group is later deleted. */ pxEventBits-ucStaticallyAllocated pdFALSE; } #endif /* configSUPPORT_STATIC_ALLOCATION */ traceEVENT_GROUP_CREATE( pxEventBits ); } else { traceEVENT_GROUP_CREATE_FAILED(); } return ( EventGroupHandle_t ) pxEventBits; } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */首先要为事件控制块动态分配一个内存的空间用于保存这个事件而它里面有一个事件32位的一个变量将它设置为0并且把事件等待的列表初始化。这个列表表示有什么任务在等待这个事件。删除事件vEventGroupDelete()用于删除一个事件传入要删除事件的句柄在删除的时候如果有任务阻塞在这个事件上那么就要把事件从等待事件列表中移除。当事件组被删除之后阻塞在该事件组上的任务都会被解锁并向等待事件的任务返回事件组的值为0。//在event_groups.c 624行 void vEventGroupDelete( EventGroupHandle_t xEventGroup ) { EventGroup_t *pxEventBits ( EventGroup_t * ) xEventGroup; const List_t *pxTasksWaitingForBits ( pxEventBits-xTasksWaitingForBits ); vTaskSuspendAll(); { traceEVENT_GROUP_DELETE( xEventGroup ); while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) ( UBaseType_t ) 0 ) { /* Unblock the task, returning 0 as the event list is being deleted and cannot therefore have any bits set. */ configASSERT( pxTasksWaitingForBits-xListEnd.pxNext ! ( ListItem_t * ) ( pxTasksWaitingForBits-xListEnd ) ); ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits-xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); } #if( ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) ( configSUPPORT_STATIC_ALLOCATION 0 ) ) { /* The event group can only have been allocated dynamically - free it again. */ vPortFree( pxEventBits ); } #elif( ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) ( configSUPPORT_STATIC_ALLOCATION 1 ) ) { /* The event group could have been allocated statically or dynamically, so check before attempting to free the memory. */ if( pxEventBits-ucStaticallyAllocated ( uint8_t ) pdFALSE ) { vPortFree( pxEventBits ); } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } ( void ) xTaskResumeAll(); }删除就是一个释放就可以了并没有什么差别。如果等待的事件列表不是空的就把他移除掉就好了BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) { TCB_t *pxUnblockedTCB; BaseType_t xReturn; /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by the event flags implementation. */ configASSERT( uxSchedulerSuspended ! pdFALSE ); /* Store the new item value in the event list. */ listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); /* Remove the event list form the event flag. Interrupts do not access event flags. */ pxUnblockedTCB ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem ); configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( pxEventListItem ); /* Remove the task from the delayed list and add it to the ready list. The scheduler is suspended so interrupts will not be accessing the ready lists. */ ( void ) uxListRemove( ( pxUnblockedTCB-xStateListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); if( pxUnblockedTCB-uxPriority pxCurrentTCB-uxPriority ) { /* Return true if the task removed from the event list has a higher priority than the calling task. This allows the calling task to know if it should force a context switch now. */ xReturn pdTRUE; /* Mark that a yield is pending in case the user is not using the xHigherPriorityTaskWoken parameter to an ISR safe FreeRTOS function. */ xYieldPending pdTRUE; } else { xReturn pdFALSE; } return xReturn; }其实最终就是一个列表的移除然后把它添加到就绪列表中。下面的内容是事件的一些实现比如说等待事件、设置事件、清除事件以及事件的一个实验。等待事件xEventGroupWaitBits()通过这个函数任务可以知道事件标志组中的哪些位有什么事件发生了然后通过“逻辑与”、“逻辑或”等操作对感兴趣的事件进行获取并且这个函数实现了等待超时机制当且仅当任务等待的事件发生时任务才能获取到事件信息。需要注意的是如果等待到对应的事件就会返回对应的事件标志位由用户判断再做处理因为在事件超时的时候也会返回一个不能确定的事件值所以需要判断任务所等待的事件是否真的发生。//返回值是一个32位的变量 按位来返回 EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup, //事件句柄 const EventBits_t uxBitsToWaitFor, //等待位按位或的值 const BaseType_t xClearOnExit, //退出时是否要清楚的标志位 const BaseType_t xWaitForAllBits, //是否等待所有标志位 pdTRUE:逻辑与 pdFALSE:逻辑或 TickType_t xTicksToWait); //等待的时间//在event_groups.c 324行 EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) { EventGroup_t *pxEventBits ( EventGroup_t * ) xEventGroup; EventBits_t uxReturn, uxControlBits 0; BaseType_t xWaitConditionMet, xAlreadyYielded; BaseType_t xTimeoutOccurred pdFALSE; /* Check the user is not attempting to wait on the bits used by the kernel itself, and that at least one bit is being requested. */ 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(); { const EventBits_t uxCurrentEventBits pxEventBits-uxEventBits; /* Check to see if the wait condition is already met or not. */ xWaitConditionMet prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits ); if( xWaitConditionMet ! pdFALSE ) { /* The wait condition has already been met so there is no need to block. */ uxReturn uxCurrentEventBits; xTicksToWait ( TickType_t ) 0; /* Clear the wait bits if requested to do so. */ if( xClearOnExit ! pdFALSE ) { pxEventBits-uxEventBits ~uxBitsToWaitFor; } else { mtCOVERAGE_TEST_MARKER(); } } else if( xTicksToWait ( TickType_t ) 0 ) { /* The wait condition has not been met, but no block time was specified, so just return the current value. */ uxReturn uxCurrentEventBits; } else { /* The task is going to block to wait for its required bits to be set. uxControlBits are used to remember the specified behaviour of this call to xEventGroupWaitBits() - for use when the event bits unblock the task. */ if( xClearOnExit ! pdFALSE ) { uxControlBits | eventCLEAR_EVENTS_ON_EXIT_BIT; } else { mtCOVERAGE_TEST_MARKER(); } if( xWaitForAllBits ! pdFALSE ) { uxControlBits | eventWAIT_FOR_ALL_BITS; } else { mtCOVERAGE_TEST_MARKER(); } /* Store the bits that the calling task is waiting for in the tasks event list item so the kernel knows when a match is found. Then enter the blocked state. */ vTaskPlaceOnUnorderedEventList( ( pxEventBits-xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait ); /* This is obsolete as it will get set after the task unblocks, but some compilers mistakenly generate a warning about the variable being returned without being set if it is not done. */ 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 tasks 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(); } } taskEXIT_CRITICAL(); /* Prevent compiler warnings when trace macros are not used. */ xTimeoutOccurred pdFALSE; } 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 ); return uxReturn; }前面是一些断言的操作没什么好说的。首先要挂起调度器为什么因为接下来可能要操作一些列表不希望其它的任务来打扰我就挂起。这里就判断一下任务等待的事件与我们已经发生的事件是否已经匹配这个当前事件uxCurrentEventBits就是事件组的标志位uxBitsToWaitFor是用户当前等待的事件位以及一个是否等待所有的标志xWaitForAllBitsstatic BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) { BaseType_t xWaitConditionMet pdFALSE; if( xWaitForAllBits pdFALSE ) { /* Task only has to wait for one bit within uxBitsToWaitFor to be set. Is one already set? */ if( ( uxCurrentEventBits uxBitsToWaitFor ) ! ( EventBits_t ) 0 ) { xWaitConditionMet pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } else { /* Task has to wait for all the bits in uxBitsToWaitFor to be set. Are they set already? */ if( ( uxCurrentEventBits uxBitsToWaitFor ) uxBitsToWaitFor ) { xWaitConditionMet pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } return xWaitConditionMet; }如果xWaitForAllBits pdFALSE不等待所有也就是只有一个发生就等待到了事件这是一个逻辑或uxCurrentEventBits uxBitsToWaitFor也就是有一个位发生了就直接返回一个pdTRUE否则就是要等待所有事件发生了就( uxCurrentEventBits uxBitsToWaitFor ) uxBitsToWaitFor都发生了的话就返回一个pdTRUE否则的话就什么也不做。如果xWaitConditionMet是一个pdTRUE的话也就说已经满足了就可以直接将等待的时间xTicksToWait设置为0就是不等待不等待的话就直接返回了而且我们要看一下提出的时候是否要进行一个清除如果需要清除的话就将对应的位进行pxEventBits-uxEventBits ~uxBitsToWaitFor。如果xTicksToWait ( TickType_t ) 0的话就直接返回uxReturn uxCurrentEventBits;也就是当前一个事件的位。否则不等于pdTRUE不满足的话就直接将他的控制位设置一下之前说过这个32位的变量实际上只有24位是可以用的那它的高八位去哪了实际上是用来做了标记的做控制的eventCLEAR_EVENTS_ON_EXIT_BIT也就是表示事件的一个清除标记位就是说在退出的时候是否要清除如果是退出需要清除的话就需要设置一下它uxControlBits | eventCLEAR_EVENTS_ON_EXIT_BIT;他要等待所有位我也要设置一下它uxControlBits | eventWAIT_FOR_ALL_BITS;表示既要退出的时候清除也要等待所有的标志位然后再将这个变量通过这个函数vTaskPlaceOnUnorderedEventList()处理一下void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) { configASSERT( pxEventList ); /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by the event groups implementation. */ configASSERT( uxSchedulerSuspended ! 0 ); /* Store the item value in the event list item. It is safe to access the event list item here as interrupts wont access the event list item of a task that is not in the Blocked state. */ listSET_LIST_ITEM_VALUE( ( pxCurrentTCB-xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); /* Place the event list item of the TCB at the end of the appropriate event list. It is safe to access the event list here because it is part of an event group implementation - and interrupts dont access event groups directly (instead they access them indirectly by pending function calls to the task level). */ vListInsertEnd( pxEventList, ( pxCurrentTCB-xEventListItem ) ); prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); }这个函数将任务放在无序的事件列表当中istSET_LIST_ITEM_VALUE( ( pxCurrentTCB-xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );设置控制块里面的value。通过这个值在我们需要的时候就知道这个当前的任务控制块它在等待什么事件等待这个事件又有什么特性呢就比如说我要退出要清除这个标志我要等待的是所有事件都要满足就是记录一下他。然后呢就把他插入到事件等待的列表之中同时我们还根据这个事件指定的延时插入到延时列表如果超时了也好把它恢复过来。然后直接返回xReturn 0。这里恢复一下调度如果需要任务切换的话就进行一次任务的切换。在这里已经进行了任务切换portYIELD_WITHIN_API();如果还可以往下执行的话就说明任务已经进行了切换就是说已经回到了这里才能往下进行有两种情况要么它等待超时了已经超时了那就继续往下执行要么就已经发生了一个事件我们在这里重置一下这个事件uxReturn uxTaskResetEventItemValue();TickType_t uxTaskResetEventItemValue( void ) { TickType_t uxReturn; uxReturn listGET_LIST_ITEM_VALUE( ( pxCurrentTCB-xEventListItem ) ); /* Reset the event list item to its normal value - so it can be used with queues and semaphores. */ listSET_LIST_ITEM_VALUE( ( pxCurrentTCB-xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB-uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ return uxReturn; }也就是获取一下事件列表的value值并且将它返回然后再重置一下它将它设置为原本的value。然后呢将这个返回值上这个事件是否发生了如果发生了我就继续执行否则就已经超时了。然后我们看一下这里获取下等待事件的这个位然后判断下当前的这个事件是否与我们需要的这个事件就是说是否满足我们要等待的一个事件之前也看过这个函数prvTestWaitCondition(),看一下他是否满足这个条件如果要满足了这个条件就看一下这个位是否要进行一个退出清除否则就不用管了。并且退出临界段。然后进行一个返回返回的是这个事件标志位uxReturn ~eventEVENT_BITS_CONTROL_BYTES;。最后我们看一下这个过程是怎样的当用户调用这个函数接口时系统首先根据用户指定参数和接收选项来判断他要等待的事件是否发生如果已经发生则根据参数xClearOnExit来决定是否清除事件的响应标志位并且返回事件标志位的值但是这个值并不是一个稳定的值如果事件没有发生则把任务添加到事件等待列表中直到事件发生或等待时间超时。设置事件xEventGroupSetBits()用于置位事件组中指定的位。当位被置位之后阻塞在该位上的任务将会被解锁。EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) { ListItem_t *pxListItem, *pxNext; ListItem_t const *pxListEnd; List_t *pxList; EventBits_t uxBitsToClear 0, uxBitsWaitedFor, uxControlBits; EventGroup_t *pxEventBits ( EventGroup_t * ) xEventGroup; BaseType_t xMatchFound pdFALSE; /* Check the user is not attempting to set the bits used by the kernel itself. */ configASSERT( xEventGroup ); configASSERT( ( uxBitsToSet eventEVENT_BITS_CONTROL_BYTES ) 0 ); pxList ( pxEventBits-xTasksWaitingForBits ); pxListEnd listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ vTaskSuspendAll(); { traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); pxListItem listGET_HEAD_ENTRY( pxList ); /* Set the bits. */ pxEventBits-uxEventBits | uxBitsToSet; /* See if the new bit value should unblock any tasks. */ while( pxListItem ! pxListEnd ) { pxNext listGET_NEXT( pxListItem ); uxBitsWaitedFor listGET_LIST_ITEM_VALUE( pxListItem ); xMatchFound pdFALSE; /* Split the bits waited for from the control bits. */ uxControlBits uxBitsWaitedFor eventEVENT_BITS_CONTROL_BYTES; uxBitsWaitedFor ~eventEVENT_BITS_CONTROL_BYTES; if( ( uxControlBits eventWAIT_FOR_ALL_BITS ) ( EventBits_t ) 0 ) { /* Just looking for single bit being set. */ if( ( uxBitsWaitedFor pxEventBits-uxEventBits ) ! ( EventBits_t ) 0 ) { xMatchFound pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } else if( ( uxBitsWaitedFor pxEventBits-uxEventBits ) uxBitsWaitedFor ) { /* All bits are set. */ xMatchFound pdTRUE; } else { /* Need all bits to be set, but not all the bits were set. */ } if( xMatchFound ! pdFALSE ) { /* The bits match. Should the bits be cleared on exit? */ if( ( uxControlBits eventCLEAR_EVENTS_ON_EXIT_BIT ) ! ( EventBits_t ) 0 ) { uxBitsToClear | uxBitsWaitedFor; } else { mtCOVERAGE_TEST_MARKER(); } /* Store the actual event flag value in the tasks event list item before removing the task from the event list. The eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows that is was unblocked due to its required bits matching, rather than because it timed out. */ ( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits-uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); } /* Move onto the next list item. Note pxListItem-pxNext is not used here as the list item may have been removed from the event list and inserted into the ready/pending reading list. */ pxListItem pxNext; } /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT bit was set in the control word. */ pxEventBits-uxEventBits ~uxBitsToClear; } ( void ) xTaskResumeAll(); return pxEventBits-uxEventBits; }它有两个参数一个是事件句柄一个是指定事件中的事件标志位。前面也是一些断言的操作首先我们先获取下事件控制块的列表我们获取下列表项的首地址NEXT如果pxListItem ! pxListEnd就说明已经有列表存在这里了所以我们要使用while循环来判断下是否有某些任务在等待这个事件Get一下它通过value判断下是否有等待它我们要等待的一个事件因为我们要设置在这里指定设置的位我们要看一下它等待的事件是否满足满足的话就唤醒不满足的话就PASS掉那么我们先获取一下所有等待的这些任务之中等待的事件的一个value之前等待的时候设置的这个value如果能等待到的话就把他设置一下就是把那个标志位设置一下也就是eventEVENT_BITS_CONTROL_BYTES设置一下说明它已经等待到了这个事件那么我们看一下它的所有的执行过程首先我们要分割下他要等待的这个位uxControlBits uxBitsWaitedFor eventEVENT_BITS_CONTROL_BYTES;就是说我要等待这些是控制位是 要等待所有 还是 不是等待所有就是逻辑与还是逻辑或uxControlBit提出它的高8位suxBitsWaitedFor清除了高8位。如果不是等待所有位就看一下有没有被置位如果有一个被置位了就把xMatchFound设置为pdTRUE就说明事件已经发生了后面就会进行一个判断判断是否要进行清除如果要进行清除那直接就是uxBitsToClear | uxBitsWaitedFor;然后就从事件等待的这个列表中移除BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) { TCB_t *pxUnblockedTCB; BaseType_t xReturn; /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by the event flags implementation. */ configASSERT( uxSchedulerSuspended ! pdFALSE ); /* Store the new item value in the event list. */ listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); /* Remove the event list form the event flag. Interrupts do not access event flags. */ pxUnblockedTCB ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem ); configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( pxEventListItem ); /* Remove the task from the delayed list and add it to the ready list. The scheduler is suspended so interrupts will not be accessing the ready lists. */ ( void ) uxListRemove( ( pxUnblockedTCB-xStateListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); if( pxUnblockedTCB-uxPriority pxCurrentTCB-uxPriority ) { /* Return true if the task removed from the event list has a higher priority than the calling task. This allows the calling task to know if it should force a context switch now. */ xReturn pdTRUE; /* Mark that a yield is pending in case the user is not using the xHigherPriorityTaskWoken parameter to an ISR safe FreeRTOS function. */ xYieldPending pdTRUE; } else { xReturn pdFALSE; } return xReturn; }就从事件列表的之中移除添加到就绪列表从这一步操作之后进行一次任务的切换他就会回到我们等待的这里就说明事件已经发生了并且还会设置一个标志位他会把这个标志位taskEVENT_LIST_ITEM_VALUE_IN_USE设置说明他已经等待到了这个事件刚刚已经判断了如果被置位了说明已经等待到了这个事件这里就是将它从时间列表里面移除。移除了之后就是等待到了直接返回就好了恢复调度器。因为这里是等待所有如果不是等待所有那就直接是设置一下就好了等他完成了他就会自动解除刚刚说了这里等待了一个事件3和事件5我等待到了事件3就唤醒了如果没有等待到我还要继续等待事件5才可以唤醒就这样子。这个函数使用的时候一般是将他通过一个宏定义使用比如我要设置某个位一般就是将事件的某个位给他设置一下就可以了#define EVENT (0x01 x) //“x”表示写入事件集合的bit x还有一个FromISR的是它的一个中断保护版本。xEventGroupSetBitsFromISR()是xEventGroupSetBits()的中断版本。其实都是差不多的不过它是基于定时器调用的后面讲定时器的时候会讲解。//在event_groups.c 717行 #if ( ( configUSE_TRACE_FACILITY 1 ) ( INCLUDE_xTimerPendFunctionCall 1 ) ( configUSE_TIMERS 1 ) ) BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) { BaseType_t xReturn; traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); xReturn xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); return xReturn; } #endif首先往定时器的任务发送一个东西告诉定时器来设置他后续讲定时器的时候也会讲的他其实就是发送一个定时器的因为它是在中断之中设置那么我就直接将他转成定时器任务而定时器任务它优先级会很高一般都是最高的那我就往定时器任务发送一个消息或者说是就是让定时器任务来处理这个事件。因为置位事件组的标志位是一个不确定的操作而且阻塞在事件组的标志位上的任务的个数也是不确定的。FreeRTOS不允许不确定的操作在中断和临界段发生的所以xEventGroupSetBitsFromISR()给FreeRTOS的软件定时器服务任务发送一个消息让置位事件组的操作在软件定时器任务里面完成他真正调用的也是xEventGroupSetBits()函数只不过是通过软件定时器。所以它实际上执行的上下文环境依旧是在任务重。有一个中断 和 软件定时器任务 本来是在中断里面调用这个任务但是中断里面发了一个消息给定时器任务请求软件定时器去调用这个函数这个函数就是xEventGroupSetBits()。也就是说本来在中断中要触发的事情我让它在软件定时器中实现就可以了他传入的参数事件句柄一个要传递的位以及返回是否要进行上下文的切换。清除事件//在event_groups.c 499行 #if ( ( configUSE_TRACE_FACILITY 1 ) ( INCLUDE_xTimerPendFunctionCall 1 ) ( configUSE_TIMERS 1 ) ) BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) { BaseType_t xReturn; traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); xReturn xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); return xReturn; } #endif它也是调用一个通过软件定时器处理的。//在event_groups.c 472行 EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) { EventGroup_t *pxEventBits ( EventGroup_t * ) xEventGroup; EventBits_t uxReturn; /* Check the user is not attempting to clear the bits used by the kernel itself. */ configASSERT( xEventGroup ); configASSERT( ( uxBitsToClear eventEVENT_BITS_CONTROL_BYTES ) 0 ); taskENTER_CRITICAL(); { traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); /* The value returned is the event group value prior to the bits being cleared. */ uxReturn pxEventBits-uxEventBits; /* Clear the bits. */ pxEventBits-uxEventBits ~uxBitsToClear; } taskEXIT_CRITICAL(); return uxReturn; }首先进入一个临界段将对应的事件位上就好了进行取反一个清除比较简单。实验详细代码见附件事件标志组实验室在FreeRTOS中创建了两个任务一个是设置事件任务一个是等待事件任务两个任务独立运行设置事件任务通过检测按键的按下情况设置不同的事件标志位等待事件任务则获取这两个事件标志位并且判断两个事件是否都发生如果是则输出相应信息LED进行翻转。等待事件任务的等待时间是port_MAXDELAY一直在等待事件的发生等待到事件之后清除对应的事件标志位。步骤1.定义事件句柄通过宏定义设置事件掩码位2.创建事件--xEventGroupCreate()3.实现等待事件任务--xEventGroupWaitBits()4.实现设置事件任务--xEventGroupSetBits()