浅析FreeRTOS_v4.5.0延时机制---vTaskDelay()的实现
文章来源:http://gliethttp.cublog.cn[转载请声明出处]
void vTaskDelay( portTickType xTicksToDelay ) { portTickType xTimeToWake; signed portBASE_TYPE xAlreadyYielded = pdFALSE; if( xTicksToDelay > ( portTickType ) 0 ) { //真的是要延时 vTaskSuspendAll();//那么锁住调度器 { //在调度器锁定期间,如果因为IRQ中断之类,引起了,某个task从事件等待状态 //变为了可运行状态,在IRQ中,调用了xQueueGenericSendFromISR()事件函数, //并不会直接将该task从Event事件队列链表中摘除, //该task会被临时放到xPendingReadyList队列链表上,当调度器解锁的时候, //会将xPendingReadyList队列链表上所有暂时被登记的tasks们,添加到就绪运行队列链表 //pxReadyTasksLists[x],同时将tasks们依次从他们对应的Event事件链表上摘下 //能够调用vTaskDelay()的task肯定没有等待Event事件,否则不会执行到这里, //所以仅仅对task的xGenericListItem进行操作,即可,不用理会xEventListItem //下面计算从当前系统滴答xTickCount开始,经过xTicksToDelay延时之后数据为多少,可能溢出, //如果会发生时间上的溢出,那么把本task添加到溢出时间队列链表上,否则添加到当前时间 //队列链表上,比如:xTickCount现在等于0xfffffffe,xTicksToDelay的值定义为延时10个ticks滴答, //那么xTimeToWake最后因为溢出将会等于8,所以在溢出时间队列链表上的第8个ticks滴答 //会唤醒本task(gliethttp) xTimeToWake = xTickCount + xTicksToDelay; //task把自己从就绪运行队列链表pxReadyTasksLists[x]中摘掉,这之后本task已经不在就绪运行队列链表中, //因为调度器在就绪运行队列链表中,再也找不本task的信息,进而调度器也就不会调度本task了 vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); //xGenericListItem的xItemValue用来存放延时ticks值xTimeToWake listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake );
if( xTimeToWake < xTickCount ) { //说明发生了时间溢出,所以把本task添加到pxOverflowDelayedTaskList队列, //当vTaskIncrementTick()执行系统滴答时,发现xTickCount == 0时,FreeRTOS会自动 //将pxOverflowDelayedTaskList切换为当前时间队列链表,同时当前时间链表会作为下一个 //溢出时间链表被置到后台, //即: //pxTemp = pxDelayedTaskList; //pxDelayedTaskList = pxOverflowDelayedTaskList;//将后台的时间队列链表置到前台,正式使用 //pxOverflowDelayedTaskList = pxTemp;//将正在使用的时间队列链表置到后台,等待下一次时间溢出时使用. vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); } else { //延时时间计算之后,并没有发生溢出现象,那么将本task挂接到前台正在使用中的时间队列链表 //pxDelayedTaskList上 vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); } } //ok,我们已经将task从就绪运行队列链表中摘下来,并且把自己放到了时间队列链表上, //现在执行xTaskResumeAll(), //1.看看,在我们执行上面代码期间内是否有其他task //发生了调度申请,也就是xPendingReadyList队列链表上是否有task了, //如果有task登记到xPendingReadyList队列链表上,那么 //分别把他们从相应的Event事件队列链表上摘下 //(因为IRQ中发现调度器锁住之后,不会进行摘除工作,仅仅把task添加到xPendingReadyList队列链表上), //同时把他们也从等待的时间队列链表pxDelayedTaskList或者pxOverflowDelayedTaskList上摘下来, //之后把他们添加到就绪运行队列链表pxReadyTasksLists[x]上,如果添加的task的优先级比当前运行 //的task的优先级高,那么标示随后需要内核调度器调度. //2.看看,在我们执行上面代码期间内是否有发生了系统tick滴答, //如果发生了,那么模拟定时器,连续调用vTaskIncrementTick()函数uxMissedTicks次,补上错过的uxMissedTicks个 //系统tick滴答,之后需要调度,所以调用taskYIELD()产生调度, //3.看看,是否因为内核被锁住而有些tasks暂时被推迟调度的现象, //3.1>是否已经有task申请了内核调度vTaskSwitchContext()或者 //3.2>prvUnlockQueue()发现有优先级高于当前正在运行的task的被唤醒, //进而通过vTaskMissedYield()登记需要执行内核调度器. //只要上面有一个条件符合,那么xTaskResumeAll()函数中就会执行taskYIELD()调度, //进而本task被抢占,直到恢复获得cpu时,xAlreadyYielded将等于true,所以下面的 //调度语句taskYIELD(),就不用再执行了,因为本task刚刚才获得cpu使用权(gliethttp). xAlreadyYielded = xTaskResumeAll(); } if( !xAlreadyYielded ) { //如果在xTaskResumeAll()中没有发生调度,没有发生其他task抢占本task的现象, //本task没能切换出去,进而让出cpu, //那么这里将调用taskYIELD(),本task自己主动让出cpu,供其他task使用(gliethttp). taskYIELD(); } }
|