FreeRTOS(V8.0.1)系统之vTaskDelay()和vTaskDelayUntil()

<pre name="code" class="objc">#if ( INCLUDE_vTaskDelay == 1 )
	//延时特定时间xTicksToDelay,这个时间需要转换为唤醒绝对时间xTimeToWake,
	//这样才能在与vTaskIncrementTick函数中操作的数值是一致的xTicksToDelay:延时的节拍数
	void vTaskDelay( const TickType_t xTicksToDelay )
	{
	TickType_t xTimeToWake;
	BaseType_t xAlreadyYielded = pdFALSE;

		if( xTicksToDelay > ( TickType_t ) 0U )//若延迟的时间是0,就是调度器的重新启动。若延时时间大于0,执行延时操作。
		{
			configASSERT( uxSchedulerSuspended == 0 );
			vTaskSuspendAll();//系统维护一个uxSchedulerSuspended计数值,当其大于0时表示禁止调度;等于0时则表示允许调度。
			{
				traceTASK_DELAY();
				//计算唤醒时间--这可能会溢出,但不会有问题 ,大家会问为什么
                //大家可以自己做个试验,两个uCHAR型数据相加,如果超过255,则等于多少?
				xTimeToWake = xTickCount + xTicksToDelay;//任务的唤醒时间更新。
				if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )//若删除完后链表中没有任务
				//把任务从当前运行链表中移除出去,然后把它添加到阻塞链表里面
				{
					//当前任务必须在就绪链表中,所以其是不必检查的,下面的宏定义可以直接调用
					portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
				prvAddCurrentTaskToDelayedList( xTimeToWake );
				//prvAddCurrentTaskToDelayedList是一个函数,它是把当前的任务控制块,
				//放进DelayedList链表中,而DelayedList有两个链表,一个是溢出的,一个是正在应用的,
				//所以要根据传递进的参数xTimeToWake进行分别设置,如果是在当前的延时链表里就添加进现在的延时链表,
				//如果计算出来后是溢出链表,则添加进溢出链表里面
			}
			xAlreadyYielded = xTaskResumeAll();//得到任務切換的具體情況,pdTRUE切换成功否则切换失败。
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		if( xAlreadyYielded == pdFALSE )//若上面的切换失败,或不需要延迟。直接进行切换。
		{
			portYIELD_WITHIN_API();
			//实际就是终端控制及状态寄存器ICSR,写位28为1悬起PendSV,进入到xPortPendSVHandler。
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

#endif 

#if ( INCLUDE_vTaskDelayUntil == 1 )
	void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
//参数:pxPreviousWakeTime---上一次调用本函数的时间 
//		xTimeIncrement---相对于pxPreviousWakeTime本次延时的节拍数
//由于调用此函数的任务解除阻塞的时间是绝对时刻,比起相对于调用时刻的相对时间更精确(即比调用vTaskDelay()可以实现更精确的周期性)。
//pxPreviousWakeTime: 此参数命名时假定vTaskDelayUntil()用于实现某个任务以固定频率周期性执行。这种情况下pxPreviousWakeTime
//					  保存了任务上一次离开阻塞态(被唤醒)的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。
//xTimeIncrement: 此参数命名时同样是假定vTaskDelayUntil()用于实现某个任务以固定频率周期性执行 —— 这个频率就是由xTimeIncrement 指定的。 
//			      *xTimeIncrement 的单位是心跳周期, 可以使用常量portTICK_RATE_MS 将毫秒转换为心跳周期
	{
	TickType_t xTimeToWake;//和vTaskDelay函数中一样定义,最终得到xTimeToWake赋值给xGenericListItem .xItemValue
	BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

		configASSERT( pxPreviousWakeTime );
		configASSERT( ( xTimeIncrement > 0U ) );
		configASSERT( uxSchedulerSuspended == 0 );

		vTaskSuspendAll();//调度器挂起
		{
			const TickType_t xConstTickCount = xTickCount;//xTickCount在这个函数里面不能改变,所以采用另外一个变量做优化。
			xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; //计算下次唤醒的时刻.

			if( xConstTickCount < *pxPreviousWakeTime )//说明 xTickCount 溢出了
			{
				//pxPreviousWakeTime指向上一次的唤醒时间,这个地方有点难理解,可以这样说明:时间轴是一个往前的轴,
				//是不会有溢出之说法,也就是说xTickCount永远都是大于唤醒时间的而不管哪一次的唤醒时间都是大于,
				//但由于溢出的存在,所以有时候xTickCount会小于唤醒时间,这里就说明是小于上一次设置的唤醒时间,如果是就说明是溢出了
				//::T3::::::T2:::::::::::::::::::::T1:::::::::::::::::::::::::::::::::::::T2:::::::::::::::::T3:::::::::*/
				//            xTickCount    *pxPreviousWakeTime                       xTickCount       xTimeToWake*/
				//T1对应*pxPreviousWakeTime ,T2对应xTickCount,T3对应xTimeToWake*/
				//因为在运行这个程序时,任务因为运行了其它程序,造成了xTickCount和唤醒时间不是在同一个点上,要么大于*pxPreviousWakeTime,
				//要么小于*pxPreviousWakeTime,就像T2一样,如果运行到T第一个T0处,那么,就相当于xTickCount已经溢出了,那么需要不
				//需要延时,就要看xTimeToWake所处的位置了,如果没溢出,则说明不需要延时就可以调度任务
				//,如果溢出了,还要看是否大于xTickCount,如果是才能算的上是真正的需要放入延时链表里面
				if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
				//这时只有 xTimeToWake 也溢出了,并且 xTimeToWake > xConstTickCount 才需要休眠
				{
					xShouldDelay = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				//下面两种情况才需要休眠
				if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
				{
					xShouldDelay = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			//为下一次 Delay 更新 pxPreviousWakeTime.
			*pxPreviousWakeTime = xTimeToWake;//把需要唤醒的绝对时间保存起来,保存到pxPreviousWakeTime指针变量里面

			if( xShouldDelay != pdFALSE )//这时需要休眠,由上面的判断任务是否进入延时链表,如果不需要,则仍是当前运行的任务
			{
				traceTASK_DELAY_UNTIL();
				//从 Ready 链表中删除,加入 Blocked List
				if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
				{
					portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				prvAddCurrentTaskToDelayedList( xTimeToWake );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		xAlreadyYielded = xTaskResumeAll();
		//在运行上面临界区的程序时,可能有任务需要调度,但因为调度器的挂起而没有被调度,只是给出了登记,
		//而这个xTaskResumeAll函数就是要把放进xPendingReadyList链表中的任务节点转移到真正的就绪链表pxReadyTasksLists里面,
		//如果任务是因为tick缺失或者因为在恢复实际走过的滴答数时有任务需要抢占CPU,则 xAlreadyYielded 都为真,
		//从而导致下面不会运行,如果没有被抢占也就是说当前还是处于最高级任务,但是上面的延时已经使其阻塞,从而在下面发生抢占
		if( xAlreadyYielded == pdFALSE )
		//强制自己交出CPU,使自身进入等待延时。个人认为:此处并不需要强制交出,如果上面并不需要加入延时链表,
		//表示还是运行的当前任务,如果这个任务仍然是最高级的,则并不需要切换
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

#endif


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值