开启辅助访问 切换到窄版

打印 上一主题 下一主题

ucos任务调度系统讲解-timetick

[复制链接]
楼主
跳转到指定楼层
| 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0x00摘要
本文接着上一期讲系统调度过程中的函数OSTimeTick(),对函数中的每一段代码及其作用原理进行分析,最后对这个函数的总体思路进行概括
0x01引言
OSTimeTick()ucos在每次心跳时必调用的函数,其功能在了解ucos任务调度的原理占有很重要的地位,本文对其进行剖析.
为了使程序看起来简洁,在正文中将程序的注释已经去掉,若需要可以关注公众号后回复UCOS,获取源码,进行阅读.
0x02原理
首先根据OS_TICK_STEP_EN,是否允许单步调试、根据OS_CRITICAL_METHOD进入临界段的方法、根据OS_TIME_TICK_HOOK_EN是否允许systick钩子函数、根据OS_TIME_GET_SET_EN是否允许记录系统运行时间等设置相应函数和变量,这里采用的是#if....#endif来进行判断,这是C语言预编译条件语句,在编译前进行处理,然后在进行正式编译
#if OS_TICK_STEP_EN > 0
BOOLEAN step;
#endif
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_TIME_TICK_HOOK_EN > 0
OSTimeTickHook();
#endif
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL();
OSTime++;
OS_EXIT_CRITICAL();
#endif
首先判断系统的运行真假,假的话不进行任务处理,直接结束timetick函数,这里涉及到OSRunning,这个变量的设置,这是系统进行判断是否ucos正常运行的一个全局变量在OSStart ()中通过OSStartHighRdy()这个汇编函数设置为OS_TRUE。
if (OSRunning == OS_TRUE) {...}
OS_TICK_STEP_EN被设置为真时,这个变量和前面 提到的变量都是用户裁剪系统时自行设定的,位置在系统的配置文件os_cfg.h.允许进行任务单步调试时,进行变量判读,当接收到调试命令时进行响应动作。(这里需要用uC/OS-View进行调试,笔者未曾用过,在调试软件中应该可将OS_TICK_STEP_ONCE传递进来,实现单步调试)。
switch (OSTickStepState) {
case OS_TICK_STEP_DIS:
step = OS_TRUE;
break;

case OS_TICK_STEP_WAIT:
step = OS_FALSE;
break;

case OS_TICK_STEP_ONCE:
step = OS_TRUE;
OSTickStepState = OS_TICK_STEP_WAIT;
break;

default:
step= OS_TRUE;
OSTickStepState = OS_TICK_STEP_DIS;
break;
}
如果是单步调试则能够让调度函数timetick只运行一次,下一次,step设为OS_FALSE,在程序运行到,下面的代码时直接返回,从而不调度任务.
if (step == OS_FALSE) {
return;
}
然后对任务的睡眠时间进行判断,首先获取TCB链表头,
ptcb = OSTCBList;
然后循环判断直到空闲任务截至,因为系统默认空闲任务是最后一个任务,这个在系统初始化时会进行设置,这里对TCB列表进行了遍历式查询,所以任务越多会使系统的查询时间变长
while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {...ptcb = ptcb->OSTCBNext; ....}

while循环里面,判断睡眠时间是否为零,为零则直接获取下一个TCB
if (ptcb->OSTCBDly != 0) { ...}

不为零则自减1,然后判断是否为零,若不为零则直接跳到下一个TCB继续操作,这里ptcb->OSTCBDlyucos判断任务是否延时或者睡眠或者挂起时间到达的唯一一个变量,后面的很多延时函数和挂起超时都是设置的这个变量.
if (--ptcb->OSTCBDly == 0) {...}
若为零则进行任务处理,首先判断是否有其他事件将任务挂起,ptcb->OSTCBStat中的每个比特位代表一个事件,这里通过ptcb->OSTCBStat与宏变量OS_STAT_PEND_ANY比较可以判断是否是某个事件引起的挂起.
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {...}
若有则表明任务因为这些事件才睡眠,然后将这些挂起标志位清零,并设置挂起状态位OSTCBStatPendOS_STAT_PEND_TO,当任务恢复执行时便可以知道因为挂起超时而返回,以便做相应的处理
ptcb->OSTCBStat &= ~(INT8U)OS_STAT_PEND_ANY;
ptcb->OSTCBStatPend = OS_STAT_PEND_TO;
若没有挂起则直接将OSTCBStatPendOS_STAT_PEND_OK,表示任务睡眠时间到,不是被其他事件挂起引起的睡眠。
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
然后在通过ptcb->OSTCBStat判断任务是否一切就绪,这里的宏变量OS_STAT_SUSPEND来进行判读,这个宏值是0x08,即二进制的00001000,作用是标志着任务是否被挂起.用函数OSTaskSuspend来暂时停止一个任务的执行,将任务状态转换为阻塞态,并设置ptcb->OSTCBStat这个宏代表的位置。那么处于阻塞态的任务要想得到运行,必须用OSTaskResume先恢复到就绪态,其他函数是无法设置这个位置的,表示任务被系统挂起。
然后将这个任务放入到就绪任务表中,供系统根据优先级进行调用。
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
  OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
  系统的优先级和就绪表涉及到一定的技巧,下次专门讲解.
