FreeRTOS源码理解(九)——事件组和任务通知

FreeRTOS源码理解(一)——FreeRTOS思路梳理

FreeRTOS源码理解(二)——中断和列表项

FreeRTOS源码理解(三)——任务

FreeRTOS源码理解(四)——任务调度器

FreeRTOS源码理解(五)——任务切换

FreeRTOS源码理解(六)——时间管理

FreeRTOS源码理解(七)——队列

FreeRTOS源码理解(八)——信号量

FreeRTOS源码理解(九)——事件组和任务通知

FreeRTOS源码理解(十)——软件定时器和空闲任务

FreeRTOS源码理解(十一)——内存管理

FreeRTOS源码理解(十二)——汇总

事件组

事件是用位图来表示存储的

事件位可以用来表明某个事件是否发生,事件的好处是可以一对多,多对一,一对一

一个任务可以同时等待好几个事件,同样也可以好几个任务等待同一个事件

事件组结构体

typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;
	List_t xTasksWaitingForBits;		/* 等待某个位变为1的任务列表 */

	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;
	#endif

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated; /* 静态方法标志位,手动释放内存 */
	#endif
} EventGroup_t;

创建事件标志组

EventGroupHandle_t xEventGroupCreate( void )
{
    EventGroup_t *pxEventBits;

    /* 申请内存 */
    pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );

    if( pxEventBits != NULL )
    {
        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 ( EventGroupHandle_t ) pxEventBits;
}

申请内存,事件组的标志位和等待事件组列表初始化

清除事件标志位&获取事件标志组的值

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

	taskENTER_CRITICAL();
	{
		traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );

		uxReturn = pxEventBits->uxEventBits;

		/* 清楚位 */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	taskEXIT_CRITICAL();

	return uxReturn;
}

#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )

等待事件组

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
{
    /*  xEventGroup:事件组结构体
    	uxBitsToWaitFor:等待事件位
        xClearOnExit:是否清除事件位,pdTRUE则清除,pdFALSE不清除
        xWaitForAllBits:是否等待所有的位,pdTRUE为事件均发生,pdFALSE表示至少一个事件发生
        xTicksToWait:等待超时时间		*/
    
    EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
    EventBits_t uxReturn, uxControlBits = 0;
    BaseType_t xWaitConditionMet, xAlreadyYielded;
    BaseType_t xTimeoutOccurred = pdFALSE;

	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;

		/* 查看事件是否已满足 */
		xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );

		if( xWaitConditionMet != pdFALSE )
		{
			/* 等待事件已经触发,无需等待 */
			uxReturn = uxCurrentEventBits;
			xTicksToWait = ( TickType_t ) 0;

			/* 退出时是否清除位处理 */
			if( xClearOnExit != pdFALSE )
			{
				pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else if( xTicksToWait == ( TickType_t ) 0 )
		{
			/* 未满足,但是等待阻塞时间为0 */
			uxReturn = uxCurrentEventBits;
		}
		else
		{
			/* 任务要阻塞等待事件触发 */
            
            /* 利用uxControlBits的位存储事件位操作和事件的触发要求 */
			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();
			}

			/*  将TCB中的事件列表项挂在事件组的等待列表下;事件列表项的value值设为uxControlBits
				value值中,低24位(0-23)表示等待的事件位,第24位表示退出是否清除事件位,第26位表示是否等待所有事件位,第27位表示等待事件
				将状态列表项移动到延时等待列表中 */
			vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
			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();
		}

		/* 程序运行到这表示任务切换回来了,即超时了或者等到了事件位 */
        
        /* 重置任务的事件列表项的value值,置为最大优先级-任务优先级 */
		uxReturn = uxTaskResetEventItemValue();

        /* 判断是因为超时还是等到了事件位 */
		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
			taskENTER_CRITICAL();
			{
				/* 超时了 */
				uxReturn = pxEventBits->uxEventBits;

				/* 再判断一下是否满足事件位,事件位可能再任务恢复之间更新 */
				if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
				{
					if( xClearOnExit != pdFALSE )
					{
						pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();

			xTimeoutOccurred = pdFALSE;
		}
		else
		{
			/* 任务唤醒已唤醒,因为满足了事件位 */
		}

		/* 任务被阻塞,因此可能设置了控制位 */
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	}
	traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}

设置事件组标志位

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;

	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );

	pxList = &( pxEventBits->xTasksWaitingForBits );
	pxListEnd = listGET_END_MARKER( pxList ); 
   
	vTaskSuspendAll();	//挂起调度器
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );

		pxListItem = listGET_HEAD_ENTRY( pxList );

		/* 设置事件标志位,将存储事件的变量置位 */
		pxEventBits->uxEventBits |= uxBitsToSet;

		/* 查看是否有因为新的事件位置一而可以取消阻塞的任务 */
		while( pxListItem != pxListEnd )
		{
			pxNext = listGET_NEXT( pxListItem );
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
			xMatchFound = pdFALSE;

			/* 将value值分割,分为等待事件位部分和事件位设置部分 */
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

			if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
			{
				/* 事件设置:事件是否满足任意一个 */
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				/* 事件设置:要求所有事件位均满足 */
				xMatchFound = pdTRUE;
			}
			else
			{
				/* 不满足事件位要求 */
			}

			if( xMatchFound != pdFALSE )
			{
				/* 事件位满足要求 */
                
				if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )	//判断是否需要清除事件位
				{
                    /* 如果需要清除,将需要清除的位记录下来 */
					uxBitsToClear |= uxBitsWaitedFor;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				/* 存储满足要求在任务事件列表项value值的第25位,同时将任务移到就绪列表中,根据优先级判断是否需要任务切换 */
				( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
			}

			/* 检查下一个等待项 */
			pxListItem = pxNext;
		}

		/* 退出时清除位,如果不需要清除或者无满足事件位组,uxBitsToClear为0 */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	( void ) xTaskResumeAll();	//开启调度器

	return pxEventBits->uxEventBits;
}

