10.FreeRTOS任务通知的简易分析

FreeRTOS任务通知

  • 架构:Cortex-M3
  • 版本:FreeRTOS V9.0.0
  • 前言:任务通知的出现,是为了代替有些场景队列和信号量的使用,为什么要替换?为什么可以替换?接下来是分析任务通知有什么优势。

1.任务通知的创建

任务通知并没有专门的函数创建,而是在每个任务创建的时候,任务通知就已经创建了

	#if ( configUSE_TASK_NOTIFICATIONS == 1 )
	{
		pxNewTCB->ulNotifiedValue = 0;
		pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	#endif

​ 任务通知有两个属性,一个是通知值被初始化为0,另一个是通知状态被初始化为NOT_WAITING_NOTIFICATION

​ 可以看到,任务通知只用了两个四字节的变量就实现了,而前面分析过的队列和信号量,初始化过程不仅相对复杂,并且占用的内存也相较更大。

2.任务通知的发送

发送任务一共有三种方式:

  1. xTaskNotifyGive( xTaskToNotify ) 这个发送通知不会对ulNotifiedValue操作
  2. xTaskNotify( xTaskToNotify, ulValue, eAction ) 这个发送通知会根据eAction来对ulNotifiedValue进行自加、按位或、覆盖的操作。
  3. xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) 同上,但是多了一步,可以查询ulNotifiedValue的值,ulNotifiedValue被放在pulPreviousNotifyValue 中

其实以上三个函数都是宏定义,真正被调用的是xTaskGenericNotify

具体分析xTaskGenericNotify

BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
	{
	TCB_t * pxTCB;
	BaseType_t xReturn = pdPASS;
	uint8_t ucOriginalNotifyState;

		configASSERT( xTaskToNotify );
		pxTCB = ( TCB_t * ) xTaskToNotify;

		taskENTER_CRITICAL();
		{
			if( pulPreviousNotificationValue != NULL )
			{
				*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
			}

			ucOriginalNotifyState = pxTCB->ucNotifyState;

			pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

			switch( eAction )
			{
				case eSetBits	:
					pxTCB->ulNotifiedValue |= ulValue;
					break;

				case eIncrement	:
					( pxTCB->ulNotifiedValue )++;
					break;

				case eSetValueWithOverwrite	:
					pxTCB->ulNotifiedValue = ulValue;
					break;

				case eSetValueWithoutOverwrite :
					if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
					{
						pxTCB->ulNotifiedValue = ulValue;
					}
					else
					{
						/* The value could not be written to the task. */
						xReturn = pdFAIL;
					}
					break;

				case eNoAction:
					/* The task is being notified without its notify value being
					updated. */
					break;
			}

			traceTASK_NOTIFY();

			/* If the task is in the blocked state specifically to wait for a
			notification then unblock it now. */
			if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
			{
				( void ) uxListRemove( &( pxTCB->xStateListItem ) );
				prvAddTaskToReadyList( pxTCB );

				/* The task should not have been on an event list. */
				configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

				#if( configUSE_TICKLESS_IDLE != 0 )
				{
					/* If a task is blocked waiting for a notification 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 will automatically get 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

				if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
				{
					/* The notified task has a priority above the currently
					executing task so a yield is required. */
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

​ 首先查看是否需要返回ulNotifiedValue,要的话就赋值到pulPreviousNotificationValue中。然后是保存ucNotifyState,然后根据eActionulNotifiedValue进行操作。检查被发送任务通知的任务是否因为等待任务通知而阻塞,如果是就挂载到等待链表中,如果被发送任务通知的任务优先级大于当前任务,则切换到被发送任务通知的任务

3.任务通知的接收

等待任务通知有两个函数:

3.1 uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ),第一个参数是要不是把ulNotifiedValue清0,第二个参数是阻塞的时间

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
	{
	uint32_t ulReturn;

		taskENTER_CRITICAL();
		{
			/* Only block if the notification count is not already non-zero. */
			if( pxCurrentTCB->ulNotifiedValue == 0UL )
			{
				/* Mark this task as waiting for a notification. */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

				if( xTicksToWait > ( TickType_t ) 0 )
				{
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_TAKE_BLOCK();

					/* All ports are written to allow a yield in a critical
					section (some will yield immediately, others wait until the
					critical section exits) - but it is not something that
					application code should ever do. */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();

		taskENTER_CRITICAL();
		{
			traceTASK_NOTIFY_TAKE();
			ulReturn = pxCurrentTCB->ulNotifiedValue;

			if( ulReturn != 0UL )
			{
				if( xClearCountOnExit != pdFALSE )
				{
					pxCurrentTCB->ulNotifiedValue = 0UL;
				}
				else
				{
					pxCurrentTCB->ulNotifiedValue = ulReturn - 1;
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		taskEXIT_CRITICAL();

		return ulReturn;
	}

如果当前没有收到通知(ulNotifiedValue为0),并且阻塞时间大于0,那么就将当前任务插入到DelayedList中,然后切换其它任务。如果此时收到通知(ulNotifiedValue不为0),根据xClearCountOnExit的值,ulNotifiedValue要么清零,要么减1。

3.2 BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) 第一个参数是在进入该函数的时候,要把ulNotifiedValue初始化为某个值,第二个参数是在函数结束的时候要把ulNotifiedValue赋为什么值。第三个参数是返回的是结束时ulNotifiedValue,第四个参数是阻塞的时间。

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
	{
	BaseType_t xReturn;

		taskENTER_CRITICAL();
		{
			/* Only block if a notification is not already pending. */
			if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
			{
				/* Clear bits in the task's notification value as bits may get
				set	by the notifying task or interrupt.  This can be used to
				clear the value to zero. */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;

				/* Mark this task as waiting for a notification. */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

				if( xTicksToWait > ( TickType_t ) 0 )
				{
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_WAIT_BLOCK();

					/* All ports are written to allow a yield in a critical
					section (some will yield immediately, others wait until the
					critical section exits) - but it is not something that
					application code should ever do. */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();

		taskENTER_CRITICAL();
		{
			traceTASK_NOTIFY_WAIT();

			if( pulNotificationValue != NULL )
			{
				/* Output the current notification value, which may or may not
				have changed. */
				*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
			}

			/* If ucNotifyValue is set then either the task never entered the
			blocked state (because a notification was already pending) or the
			task unblocked because of a notification.  Otherwise the task
			unblocked because of a timeout. */
			if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION )
			{
				/* A notification was not received. */
				xReturn = pdFALSE;
			}
			else
			{
				/* A notification was already pending or a notification was
				received while the task was waiting. */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
				xReturn = pdTRUE;
			}

			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

如果当前没有收到通知,那么就将ulNotifiedValue&取反ulBitsToClearOnEntry,如果设置的阻塞时间大于0,就把当前任务插入到DelayedList中。如果收到了通知,则把ulNotifiedValue&取反ulBitsToClearOnExit

原文地址:https://www.cnblogs.com/r1chie/p/14171181.html