1. 程式人生 > >freertos之tasknotify淺析

freertos之tasknotify淺析

前言

freertos在V8.0.0版本之後就加入了task notify 的功能,據說比訊號量 佇列傳遞訊息等更快更高效且佔系統資源更少,在每個TCB結構體中多佔用5個位元組空間而已,相對於queue實現的訊號量等來說著實輕便了很多;但是有一個很大的問題,RTOS通知任務才可使用只有一個任務可以被該接收者的事件,雖然這種情況在大多數真實應用中。

可以通過一些特定引數設定來實現二值訊號量,二值訊號郵箱等操作。

程式碼實現

通知接收

task通過呼叫ulTaskNotifyTakexTaskNotifyWait來等待任務通知,ulTaskNotifyTake的返回值是收到的任務通知值;xTaskNotifyWait的返回值是是否收到任務通知。
這兩個函式的用處還是有點不一樣的,注意從程式碼上去區分。

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

	taskENTER_CRITICAL();
	{
		/*只有當通知計數不是非零時才阻塞。只有當通知計數 是 零時才阻塞。*/
		if( pxCurrentTCB->ulNotifiedValue == 0UL ){
			/* 標註這個task正在等待通知*/
			pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
			if( xTicksToWait > ( TickType_t ) 0 )
			{//等待tick數大於0的話,需要加入 pxDelayedTaskList中去
				prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
				/* 執行可移植 導致任務切換的函式*/
				portYIELD_WITHIN_API();
			}
		}
	}
	taskEXIT_CRITICAL();//真正的·1任務切換髮生在這裡

	taskENTER_CRITICAL();
	{
		ulReturn = pxCurrentTCB->ulNotifiedValue;
		/*此處應該是已經返回,有兩種情況
		1.有task2將通知傳送給本任務了,然後加入到readylists被執行了
		2.xTicksToWait過完了*/
		if( ulReturn != 0UL ){
			if( xClearCountOnExit != pdFALSE ){//如果需要清空任務通知值的話
				pxCurrentTCB->ulNotifiedValue = 0UL;
			}
		}
		/*修改任務通知狀態 為 不需要等待 任務通知*/
		pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	taskEXIT_CRITICAL();
	return ulReturn;
}


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

	taskENTER_CRITICAL();{
		/* 只有在通知尚未掛起時才阻塞。*/
		if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ){
			/*可能在傳送的通知的 task或中斷 中 ,清除任務通知的相應的bit
			這可以用來清除 該位 為 0。*/
			pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;
			/*標註這個task正在等待通知*/
			pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
			if( xTicksToWait > ( TickType_t ) 0 ){
				prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
				/* 執行可移植 導致任務切換的函式*/
				portYIELD_WITHIN_API();
			}
		}
	}
	taskEXIT_CRITICAL();

	taskENTER_CRITICAL();{
		if( pulNotificationValue != NULL ){
			/* 輸出當前通知值,該值可以更改,也可以不更改。*/
			*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
		}

		/* 如果設定了ucNotifyValue,那麼要麼任務從未輸入阻塞狀態
		(因為通知已經掛起)或任務因通知解除阻塞。
		否則,任務由於超時而解除阻塞*/
		if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ){
			/* 沒有接收到任務通知 */
			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;
}

通知傳送

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

	taskENTER_CRITICAL();
	{/*pulPreviousNotificationValue 和 ucOriginalNotifyState 儲存原來任務通知值和通知狀態*/
		if( pulPreviousNotificationValue != NULL ){
			*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
		}
		ucOriginalNotifyState = pxTCB->ucNotifyState;
		pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;
		switch( eAction )
		{/*根據設定的任務通知操作來 操作 被通知任務的通知值*/
			case eSetBits	://設定相應的位
				pxTCB->ulNotifiedValue |= ulValue;
				break;

			case eIncrement	://僅僅給通知值+1
				( pxTCB->ulNotifiedValue )++;
				break;

			case eSetValueWithOverwrite	://不管原來任務通知值有沒有,直接覆蓋
				pxTCB->ulNotifiedValue = ulValue;
				break;

			case eSetValueWithoutOverwrite :
				//如果原來任務已經接收到了通知值,那麼不操作返回通知失敗,否則就通知,即不覆蓋式操作
				if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){
					pxTCB->ulNotifiedValue = ulValue;
				}else{
					/* 通知值 通知不到 應該被通知的task */
					xReturn = pdFAIL;
				}
				break;

			case eNoAction://不操作,僅僅告訴被通知任務 有任務通知了 而已
				break;
		}

		/* 如果任務處於阻塞狀態,特別是等待通知,那麼現在就解除阻塞。
		就是常規列表操作*/
		if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
		{
			( void ) uxListRemove( &( pxTCB->xStateListItem ) );
			prvAddTaskToReadyList( pxTCB );
			if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ){
			/*被通知任務的優先順序高於當前執行任務,因此需要任務切換。*/
				taskYIELD_IF_USING_PREEMPTION();
			}
		}
	}
	taskEXIT_CRITICAL();
	return xReturn;
}

由於中斷不能有延時或者休眠操作,所以等待通知是不能在中斷中的,而freertos提供了中斷中的傳送任務通知的函式,其操作跟普通notify很相似,之後再分析所有系統api在中斷中的版本。

實現二值訊號量

實現計數訊號量

實現訊息郵箱

實現事件組