FreeRTOS 第十章 事件

信号量可以用来完成同步,但是使用信号量来同步的话任务只能与单个事件或者任务进行同步。有事件某个任务可能会需要与多个事件或者任务同步,此时信号量就无法满足要求了。FreeRTOS提供了一个解决方案,就是事件标记组。

事件位:

事件位用来表明某个事件是否发送,事件位通常用作事件标记,比如:

1、当收到一条消息并且把这条消息处理掉以后就可以将某个位置1,当队列中没有消息需要处理的时候就可以将这个位置0.

2、当把队列中的消息通过网络发送输出以后就可以将某个位置1,没有数据发送时将位置0.

3、现在需要向网络中发送一个心跳信息,将位置1,不需要发送置0.

事件组:

一个事件组就是一组的事件位,事件组中的事件通过编号来访问。

1、事件标记组的bit表示队列中的消息是否处理掉。

2、事件标记组的bit1表示是否有消息需要从网络发送出去。

3、事件标记组的bit2表示现在是否需要向网络发送心跳消息。

数据类型:

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

    #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;
#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
    typedef uint16_t     TickType_t;
    #define portMAX_DELAY              ( TickType_t ) 0xffff
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
    typedef uint32_t     TickType_t;
    #define portMAX_DELAY              ( TickType_t ) 0xffffffffUL

uxEventBits就是一个16或者32位的整型。

xEventGroupCreate()

    EventGroupHandle_t xEventGroupCreate( void )
    {
        EventGroup_t * pxEventBits;

        traceENTER_xEventGroupCreate();

        //申请内存
        pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */

        if( pxEventBits != NULL )
        {
            //初始为0
            pxEventBits->uxEventBits = 0;
            //xTasksWaitingForBits是一个list。也就是等待这个事件的所有task都挂在这个list下。
            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(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */
        }

        traceRETURN_xEventGroupCreate( pxEventBits );

        return pxEventBits;
    }

xEventGroupSetBits

设置事件位。

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet )
{
    ListItem_t * pxListItem;
    ListItem_t * pxNext;
    ListItem_t const * pxListEnd;
    List_t const * pxList;
    EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
    EventGroup_t * pxEventBits = xEventGroup;
    BaseType_t xMatchFound = pdFALSE;

    traceENTER_xEventGroupSetBits( xEventGroup, uxBitsToSet );

    /* Check the user is not attempting to set the bits used by the kernel
     * itself. */
    configASSERT( xEventGroup );
    configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
    //获取plist
    pxList = &( pxEventBits->xTasksWaitingForBits );
    //获取pllist的尾部
    pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 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. */
        //循环遍历list里的每个列表项,因为列表项就是task,查看哪个task在等待标志位。
        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 )
            {
                //等待所有的标志位,一定是match
                /* All bits are set. */
                xMatchFound = pdTRUE;
            }
            else
            {
                /* Need all bits to be set, but not all the bits were set. */
            }

            if( xMatchFound != pdFALSE )
            {    
                //根据标记决定是否请标记。这里的意思应该就是只有一个task可以接受事件,还是可以多个task接受事件。
                /* 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 task's 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. */
                //match成功后,就需要将task从list里移除。
                vTaskRemoveFromUnorderedEventList( 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. */
        //根据清掉标记。如果有一个任务设置了清标记位,那么都可以使任务不会再次获取事件标志位。
        //见下列demo        
pxEventBits->uxEventBits &= ~uxBitsToClear;
    }
    ( void ) xTaskResumeAll();

    traceRETURN_xEventGroupSetBits( pxEventBits->uxEventBits );

    return pxEventBits->uxEventBits;
}
void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem,
                                        const TickType_t xItemValue )
{
    TCB_t * pxUnblockedTCB;

    traceENTER_vTaskRemoveFromUnorderedEventList( pxEventListItem, xItemValue );

    /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED.  It is used by
     * the event flags implementation. */
    configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U );

    /* 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 = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
    configASSERT( pxUnblockedTCB );
    listREMOVE_ITEM( pxEventListItem );

    #if ( configUSE_TICKLESS_IDLE != 0 )
    {
        /* If a task is blocked on a kernel object then xNextTaskUnblockTime
         * might be set to the blocked task's time out time.  If the task is
         * unblocked for a reason other than a timeout xNextTaskUnblockTime is
         * normally left unchanged, because it is automatically reset to a new
         * value when the tick count equals xNextTaskUnblockTime.  However if
         * tickless idling is used it might be more important to enter sleep mode
         * at the earliest possible time - so reset xNextTaskUnblockTime here to
         * ensure it is updated at the earliest possible time. */
        prvResetNextTaskUnblockTime();
    }
    #endif

    /* 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. */
    listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) );
    prvAddTaskToReadyList( pxUnblockedTCB );

    #if ( configNUMBER_OF_CORES == 1 )
    {
        if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
        {
            /* The unblocked task has a priority above that of the calling task, so
             * a context switch is required.  This function is called with the
             * scheduler suspended so xYieldPending is set so the context switch
             * occurs immediately that the scheduler is resumed (unsuspended). */
            xYieldPendings[ 0 ] = pdTRUE;
        }
    }
    #else /* #if ( configNUMBER_OF_CORES == 1 ) */
    {
        #if ( configUSE_PREEMPTION == 1 )
        {
            taskENTER_CRITICAL();
            {
                prvYieldForTask( pxUnblockedTCB );
            }
            taskEXIT_CRITICAL();
        }
        #endif
    }
    #endif /* #if ( configNUMBER_OF_CORES == 1 ) */

    traceRETURN_vTaskRemoveFromUnorderedEventList();
}

