zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    ios录音
    ios 音乐播放
    ios程序播放音频文件
    ios摇一摇截屏代码
    加速计
    蓝牙4.0实现及原理
    物理仿真
    iOS 监听耳机变化
    删除缓存
    宏定义加参数
  • 原文地址:https://www.cnblogs.com/r1chie/p/14171181.html
Copyright © 2011-2022 走看看