UCOSIII中的节拍列表更新
Posted yinshijia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UCOSIII中的节拍列表更新相关的知识,希望对你有一定的参考价值。
序言
上一篇博客中我介绍到,单片机使用一个与内核绑定的定时器产生固定频率的中断,这个中断所对应的ISR会调用OSTimeTick(),OSTimeTick()会使用OS_IntQPost来向一个IntQ队列中放一个Post过程信息,之后让OS_IntQTask就绪,来向OS_TickTask发送信号量,OS_TickTask收到这个信号量之后就会调用OS_TickListUpdate()。这篇文章中我们来详细探究一下OS_TickListUpdate的内部执行过程
源码解释
我这里直接通过代码打注释的方式来一步一步探究这个函数的执行过程,最后会有一个总结
void OS_TickListUpdate (void)
{
CPU_BOOLEAN done;
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
OS_TICK_SPOKE_IX spoke;
CPU_TS ts_start;
CPU_TS ts_end;
CPU_SR_ALLOC();
//进入临界区
OS_CRITICAL_ENTER();
//获取时间戳,用来计算某段代码的执行时间,和最后的 ts_end = OS_TS_GET() - ts_start 配合使用
ts_start = OS_TS_GET();
//一个计数变量,用来统计UCOSIII从启动到现在一共经历了多少个时钟节拍
OSTickCtr++; /* Keep track of the number of ticks */
//从节拍列表中取一个任务出来,具体如何取得,可以研究一下OSCfg_TickWheel这个结构体
spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);
p_spoke = &OSCfg_TickWheel[spoke];
//取出数组元素双向链表得第一个任务控制块
p_tcb = p_spoke->FirstPtr;
done = DEF_FALSE;
while (done == DEF_FALSE) {
if (p_tcb != (OS_TCB *)0) {
p_tcb_next = p_tcb->TickNextPtr; /* Point to next TCB to update */
//判断任务状态
switch (p_tcb->TaskState) {
//这些状态都是不可能得
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
break;
//如果任务是单纯得延时状态
case OS_TASK_STATE_DLY:
//这里可以使用全局搜索看一下TickCtrMatch是如何被初始化的,就可以理解这里了
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
p_tcb->TaskState = OS_TASK_STATE_RDY;
OS_TaskRdy(p_tcb); /* Make task ready to run */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
//如果是因为带超时监测的阻塞而被延时
case OS_TASK_STATE_PEND_TIMEOUT:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
//将任务从阻塞列表中移除
OS_PendListRemove(p_tcb); /* Remove from wait list */
//将让任务就绪,其实这个函数包含两个步骤:将任务移除TickList,将任务插入就绪列表
OS_TaskRdy(p_tcb);
p_tcb->TaskState = OS_TASK_STATE_RDY;
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
//如果任务延时而且被悬挂
case OS_TASK_STATE_DLY_SUSPENDED:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
//我们这里仅仅将任务从TickList中移除,并不让它就绪
OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
//如果任务是因为带超时监测的阻塞而延时,并且这个时候被悬挂起来了
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
//移出PendList
OS_PendListRemove(p_tcb); /* Remove from wait list */
//移出TickList
OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
default:
break;
}
//更新p_tcb,准备检查下一个任务
p_tcb = p_tcb_next;
} else {
done = DEF_TRUE;
}
}
//配合开头计算,执行上面这段代码所用的时间
ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */
if (OSTickTaskTimeMax < ts_end) {
OSTickTaskTimeMax = ts_end;
}
OS_CRITICAL_EXIT();
}
总结
整个代码的执行流程是这样的:每当一个节拍到来,我们就根据某种方法计算出一个位置,根据这个位置从OSCfg_TickWheel取出一个元素,这个元素其实代表着一个双向链表,我们在这个节拍内逐个检查这个双向链表上面的每一个任务,到时间的就将它从TickList中移出去。下面是OSCfg_TickWheel的示意图
以上是关于UCOSIII中的节拍列表更新的主要内容,如果未能解决你的问题,请参考以下文章
无法从 ViewPager 中的另一个片段刷新/更新片段中的列表视图