首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

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

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

#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
记录学习中的点点滴滴,让每一天过的更加有意义!
返回列表