现在的位置: 首页 > 综合 > 正文

uC/OS-II源码分析(总体思路 三)

2014年11月09日 ⁄ 综合 ⁄ 共 4742字 ⁄ 字号 评论关闭

 

OSTimeDly

 

在Task中,一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行,OSTimeDly将本进程从Ready TCBList中删除,然后将Delay的时间设置给OSTCBDly,最后调用OS_Sched进行进程调度。

void  OSTimeDly (INT16U ticks)

{

    INT8U      y;

  

    if (ticks > 0) {                            

        OS_ENTER_CRITICAL();

                  OSTCBCur->OSTCBY;       

        OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

        if (OSRdyTbl[y] == 0) { 

            OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

        }

        OSTCBCur->OSTCBDly = ticks;             

        OS_EXIT_CRITICAL();

        OS_Sched();                             

    }

}

如果ticks为零,说明不需延迟,则什么事情都不做。否则,调用OS_ENTER_CRITICAL进入临界段,将本进程从Ready TCBList中删除的代码如下:

                  OSTCBCur->OSTCBY;       

        OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

        if (OSRdyTbl[y] == 0) { 

            OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

        }

y为当前进程所在Group,OSRdyTbl[y]为该Group所在字节,&=~则将该字节中本进程所占用的Bit清零。如果OSRdyTbl[y]为0,则说明这个Group中没有进程处于Ready状态,则将OSRdyGrp中该Group所占用的Bit清零。

然后将ticks保存在OSTCBDly中,每次OSTimeTick运行时会将这个值减一直至为零。

调用OS_EXIT_CRITICAL离开临界段,紧接着调用OS_Sched进入调度例程。

 

OS_Sched

 

OS_Sched是进程调度所使用的函数,在这里面找到最高优先级的进程,然后切换到该进程运行。

void  OS_Sched (void)

{

    INT8U      y;

    OS_ENTER_CRITICAL();

    if (OSIntNesting == 0) {                          

        if (OSLockNesting == 0) {                     

                       = OSUnMapTbl[OSRdyGrp];     

            OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

            if (OSPrioHighRdy != OSPrioCur) {         

                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

                OSCtxSwCtr++;                         

                OS_TASK_SW();                         

            }

        }

    }

    OS_EXIT_CRITICAL();

}

OS_Sched不允许在中断嵌套中调用,因此先判断是否是中断嵌套,并且是否限制进程调度,这两个条件都满足之后,找到最高优先级的进程,如果这个进程不是当前进程,则将新的进程TCB指针保存到OSTCBHighRdy中,为调度计数器OSCtxSwCtr加一,然后调用宏OS_TASK_SW()进行切换。

OS_TASK_SW()宏也是一个自定义的宏,uC/OS-II推荐使用软中断方式实现。

OSCtxSw是一个中断响应函数,一般我们在初始化时将这个软终端和OSCtxSw挂接好。在OSCtxSw中所需要做的事情就是将当前寄存器的值保存到当前堆栈中,然后切换堆栈到新进程的堆栈,将寄存器的值出栈,然后调用中断返回指令IRET就返回到新进程中断前的地方继续执行了。

 

定时中断

 

uC/OS-II的定时中断必须在OSStart之后初始化,而不能在OSStart之前,因为害怕第一个TimeTick发生时第一个进程还没有开始运行,而这时uC/OS是处于不可预期状态,会导致死机。

因此对于定时中断,我一般是放在最高级进程的初始化中进行,然后将定时中断和OSTickISR挂接。

OSTickISR也是一个用户自定义函数,所要完成的功能一个是保存当前的寄存器到当前堆栈将OSIntNesting加一,然后调用uC/OS提供的OSTimeTick函数,然后调用OSIntExit()将OSIntNesting减一,最后将各寄存器值出栈,使用中断返回指令IRET返回。

OSTimeTick在每个时钟中断中被调用一次,在该函数中会更新各个进程TCB所对应的OSTCBDly,如果该OSTCBDly减为0,则对应的TCB就被放入Ready TCBList中。

    OS_ENTER_CRITICAL();                                  

    OSTime++;

    OS_EXIT_CRITICAL();

   

            ptcb = OSTCBList;                                 

        while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {         

            OS_ENTER_CRITICAL();

            if (ptcb->OSTCBDly != 0) {                    

                if (--ptcb->OSTCBDly == 0) {              

                                                          

                    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

                        ptcb->OSTCBStat   &= ~OS_STAT_PEND_ANY;               

                        ptcb->OSTCBPendTO  = TRUE;                            

                    } else {

                        ptcb->OSTCBPendTO  = FALSE;

                    }

                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { 

                        OSRdyGrp              
|= ptcb->OSTCBBitY;            

                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

                    }

                }

            }

            ptcb = ptcb->OSTCBNext;                       

            OS_EXIT_CRITICAL();

        }

首先在临界段将OSTime加一,然后遍历整个非Free的TCBList,如果OSTCBDly不为0,则,将OSTCBDly减一,如果这时OSTCBDly为0,而且TCB对应的进程需要等待任何信号量或Event等,则说明超时时间到了,将当前TCB的State中OS_STAT_PEND_ANY位去掉,然后将OSTCBPendTo设置为TRUE,表示这是PEND的超时,否则设置OSTCBPendTO为FALSE。

如果OSTCBDly减为零,且该进程没有Suspend,则将该进程放入Ready TCBList中,使用方法同TaskCreate中的方法。

然后我们来说说OSIntExit这个函数。该函数代码如下:

void  OSIntExit (void)

{

    INT8U      y;

 

    if (OSRunning == TRUE) {

        OS_ENTER_CRITICAL();

        if (OSIntNesting > 0) {                           

            OSIntNesting--;

        }

        if (OSIntNesting == 0) {                          

            if (OSLockNesting == 0) {                     

              = OSUnMapTbl[OSRdyGrp];         

                OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

                if (OSPrioHighRdy != OSPrioCur) {         

                    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

                    OSCtxSwCtr++;                         

                    OSIntCtxSw();                         

                }

            }

        }

 OS_EXIT_CRITICAL();

    }

}

首先判断OSRunning是否为1,也就是OS是否在运行,当然没有运行就什么都不做。

然后将OSIntNesting减一,这个是需要在临界段进行的。如果OSIntNesting减为零,并且没有限制进程切换,则找到当前最高优先级的进程(方法同OS_Sched()),然后调用OSIntCtxSw进行进程切换。

OSIntCtxSw()是用户自定义函数,该函数的主要功能与OSCtxSw类似,只是需要对当前的堆栈进行稍微的调整,将OSIntExit和OSIntCtxSw调用所需要的堆栈去掉,然后做的和OSCtxSw一样。

在实际的Porting中发现要去掉OSIntExit和OSIntCtxSw调用所占用的堆栈还是比较麻烦的,因此我就现在OSTickISR刚开始的时候保存好现场之后就将堆栈指针赋给当前进程TCB的OSStkPtr,这样,在OSIntCtxSw中就不需要重新对当前堆栈的值进行保存,只需进行切换就可以了

抱歉!评论已关闭.