uC/OS-II的时钟

前言

任何操作系统都需要一个周期性的信号源,以供系统处理延迟、超时等与时间有关的事件;这个周期性的信号源叫做时钟。也就是系统的“心跳”。

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,那么就将对应的任务置为就绪状态。

OSTimeTick分析

源码如下:

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原理及应用 – 任哲

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值