UCOS你问我答系列之系统时钟节拍详解

前言

系统时钟节拍是多任务得以正常运行的基石,UCOS的系统时钟节拍一般依赖于MCU的硬件定时器.硬件定时器产生固定时间间隔的中断,中断中调用UCOS的系统函数,完成多任务操作系统的基本调度功能.

本文使用的UCOS版本:V2.91.

代码分析讲解

在本例中我们设定uCOS的时钟频率为100HZ,即10ms进入一次系统时钟中断.               

#define OS_TICKS_PER_SEC 100u
每10ms执行的系统时钟中断相关函数如下:

void SysTick_Handler(void)
{   
    // 主要作用是累加OSIntNesting值, 标识当前的中断桥套层数. 
    // OSIntNesting > 0时表示当前处于中断处理函数中. 
    OSIntEnter();
    // 主要作用是累加OSTime的值,并遍历任务链表,对于OSTCBDly>0的任务,对其OSTCBDly进行减1操作,并将就绪任务的优先级放入任务就绪表.
    OSTimeTick();                  
    // OSIntNesting进行减一操作, 并找出优先级最高的任务,进行一次任务调度.
    OSIntExit();            
}

下面对相关函数的具体内容进行进一步讲解,先来分析OSTimeTick函数,主要思路流程在代码内以注释的形式体现.

// 对于此函数的分析仅保留了相关的核心代码
void  OSTimeTick (void)
{
#if OS_TIME_GET_SET_EN > 0u
    OS_ENTER_CRITICAL();                                   /* Update the 32-bit tick counter               */
    // 累加OSTime值, 此值可用于标识系统运行时间.
    OSTime++;   
    OS_EXIT_CRITICAL();
#endif
    // 确保ucos已经开始调度后才执行下面的代码.
    if (OSRunning == OS_TRUE) {
        ptcb = OSTCBList;                                  /* Point at first TCB in TCB list   */
        /*
            遍历任务链表,对于OSTCBDly不为0的任务进行减一操作,如果减一后为0,判断任务是否还在因等待某个事件而挂起,
            如果是,则将任务的OSTCBStatPend设置为OS_STAT_PEND_TO.否则设置为OS_STAT_PEND_OK.
            判断任务是否执行了任何挂起操作,如果没有,将此优先级的任务放入就绪表.
        */

        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();
        }
    }
}

OSTimeTick最最核心的作用就是,递减每个任务的OSTCBDly值,并将已就绪任务的优先级,放入就绪表. 
OSTimeTick函数本身并不会触发任务调度,那我们的任务调度在哪里触发呢?且看下面的OSIntExit()函数的分析.

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr = 0u;
#endif
    /*
        OSIntExi函数一般在中断中配合OSIntEnter成对出现, 其主要作用是执行完中断处理函数且无嵌套中断时,
        判断一下是否有更高优先级的任务就绪了,如果有,执行任务调度.转去运行更高优先级的任务.如果没有,继续回到
        之前被中断打断的那个任务去运行.
    */
    if (OSRunning == OS_TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0u) {                           /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if (OSIntNesting == 0u) {                          /* Reschedule only if all ISRs complete ... */
            if (OSLockNesting == 0u) {                     /* ... and not locked.                      */
                OS_SchedNew();
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
                if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0u
                    OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task  */
#endif
                    OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */
                    OSIntCtxSw();                          /* Perform interrupt level ctx switch       */
                }
            }
        }
        OS_EXIT_CRITICAL();
    }
}

总结

至此我们对于CPU产生系统时钟中断时所做的事情就比较清晰了.
总结一下就是: 遍历任务,递减其延时值.找到延时值降至0的任务将其优先级放入就绪表.在退出中断时判断是否需要进行任务 调度,并根据需要进行任务调度.

问答环节

1.问: 假设一共创建了两个任务,某一时刻都处于delay状态,这时退出系统时钟中断时如何调度.
答: UCOS默认创建了了一个最低优先级的系统任务OS_TaskIdle,这个任务永远处于就绪状态. 在没有其他任务就绪时,会调度OS_TaskIdle这个任务.

2.问: 假定有两个任务,A任务优先级为1,B任务优先级为2.简述整个软件大致运行流程:

void taskA(void *pdata)
{
    while(1)
    {
        Led_Flip();
        OSTimeDly(100);  
    }                                    
}
void taskB(void *pdata)
{
    while(1)
    {
        serial_print();
        OSTimeDly(500);  
    }                                    
} 

答: 任务创建完成,调用OSStart后,最高优先级的任务A首先运行,LED状态翻转.然后调用OSTimeDly函数, 设置任务A的OSTCBDly值为100, 并且将任务A的优先级从就绪表移除,触发一次任务调度.此时任务B是就绪表 中优先级最高的任务.CPU调度到任务B开始运行B任务.执行serial_print()函数.然后调用OSTimeDly(500); 设置任务B的OSTCBDly值为500, 并且将任务B的优先级从就绪表移除,触发一次任务调度.此时任务A、B都不处于 就绪状态,CPU调度到最低优先级的任务OS_TaskIdle运行.在上述的任何过程中,都可能会触发系统时钟中断, 触发系统时钟中断时,OSTimeTick函数会判断任务A 任务B是否已进入任务延时,对于已经进入任务延时的任务, 递减其OSTCBDly值.如果有任务OSTCBDly减至0,就将其任务优先级放入就绪表.OSIntExit函数找到就绪表中优先级 最高的任务,进行调度.

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值