将任务由阻塞列表添加到就绪列表。

这个函数会判断,如果阻塞的任务优先级比当前task高,那么就需要进行一次任务切换。

xEventGroupClearBits

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
                                  const EventBits_t uxBitsToClear )
{
    EventGroup_t * pxEventBits = xEventGroup;
    EventBits_t uxReturn;

    traceENTER_xEventGroupClearBits( xEventGroup, uxBitsToClear );

    /* 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();

    traceRETURN_xEventGroupClearBits( uxReturn );

    return uxReturn;
}

清标志位就是简单的运算,没有task相关操作。

xEventGroupWaitBits

某个任务可能要与多个事件进行同步,那么这个任务就需要等待并判断多个时间位。

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait )

xEventGroup:

指定要等待的事件标志组。
uxBitsToWaitFord:

指定要等待的事件位,比如要等待bit0 和(或)bit2的时候此参数就是 0X05,如果要等待 bit0 和(或)bit1 和(或)bit2 的时候此参数就是 0X07,以此类推。此参数要是为pdTRUE的话,那么在退出此函数之前由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置位pFALSE 的话这些事件位就不会改变。
xClearOnExit:
此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1,或者指定的阻塞时间到的时候函数 xEventGroupWaitBitsO)才会返回。当此函数为 pdFALSE 的话,只要 uxBitsToWaitFor 所设置的这些事件位其中的任意一个置1,或者指定的阻塞时间到的话函数xEventGroupWaitBitsO)就会返回。
xWaitForAllBits:
xTicksToWait:
设置阻塞时间,单位为节拍数。

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait )
{
    EventGroup_t * pxEventBits = xEventGroup;
    EventBits_t uxReturn, uxControlBits = 0;
    BaseType_t xWaitConditionMet, xAlreadyYielded;
    BaseType_t xTimeoutOccurred = pdFALSE;

    traceENTER_xEventGroupWaitBits( xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait );

    /* 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 ) //如果匹配成功了。不需要block
        {
            /* 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;
            xTimeoutOccurred = pdTRUE;
        }
        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. */
            //否则进入阻塞等待中
            //如果设置了clear,那么设置标志(setbits)唤醒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
             * task's event list item so the kernel knows when a match is
             * found.  Then enter the blocked state. */
            //将task添加到delay list里
            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 )
        {
            taskYIELD_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. */
        //走到这里说明task已经超时或者事件被置位了。因为task已经运行了。
        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. */
                //有可能task在再次运行时事件被置位了,因此再次判断一次。
                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. */
            //task因为获取了事件标志位而得到运行
        }

        /* 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;

    traceRETURN_xEventGroupWaitBits( uxReturn );

    return uxReturn;
}

demo;

static void dds_event_send(void* pvParameters)
{
    /* Prevent compiler warning about unused parameter in the case that
    configASSERT() is not defined. */
    (void)pvParameters;

    int idx = 0;
    while (1)
    {
        idx++;
        xEventGroupSetBits(xEventGroup, 0xff);
          printf("dds task send sem %d\r\n", idx);

        vTaskDelay(1000);
    }
}

static void dds_event_recv(void* pvParameters)
{
    /* Prevent compiler warning about unused parameter in the case that
    configASSERT() is not defined. */
    (void)pvParameters;
    int idx = 0;
    while (1)
    {
        xEventGroupWaitBits(xEventGroup, 0xff, pdTRUE, pdFALSE, 10000);
        //   vTaskDelay(100);
        printf("dds task recv sem %d\r\n", idx);
    }
}

static void dds_event_recv1(void* pvParameters)
{
    /* Prevent compiler warning about unused parameter in the case that
    configASSERT() is not defined. */
    (void)pvParameters;
    int idx = 0;
    while (1)
    {
        xEventGroupWaitBits(xEventGroup, 0xff, pdFALSE, pdFALSE, 10000);
        //   vTaskDelay(100);
        printf("dds task recv1 sem %d\r\n", idx);
    }
}

一个发送,两个接受,两个接收者,启动一个设置了clear。可以发现,两个task不会一直循环执行。所以一般都设置clear标记位。注意:清标记位不会导致其他任务获取不到事件,因为清之前已经while循环处理所有任务了。

和信号量不同,如果是1对多,那么只能有一个任务获取信号量。因为信号量的用途是用于同步的。从这里看事件的方式更智能。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王涛的专栏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值