freertos之tasknotify浅析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33894122/article/details/85337189

前言

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在中断中的版本。

实现二值信号量

实现计数信号量

实现消息邮箱

实现事件组

猜你喜欢

转载自blog.csdn.net/qq_33894122/article/details/85337189