文章目录
前言
任何操作系统都需要一个周期性的信号源,以供系统处理延迟、超时等与时间有关的事件;这个周期性的信号源叫做时钟。也就是系统的“心跳”。
uC/OS-II的时钟
uC/OS-II和大多数操作系统一样,采用硬件定时器的方式来产生一个周期为毫秒(ms)级中断来实现时钟系统。最小时钟单位就是节拍,即两次中断之间的最短时间。
硬件定时器以时钟节拍为单位周期性地产生中断,对应的中断服务程序为OSTickISR(),但是通常不在OSTickISR()中写用户的操作,在OSTickISR()中会调用函数OSTimeTick()函数,在OSTimeTick()调用OSTimeHook()函数,通常用户需要进行的操作会写在OSTimeHook()函数中。为什么要这么复杂呢?我猜测可能是因为“一个函数只做一件事”这个理念。
常见的函数
OSTickISR()时钟中断的中断服务程序
这个函数是每个时钟中断到了之后进入的函数,移植的时候这个函数通常写在对应微处理器的时钟中断里面。这个函数是用汇编写的。OSTickISR()函数的基本功能如下:
void OSTickISR(void)
{
保存CPU寄存器;
调用OSIntEnter(); // 记录中断嵌套层数
if (OSIntNesting == 1)
{
OSTCBCur->OSTCBStkPtr = SP; // 在任务TCB中保存堆栈指针
}
调用OSTimeTick(); // 节拍处理
清除中断;
开中断;
调用OSIntExit();
恢复CPU寄存器;
中断返回;
}
可以看到这个函数就是进入中断之后保存现场,调用OSTimeTick()函数,恢复现场。
OSTimeTick()时钟节拍服务函数
这个函数的功能是记录节拍数,遍历任务控制块链表,将所有的任务控制块中用来存放延迟时限的变量OSTCBDly变量减1,如果OSTCBDly等于0,那么就将对应的任务置为就绪状态。
源码如下:
void OSTimeTick (void)
{
OS_TCB *ptcb;
#if OS_TICK_STEP_EN > 0u
BOOLEAN step;
#endif
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_TIME_TICK_HOOK_EN > 0u
OSTimeTickHook(); /* Call user definable hook */
#endif
#if OS_TIME_GET_SET_EN > 0u
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
#endif
if (OSRunning == OS_TRUE) {
#if OS_TICK_STEP_EN > 0u
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 != 0u) { /* No, Delayed or waiting for event with TO */
ptcb->OSTCBDly--; /* Decrement nbr of ticks to end of delay */
if (ptcb->OSTCBDly == 0u) { /* Check for timeout */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= (INT8U)~(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, Make ready */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}
OSTimeTickHook()函数
这个函数是用户真正实现自己功能的地方,位于os_cpu_c.c文件
任务的延迟
通常嵌入式任务是一个无限循环,并且uC/OS-II还是抢占式内核,所以为了避免高优先级任务一直运行,而低优先级任务没机会运行,uC/OS-II规定了每一个任务都必须在合适的位置加上延迟来让出cpu的使用权。
OSTimeDly(INT16U ticks)
这个函数功能就是将本任务取消就绪状态,同时将ticks赋给OSTCBCur->OSTCBDly;然后调用OS_Sched()函数。
源码如下:
void OSTimeDly (INT16U ticks)
{
#if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
OS_CPU_SR cpu_sr;
#endif
if (ticks > 0) { //如果延时设定为0值,表示不想对任务延时,返回调用任务
OS_ENTER_CRITICAL(); //关闭中断
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { /* Delay current task */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
//非0值会使得任务延时函数OSTimeDly()将当前任务从就绪表中移除
OSTCBCur->OSTCBDly = ticks; //接着,这个延时节拍数会被保存在当前任务的OS_TCB中
OS_EXIT_CRITICAL(); //打开中断
OS_Sched(); //既然任务已经不再处于就绪任务,(任务调度),任务调度程序会执行下一个优先级最高的就绪任务
如果周期为毫秒的话,那么ticks单位就是毫秒
OSTimeDlyHMSM()
和OSTimeDly()函数功能差不多,但是参数有区别
INT8U OSTimeDlyHMSM (INT8U hours,
INT8U minutes,
INT8U seconds,
INT16U ms);
取消任务延迟OSTimeDlyResume()
INT8U OSTimeDlyResume (INT8U prio);
函数参数为任务优先级,前面已经说过,由于某个任务的优先级都是唯一的,因此,用来取消延迟而进入就绪状态。
核心代码就是:
ptcb->OSTCBDly == 0
OSTimeGet()获取系统时间
系统定义了一个INT32U类型的全局变量OSTime来记录系统发生的时钟节拍数。OSTime在应用程序调用OSStart()时被初始化为0,以后每发生1个时钟节拍,OSTime的值就被加1.
INT32U OSTimeGet(void);
返回值就是OSTime的值。
OSTimeSet()设置系统时间
void OSTimeSet(INT32U ticks);
ticks为OSTime的设置值(节拍数)。
参考资料
嵌入式实时操作系统uC/OS-II原理及应用 – 任哲