任务通知

任务通知是一对一或者多对一的,一个任务可以接收任何任务发的通知,也可以向任何任务发送通知;任务通知是个事件

注:任务通知不能为0

发送任务通知:将TCB中的ulNotifiedValue元素按指定方式修改,同时修改TCB中任务通知小黄太,然后将阻塞的任务恢复到就绪列表中

接收任务通知:根据TCB中任务通知的状态和等待时间,将任务移到延时等待列表;如果切换回来,判断是否因为超时,超时返回0,接收到了通知返回接收到的通知值

文档汇总

文档汇总

好的,以下是一个简单的通过事件组实现RTOS多任务看门狗的示例代码,其中有三个任务: ```c #include "stm32f4xx.h" #include "FreeRTOS.h" #include "task.h" #include "event_groups.h" #define EVENT_ALL_BITS 0x1F TaskHandle_t xTask1Handle = NULL; TaskHandle_t xTask2Handle = NULL; TaskHandle_t xTask3Handle = NULL; EventGroupHandle_t xEventGroup; void vTask1(void *pvParameters) { while (1) { //执行任务1的操作 xEventGroupSetBits(xEventGroup, 0x01); //任务1执行完毕,设置事件组的第一位 vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1秒 } } void vTask2(void *pvParameters) { while (1) { //执行任务2的操作 xEventGroupSetBits(xEventGroup, 0x02); //任务2执行完毕,设置事件组的第二位 vTaskDelay(2000 / portTICK_PERIOD_MS); //延时2秒 } } void vTask3(void *pvParameters) { while (1) { //等待事件组的所有位都被设置,表示所有任务都执行完毕 EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, EVENT_ALL_BITS, pdTRUE, pdTRUE, portMAX_DELAY); if ((uxBits & 0x1F) == 0x1F) //如果事件组的所有位都被设置 { //执行任务3的操作 xEventGroupClearBits(xEventGroup, 0x1F); //清除事件组的所有位 } } } int main(void) { xEventGroup = xEventGroupCreate(); //创建事件组 xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle); //创建任务1 xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle); //创建任务2 xTaskCreate(vTask3, "Task3", configMINIMAL_STACK_SIZE, NULL, 3, &xTask3Handle); //创建任务3 vTaskStartScheduler(); //启动RTOS调度器 while (1) { } } ``` 在上述代码中,通过事件组来实现多任务看门狗。任务1和任务2在执行完毕后,会设置事件组的第一位和第二位。任务3会等待事件组的所有位都被设置,表示所有任务都执行完毕后再执行自己的操作。如果任意一个任务阻塞了事件过长时间,不会误触发看门狗复位,因为任务3只有在所有任务都执行完毕后才会执行。 需要注意的是,在实际应用中需要根据实际情况进行调整,如设置任务的优先级、延时时间等。此外,如果需要使用硬件看门狗,还需要在代码中添加相应的驱动程序和配置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值