- <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
|