第十二章 FreeRTOS的时间管理

目录

一.延时函数的介绍

二.相对延时的源码解读

2.1 prvAddCurrentTaskToDelayedList()-解析

2.2 滴答定时器中断函数中的 xTaskIncrementTick() - 解析

2.3 taskSWITCH_DELAYED_LISTS() - 解析


一.延时函数的介绍

函数描述
vTaskDelay()相对延时
vTaskDelayUntil()绝对延时

相对延时:

指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束(任务被阻塞的时间,到调用此函数开始的时间)


绝对延时

指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务(整个任务执行的时间,从头到尾的时间)


二.相对延时的源码解读

vTaskDelay()函数的源码 + 中文注释:

void vTaskDelay( const TickType_t xTicksToDelay )
{
	BaseType_t xAlreadyYielded = pdFALSE;

	/*入口参数必须大于0,延时时间才有效。*/
	if( xTicksToDelay > ( TickType_t ) 0U )
	{
		configASSERT( uxSchedulerSuspended == 0 );
			
		vTaskSuspendAll();		//挂起任务调度器
		{
			traceTASK_DELAY();	//一个函数接口,未实现

			/*将当前正在执行的任务移到阻塞列表。*/
			prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
		}
			
		xAlreadyYielded = xTaskResumeAll();	//恢复任务调度器。
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/*判断xAlreadyYielded 是否需要进行任务切换。*/
	if( xAlreadyYielded == pdFALSE )
	{
		portYIELD_WITHIN_API();
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

此函数是将任务挂载到阻塞列表解除是在滴答定时器的中断服务函数xPortSysTickHandler()


2.1 prvAddCurrentTaskToDelayedList()-解析

/*
  函数有两个入口参数一个是延时时间xTicksToWait,
  另一个是xCanBlockIndefinitely 等于pdFALSE。
*/
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, 
                                            const BaseType_t xCanBlockIndefinitely ) 

 prvAddCurrentTaskToDelayedList()函数的源码 + 中文注释:

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
	TickType_t xTimeToWake;
	/*xConstTickCount存储时钟节拍,滴答定时器中断一次,变量xTickCount加1*/
	const TickType_t xConstTickCount = xTickCount;

	/*宏INCLUDE_xTaskAbortDelay判断是否是中断延时,这里并没有使用,所以不用管。*/
	#if( INCLUDE_xTaskAbortDelay == 1 )
	{
		pxCurrentTCB->ucDelayAborted = pdFALSE;
	}
	#endif

	/*使用函数uxListRemove()将正在执行的任务的状态列表项从就绪列表中移除*/
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/*
			移除完判断是否有同等优先级的任务,没有就代表只有这一个任务,被移除掉后就绪列表中剩余任务为0,
			那么将此优先级的任务优先级复位。
		*/
		portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/*宏INCLUDE_vTaskSuspend 判断是否使能挂起*/
	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		/*判断延时时间xTicksToWait等于最大延时时间并且xCanBlockIndefinitely 不等于pdFALSE*/
		if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			/*此时将任务挂载到挂起列表中*/
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			/*
				首先记录时间,xConstTickCount 为进入函数prvAddCurrentTaskToDelayedList()时记录的时间,
				加上延时时间xTicksToWait,就是任务到截止阻塞时间该被恢复的时间
			*/
			xTimeToWake = xConstTickCount + xTicksToWait;

			
			/*
				通过函数listSET_LIST_ITEM_VALUE将延时时间写入到列表项值里,
				此值将用作挂载到阻塞列表时根据此值进行升序排列;
			*/
			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

			/*
				判断需要等待截止的时间是否小于进入函数prvAddCurrentTaskToDelayedList()时记录的时间,
				这里判断是否数值溢出,如果溢出就将任务挂载到溢出阻塞列表中,否则挂载到阻塞列表中。
			*/
			if( xTimeToWake < xConstTickCount )
			{
				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
			}
			else
			{
				vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

				/*判断下一个阻塞超时时间如果大于新的阻塞时间,那么将新的阻塞时间更新为下一个阻塞超时时间。*/
				if( xTimeToWake < xNextTaskUnblockTime )
				{
					xNextTaskUnblockTime = xTimeToWake;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
	}
  .........
 以下代码省略
}

2.2 滴答定时器中断函数中的 xTaskIncrementTick() - 解析

 xTaskIncrementTick()函数的源码 + 中文注释:

BaseType_t xTaskIncrementTick( void )
{
	TCB_t * pxTCB;
	TickType_t xItemValue;
	BaseType_t xSwitchRequired = pdFALSE;

	traceTASK_INCREMENT_TICK( xTickCount );
	
	/*首先判断任务调度器是否被挂起,如果等于pdFALSE 则没有被挂起*/
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
		/*
			将系统时钟节拍xTickCount加1,然后再将值赋给自己,
			每进来一次时钟节拍将自加1
		*/
		const TickType_t xConstTickCount = xTickCount + 1;
		xTickCount = xConstTickCount;

		/*判断xConstTickCount 是否为0,为0则值溢出*/
		if( xConstTickCount == ( TickType_t ) 0U )
		{
			taskSWITCH_DELAYED_LISTS();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/*判断当前时钟节拍ConstTickCount 是否大于等于下一个阻塞超时时间*/
		if( xConstTickCount >= xNextTaskUnblockTime )
		{
			for( ;; )
			{
				/*判断阻塞列表中是否有任务,如果没有任务则没有需要被解除的任务。*/
				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
				{
					/*则将下一个阻塞超时时间xNextTaskUnblockTime设置为最大值。*/
					xNextTaskUnblockTime = portMAX_DELAY; 
					break;
				}
				/*else则阻塞列表中有任务*/
				else
				{
					/*通过函数listGET_OWNER_OF_HEAD_ENTRY()获取阻塞列表的第一个成员的任务控制块*/
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
					
					/*
						通过函数listGET_LIST_ITEM_VALUE()获取列表项的数值,列表项中一般存放的是阻塞时间,
						则xItemValue被赋值阻塞时间
					*/
					xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

					
					/*判断系统时间节拍的数值是否小于阻塞时间,代表此时发生异常*/
					if( xConstTickCount < xItemValue )
					{
						/*
							因为在首先进入if时判断了当前的系统时钟节拍比下一个阻塞超时时间大。
							此时将列表项的值赋值给下一个阻塞超时时间,退出。
						*/
						xNextTaskUnblockTime = xItemValue;
						break;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/*
						使用函数listREMOVE_ITEM()将任务从阻塞列表中移除,
						同时也从使用函数listREMOVE_ITEM从事件列表中移除。
					*/
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );

					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* 将任务添加到就绪列表中 */
					prvAddTaskToReadyList( pxTCB );

					/* 判断宏configUSE_PREEMPTION是否使能抢占式任务调度 */
					#if (  configUSE_PREEMPTION == 1 )
					{
						/*判断恢复的任务的任务优先级是否比当前正在执行的任务优先级高*/
						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
						{
							/*将任务切换xSwitchRequired变量赋值pdTRUE。*/
							xSwitchRequired = pdTRUE;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configUSE_PREEMPTION */
				}
			}
		}

		/* 以下程序是时间片调度 */
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
		{
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
			{
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

		#if ( configUSE_TICK_HOOK == 1 )
		{
			/* Guard against the tick hook being called when the pended tick
			count is being unwound (when the scheduler is being unlocked). */
			if( uxPendedTicks == ( UBaseType_t ) 0U )
			{
				vApplicationTickHook();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICK_HOOK */
	}
	else
	{
		++uxPendedTicks;

		/* The tick hook gets called at regular intervals, even if the
		scheduler is locked. */
		#if ( configUSE_TICK_HOOK == 1 )
		{
			vApplicationTickHook();
		}
		#endif
	}

	#if ( configUSE_PREEMPTION == 1 )
	{
		if( xYieldPending != pdFALSE )
		{
			xSwitchRequired = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_PREEMPTION */

	return xSwitchRequired;
}

2.3 taskSWITCH_DELAYED_LISTS() - 解析

只溢出之后,将就绪列表pxDelayedTaskList溢出就绪列表pxOverflowDelayedTaskList 进行互换。

#define taskSWITCH_DELAYED_LISTS()																	\
{																									\
	List_t *pxTemp;																					\
																									\
	/* The delayed tasks list should be empty when the lists are switched. */						\
	configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );										\
																									\
	pxTemp = pxDelayedTaskList;																		\
	pxDelayedTaskList = pxOverflowDelayedTaskList;													\
	pxOverflowDelayedTaskList = pxTemp;																\
	xNumOfOverflows++;																				\
	prvResetNextTaskUnblockTime();																	\
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值