到此完成了对一个任务的判断,然后继续向下进行检查,直到所有的任务检查完成.
Timetick的整体思路就是,先根据用户定义,决定是不是设定一些特殊函数,比如单步调试,这些可有可无,根据用户需求自行裁剪.然后是调度的核心程序,通过任务表链表对每个任务进行剩余时间判断,若剩余时间为零则跳过,否则将剩余时间减1后再判断,不为0则跳过,继续下一个任务检查.当剩余时间为0,则表明任务的时间已到,判断是因为事件挂起的时间到了还是任务自行延时时间到了,若是事件挂起的时间到了,表明事件没有得到满足,将挂起标志清除,并设置变量表明因为挂起超时,任务继续执行.若是自行延时时间到了则不需要特别设定.最后在判定任务有没有被永久挂起,没有的话就可以将任务设定到就绪表中等待调度.

0x03小结
通过对TImetick的讲解,大家应该能够很好的了解任务调度中的一些细节,对于初学者来说,仅仅通过一两个函数仍然是很难了解其中的具体内容,仍旧感觉云里雾里,在以后的文章中将会对每一个涉及到的函数进行一一讲解.由于是在工作之余对源码分析总结可能周期会有点长,文章更新的速度也会慢一些.


欢知识传播是一种美德

让更多人少走弯路

才能有更多人与你携手前进

欢迎扫码一起分享经验






如有任何疑问请后台回复

或添加作者Waiting_B_H

附上OSTimeTick()的源码:
void OSTimeTick (void)
{
OS_TCB *ptcb;
#if OS_TICK_STEP_EN > 0
BOOLEAN step;
#endif
#if OS_CRITICAL_METHOD == 3/* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_TIME_TICK_HOOK_EN > 0
OSTimeTickHook();/* Call user definable hook */
#endif
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL();/* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
#endif
if (OSRunning == OS_TRUE) {
#if OS_TICK_STEP_EN > 0
switch (OSTickStepState) { /* Determine whether we need to process a tick */
case OS_TICK_STEP_DIS: /* Yes, stepping is disabled */
step = OS_TRUE;
break;

case OS_TICK_STEP_WAIT: /* No, waiting for uC/OS-View to set ... */
step = OS_FALSE; /* .. OSTickStepState to OS_TICK_STEP_ONCE */
break;

case OS_TICK_STEP_ONCE: /* Yes, process tick once and wait for next ... */
step = OS_TRUE; /* ... step command from uC/OS-View */
OSTickStepState = OS_TICK_STEP_WAIT;
break;

default: /* Invalid case, correct situation */
step= OS_TRUE;
OSTickStepState = OS_TICK_STEP_DIS;
break;
}
if (step == OS_FALSE) { /* Return if waiting for step command */
return;
}
#endif
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* No, Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) {/* Decrement nbr of ticks to end of delay */
/* Check for timeout */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~(INT8U)OS_STAT_PEND_ANY; /* Yes, Clear status flag */
ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout */
} else {
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
}

if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No,Makeready*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}

@font-face { font-family: "Times New Roman";}@font-face { font-family: "宋体";}@font-face { font-family: "Calibri";}@font-face { font-family: "SimSun";}p.MsoNormal { margin: 0pt 0pt 0.0001pt; text-align: justify; font-family: Calibri; font-size: 10.5pt; }span.msoIns { text-decoration: underline; color: blue; }span.msoDel { text-decoration: line-through; color: red; }div.Section0 { }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表