FreeRTOS-时间管理(延时、系统时钟)源码分析

在写FreeRTOS的应用程序时,经常需要使用到延时函数,当执行到延时函数时,会将任务从就绪状态变为延时等待状态,这里会放弃CPU的占用权进入阻塞态,将CPU让给其它任务使用,直到延时时间结束会重新变为就绪态。FreeRTOS中的延时函数有两种模式,一种是相对模式,另一种是绝对模式。相对延时函数使用 vTaskDelay() ,相对延时函数是指每次执行都是从函数接口处开始计时,计时到指定时间结束回来继续往下执行。绝对延时函数使用 vTaskDelayUntil() ,绝对延时函数是指每隔一段指定的时间执行一次 vTaskDelayUntil() ,那些需要按照一定频率运行的任务可以使用绝对模式。


先分析一下相对模式的应用方式,一般我们是这样使用的:

void led_task(void *pvParameters)
{
	while(1)
    {
        LED0=!LED0;
        vTaskDelay(500); /* LED灯每隔500ms亮灭一次 */
    }
}

函数的实现如下:

#if ( INCLUDE_vTaskDelay == 1 )

	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();
		}


		if( xAlreadyYielded == pdFALSE )
		{
			/* 如果 xTaskResumeAll 函数没有进行调度的话就在这里进行任务切换,此 */
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

#endif

上面主要涉及到几个函数:

vTaskSuspendAll
prvAddCurrentTaskToDelayedList
xTaskResumeAll
portYIELD_WITHIN_API
void vTaskSuspendAll( void )
{
	/* 挂起调度器不需要关闭可屏蔽中断,这个变量递增后在systick中断函数里面判断到 uxSchedulerSuspended 
		不为 pdFALSE就不会去判断延时列表是否有任务可以执行,这里挂起主要为了避免调度器和即将运行的函数操作发生冲突,
		在systick操作了延时列表和计数器,接下来的函数操作中也会操作延时列表和计数器,所以需暂时屏蔽 */
	/* 挂起计数器(支持嵌套) */
	++uxSchedulerSuspended;
}

portYIELD_WITHIN_API() 实际上就是操作中断控制及状态寄存器ICSR,请求一次PendSV中断,PendSV任务切换具体步骤前面任务“FreeRTOS-任务切换源码分析”已经分析了,

#define portYIELD()																\
{																				\
	/* Set a PendSV to request a context switch. */								\
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\
																				\
	/* Barriers are normally not required but do ensure the code is completely	\
	within the specified behaviour for the architecture. */						\
	__dsb( portSY_FULL_READ_WRITE );											\
	__isb( portSY_FULL_READ_WRITE );											\
}

延时函数主要就是将任务从就绪状态切换为等待状态,使用 prvAddCurrentTaskToDelayedList 来实现:

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
TickType_t xTimeToWake;
const TickType_t xConstTickCount = xTickCount;

	/* 将任务从就绪列表中删除 */
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/* 清除对应就绪优先级标志 */
		portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); 
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/* 如果允许任务挂起 */
	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			/* 如果设置了最大延时时间
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值