系统延时函数应用以及延时函数FreeRTOS源码分析

系统延时API详解

vTaskDelay()

vTaskDelayUntil()

xTaskGetTickCount()

相对延时与绝对延时的区别

系统延时函数实现原理

vTaskDelay

	void vTaskDelay( const TickType_t xTicksToDelay )
	{
	//xAlreadyYielded:已经调度的状态
	//初始赋值为0 
	BaseType_t xAlreadyYielded = pdFALSE;

		/* 延时周期是否大于0,不大于0,就不应该调度 */
		if( xTicksToDelay > ( TickType_t ) 0U )
		{
			configASSERT( uxSchedulerSuspended == 0 );
			//1、挂起调度器
			vTaskSuspendAll();
			{
				traceTASK_DELAY();

				/* 1、添加任务到延时列表中
				   2、需要传入两个参数
					2.1、xTicksToDelay:延时周期
					2.2、pdFALSE:状态值为0


				*/
				prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
			}
			//恢复调度器,这个调度器是有返回值的,这返回值,表示在恢复调度器
			的时候,是否已经进行了任务切换
			xAlreadyYielded = xTaskResumeAll();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* xAlreadyYielded 等于FALSE,代表在恢复调度器的时候,没有进行任务切换 */
		if( xAlreadyYielded == pdFALSE )
		{
			//调用了任务切换:内部就是触发PendSV异常
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
//添加任务到延时列表中
//传入两个参数
//xTicksToWait:延时周期
//xCanBlockIndefinitely :延时的确定状态
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
//延时周期----下次唤醒的时间
TickType_t xTimeToWake;
//xTickCount为系统节拍值  全局的,进行一次获取
const TickType_t xConstTickCount = xTickCount;


	/* 把当前任务从就绪列表中移除 */
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/* The current task must be in a ready list, so there is no need to
		check, and the port reset macro can be called directly. */
		portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
	//是否使用了任务挂起的功能
	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		//portMAX_DELAY  = 0XFFFFFFFF 表示延时是一直持续的,也就是让任务一直阻塞
		if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			/* 把任务添加到,挂起列表中去*/
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			/* 先去计算,下次唤醒的tick值 */
			xTimeToWake = xConstTickCount + xTicksToWait;

			/* 每个任务控制块里,状态列表都有一个延时值:value
				这个value就是任务延时周期,在systick里面,进行比较,是否到达
			*/
			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
			//判断下次延时周期,是否小于系统节拍值,那就证明定时已经溢出
			if( xTimeToWake < xConstTickCount )
			{
				/* 溢出,就把任务,添加到延时溢出列表里 */
				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
			}
			else
			{
				/* 没有溢出,把任务添加到延时列表中,让内核进行处理 */
				vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

				/*还要去更新系统时间片,因为系统时间片永远保存最小的延时周期 */
				if( xTimeToWake < xNextTaskUnblockTime )
				{
					xNextTaskUnblockTime = xTimeToWake;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
	}
	#else /* INCLUDE_vTaskSuspend */
	{
		/* 计算下次唤醒的系统节拍值 */
		xTimeToWake = xConstTickCount + xTicksToWait;

		/* 赋值到任务控制块里 */
		listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

		if( xTimeToWake < xConstTickCount )
		{
			/* 溢出,添加到延时溢出列表中 */
			vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			/* 没有溢出,添加到延时列表中 */
			vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

			/*更新时间片 */
			if( xTimeToWake < xNextTaskUnblockTime )
			{
				xNextTaskUnblockTime = xTimeToWake;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}

		/* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */
		( void ) xCanBlockIndefinitely;
	}
	#endif /* INCLUDE_vTaskSuspend */
}

vTaskDelayUntil

	void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
	{
	//下次任务要唤醒的系统节拍值
	TickType_t xTimeToWake;
	//xAlreadyYielded:表示是否已经进行了任务切换
	//xShouldDelay:表示是否需要进行延时处理
	BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

		//挂起调度器
		vTaskSuspendAll();
		{
			/* 获取系统节拍值 */
			const TickType_t xConstTickCount = xTickCount;

			/* 获取任务下次唤醒的系统节拍值 */
			xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
			//pxPreviousWakeTime指向上一次保存的任务的唤醒节拍值
			//这个时候如果大于当前系统节拍值,无非两种可能
			//	1、延时周期已经到达了
				2、整个tick计数值,已经溢出了
			if( xConstTickCount < *pxPreviousWakeTime )
			{
				/*1、下次要唤醒的系统节拍值小于上次要唤醒节拍值 ----- 这个就表示:系统节拍值计数溢出了
				  2、下次要唤醒的系统节拍值大于当前的系统节拍值 ----- 表示需要延时
				*/
				if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
				{
					//标记需要延时
					xShouldDelay = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/* 
					1、下次要唤醒的系统节拍值小于上次唤醒的系统节拍值 --- 证明系统节拍值溢出,需要进行延时
					2、下次要唤醒的系统节拍值大于当前系统节拍值---------证明延时周期,在当前的时间之后
				*/
				if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
				{
					//标记延时状态
					xShouldDelay = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}

			/* 保存下次唤醒的节拍值,所以传入pxPreviousWakeTime是指针类型 */
			*pxPreviousWakeTime = xTimeToWake;
			//判断是否需要延时
			if( xShouldDelay != pdFALSE )
			{
				traceTASK_DELAY_UNTIL( xTimeToWake );

				/* 添加任务到延时列表中去 */
				prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		//调度器恢复,如果调度器内部已经进行了任务切换,返回一个true
		xAlreadyYielded = xTaskResumeAll();

		/* 如果 调度器没有进行任务切换,那么要进行任务切换*/
		if( xAlreadyYielded == pdFALSE )
		{
			//进行PendSV异常触发
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

vTaskSuspendAll

void vTaskSuspendAll( void )
{
	/* 调取记录值++
		这个值是去让systick 中断产生了时候,不去遍历阻塞列表,进行任务恢复
	
	*/
	++uxSchedulerSuspended;
}

xTaskResumeAll

BaseType_t xTaskResumeAll( void )
{

TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;


	/* 进入临界段 */
	taskENTER_CRITICAL();
	{
		//调度器记录值减一
		--uxSchedulerSuspended;
		//如果调度器需要恢复了
		if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
		{
			//判断当前任务数量大于0
			if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
			{
				/* 从挂起的就绪列表中遍历 */
				while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
				{
					//获取任务控制块
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
					//移除 挂起就绪列表,移除事件列表
					( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
					//添加到就绪列表中
					prvAddTaskToReadyList( pxTCB );

					/* 如果优先级大于当前任务优先级,则进行任务切换 */
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
					{
						xYieldPending = pdTRUE;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				//获取到任务控制块不为空
				if( pxTCB != NULL )
				{
					/* 需要更新系统的时间片 */
					prvResetNextTaskUnblockTime();
				}

				/* 获取 在调度器挂起时,systick挂起记录值 */
				{
					UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
					//如果记录值大于0
					if( uxPendedCounts > ( UBaseType_t ) 0U )
					{
						do
						{
							//进行systick调度处理,其实就遍历阻塞列表,如果需要任务切换,返回true
							if( xTaskIncrementTick() != pdFALSE )
							{
								//标记任务需要切换
								xYieldPending = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
							--uxPendedCounts;
							//一直遍历,直到uxPendedCounts = 0
						} while( uxPendedCounts > ( UBaseType_t ) 0U );
						//赋值为0
						uxPendedTicks = 0;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				//如果需要进行任务切换
				if( xYieldPending != pdFALSE )
				{
					//判断是否内核是抢占式
					#if( configUSE_PREEMPTION != 0 )
					{
						//标记已经调度的状态
						xAlreadyYielded = pdTRUE;
					}
					#endif
						//进行调度
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	//退出临界段
	taskEXIT_CRITICAL();
	//返回调度的状态值
	return xAlreadyYielded;
}

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值