- uC/OS-II 用硬件定时器产生一个周期为毫秒(ms)级的周期性中断来实现系统时钟。最小的时钟单位就是两次中断之间相间隔的时间,这个最小时钟单位叫做时钟节拍。
硬件定时器以时钟节拍为周期定时地产生中断,该中断的中断服务程序叫做OSTickISR()。其示意代码如下:
Void OSTickISR(void)
{
OSIntEnter();
OSTimeTick();
OSIntExit();
}
在时钟中断服务程序中调用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 == 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 = TRUE;
break;
case OS_TICK_STEP_WAIT: /* No, waiting for uC/OS-View to set ...*/
step = FALSE; /* .. OSTickStepState to OS_TICK_STEP_ONCE */
break;
case OS_TICK_STEP_ONCE: /* Yes, process tick once and wait for next ... */
step = TRUE; /*... step command from uC/OS-View */
OSTickStepState = OS_TICK_STEP_WAIT;
break;
default: /* Invalid case, correct situation */
step = TRUE;
OSTickStepState = OS_TICK_STEP_DIS;
break;
}
if (step == FALSE) { /* Return if waiting for step command */
return;
}
#endif
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_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 */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */
ptcb->OSTCBPendTO = TRUE; /* Indicate PEND timeout */
} else {
ptcb->OSTCBPendTO = FALSE;
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY)
{ /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}
从加粗的代码片段中可知,uC/OS-II在每次响应定时中断时调用OSTimeTick()做了两件事:一是计数器OSTime加1;二是遍历任务控制链表中的所有任务控制块,把各个任务控制块中用来存放任务延时时限OSTCBDly变量减1,并使该项为0,同时又不使被挂任务的任务进入就绪状态。函数OSTimeTick的任务就是在每个时钟节拍了解每个任务的延时状态,使其中已经到了延时时限的非挂起任务进入就绪状态。
- 任务的延时
怎么解除高优先级任务独占CPU,可以给其他任务优先级别低的任务获得CPU使用权的机会?
系统规定:除了空闲任务之外的所有任务必须在任务中合适的位置调用系统提供的函数OSTimeDly(),使当前任务的运行延时一段时间并进行一次任务调度,以让出CPU的使用权。
函数OSTimeDly()的代码如下:
void OSTimeDly (INT16U ticks)
{
INT8U y;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (ticks > 0) { /* 0 means no delay! */
OS_ENTER_CRITICAL();
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; //取消当前任务的就绪状态
}
OSTCBCur->OSTCBDly = ticks; /*延时节拍数存入任务控制块 */
OS_EXIT_CRITICAL();
OS_Sched(); /* 调用调度函数 */
}
}
为了方便使用,系统还提供了OSTimeDlyHMSM()。该函数的原型如下:
INT8U OSTimeDlyHMSM(
INT8U hours, //小时
INT8U minutes, //分
INT8U seconds, //秒
INT16U milli //毫秒
);
- 取消任务的延时
延时的任务可通过在其他任务中调用函数OSTimeDlyResume()取消延时而进入就绪状态。如果新任务比正在运行的任务优先级别高,则立即引发一次任务调度。
函数OSTimeDlyResune()的原型如下:
INT8U OSTimeDlyResume (INT8U prio)
{
OS_TCB *ptcb;
#if OS_CRITICAL_METHOD == 3 /* Storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (prio >= OS_LOWEST_PRIO) {
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
ptcb = OSTCBPrioTbl[prio]; /* Make sure that task exist */
if (ptcb == (OS_TCB *)0) {
OS_EXIT_CRITICAL();
return (OS_TASK_NOT_EXIST); /* The task does not exist */
}
if (ptcb == (OS_TCB *)1) {
OS_EXIT_CRITICAL();
return (OS_TASK_NOT_EXIST); /* The task does not exist */
}
if (ptcb->OSTCBDly == 0) { /* See if task is delayed */
OS_EXIT_CRITICAL();
return (OS_TIME_NOT_DLY); /* Indicate that task was not delayed */
}
ptcb->OSTCBDly = 0; /* Clear the time delay */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */
ptcb->OSTCBPendTO = TRUE; /* Indicate PEND timeout */
} else {
ptcb->OSTCBPendTO = FALSE;
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
OS_Sched(); /* See if this is new highest priority */
} else {
OS_EXIT_CRITICAL(); /* Task may be suspended */
}
return (OS_NO_ERR);
}