关闭

关于μCOS的7个问题

291人阅读 评论(0) 收藏 举报
分类:

1、   μC/OS-Ⅱ是怎样处理临界段代码的?

    μC/OS-Ⅱ为了处理临界段代码,避免同时有其它任务或中断服务进入临界段代码,因此需要关中断,处理完毕后再开中断。μC/OS-Ⅱ在文件 OS_CPU.H 中定义两个宏(macros)来关中断和开中断,以便避开不同 C 编译器厂商选择不同的方法来处理关中断和开中断。μC/OS-Ⅱ中的这两个宏调用分别是:OS_ENTER_CRITICAL() OS_EXIT_CRITICAL()。这两个宏的定义取决于所用的微处理器。

2、   什么是任务,怎样把用户的任务交给μC/OS-Ⅱ?

    一个任务看起来像其它 C 的函数,通常是一个无限的循环。这里要强调的是任务是没有返回值的,故返回参数必须定义成 void。还有一点,任务可以自我删除 ,当任务完成以后,任务可以自我删除,代码并非真的删除了,只是简单地不再理会这个任务了,这个任务的代码也不会再运行。

3、   任务是怎样调度的?

    μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个任务。 确定哪个任务优先级最高是由调度器(Scheduler)完成的,在程序代码中任务级的调度是由函数OSSched()完成的,中断级的调度是由另一个函数 OSIntExt()完成的。

void OSSched (void)

{

    INT8U y;

    OS_ENTER_CRITICAL();

    if ((OSLockNesting | OSIntNesting) == 0) {                               (1)

        y = OSUnMapTbl[OSRdyGrp];                                     (2)

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

        if (OSPrioHighRdy != OSPrioCur) {                                  (3)

            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];                  (4)

            OSCtxSwCtr++;                                            (5)

            OS_TASK_SW();                                            (6)

        }

    }

    OS_EXIT_CRITICAL();

}

    首先关中断,再有个判断条件:如果在中断服务子程序中调用 OSSched(),此时中断嵌套层数 OSIntNesting>0,或者由于用户至少调用了一次给任务调度上锁函数 OSSchedLock(),使OSLockNesting>0。如果不是在中断服务子程序调用 OSSched(),并且任务调度是允许的,即没有上锁,则任务调度函数将找出那个进入就绪态且优先级最高的任务[L3.8(2)],进入就绪态的任务在就绪任务表中有相应的位置位。一旦找到那个优先级最高的任务,OSSched()检验这个优先级最高的任务是不是当前正在运行的任务,以此来避免不必要的任务调度[L3.8(3)]

    注意,在μC/OS 中曾经是先得到 OSTCBHighRdy 然后和 OSTCBCur 做比较。因为这个比较是两个指针型变量的比较,在 8 位和一些 16 位微处理器中这种比较相对较慢。而在μC/OS-Ⅱ中是两个整数的比较。并且,除非用户实际需要做任务切换,在查任务控制块优先级表 OSTCBPrioTbl[]3-10时,不需要用指针变量来查 OSTCBHighRdy。综合这两项改进,即用整数比较代替指针的比较和当需要任务切换时再查表,使得μC/OS-Ⅱ比μC/OS  8 位和一些16 位微处理器上要更快一些。

为实现任务切换,OSTCBHighRdy 必须指向优先级最高的那个任务控制块 OS_TCB,这是通过将以OSPrioHighRdy为下标的OSTCBPrioTbl[]数组中的那个元素赋给OSTCBHighRdy来实现的[L3.8(4)]。接着,统计计数器 OSCtxSwCtr  1,以跟踪任务切换次数[L3.8(5)]

最后宏调用 OS_TASK_SW()来完成实际上的任务切换[L3.8(6)]。(任务切换很简单,由以下两步完成:将被挂起任务的微处理器寄存器推入堆栈,然后将较高优先级的任务的寄存器值从栈中恢复到寄存器中。在μC/OS-Ⅱ中,就绪任务的栈结构总是看起来跟刚刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,μC/OS-Ⅱ运行就绪态的任务所要做的一切, 只是恢复所有的 CPU 寄存器并运行中断返回指令。为了做任务切换,运行OS_TASK_SW(),人为模仿了一次中断。多数微处理器有软中断指令或者陷阱指令 TRAP 来实现上述操作。中断服务子程序或陷阱处理(Trap hardler ,也称作事故处理(exception handler ,必须提供中断向量给汇编语言函数 OSCtxSw()OSCtxSw()除了需要OS_TCBHighRdy 指向即将被挂起的任务,还需要让当前任务控制块 OSTCBCur 指向即将被挂起的任务,参见第 8 章,移植μC/OS-Ⅱ,有关于 OSCtxSw()的更详尽的解释。)

         OSSched()的所有代码都属临界段代码。在寻找进入就绪态的优先级最高的任务过程中,为防止中断服务子程序把一个或几个任务的就绪位置位,中断是被关掉的。为缩短切换时间,OSSched()全部代码都可以用汇编语言写。为增加可读性,可移植性和将汇编语言代码最少化,OSSched()是用写的。

4、   应用程序 CPU 的利用率是多少,μC/OS-Ⅱ是怎样知道的?

                  usage = (INT8S)(100L - 100L * run / OSIdleCtrMax);通过调用统计任务 OSTaskStat()知道的。

具体过程可以参考邵贝贝那本μC/OS-Ⅱ书第三章。

5、   怎样写中断服务子程序?

用户中断服务子程序:                                                            

    1、保存全部CPU寄存器;                                      (1)                       

2、调用OSIntEnterOSIntNesting直接加1                    (2) 

    3、执行用户代码做中断服务;                                  (3)                  

    4、调用OSIntEnter                                          (4)                     

    5、恢复所有CPU寄存器;                                     (5)                      

        6、执行中断返回指令;                                        (6)

以下列出 OSIntEnter OSIntEnter中断开始和推出函数代码。

通知μC/OS-Ⅱ,中断服务子程序开始了:

void OSIntEnter (void)

{

    OS_ENTER_CRITICAL();

    OSIntNesting++;

    OS_EXIT_CRITICAL();

}

通知μC/OS-Ⅱ,脱离了中断服务:

void OSIntExit (void)

{

    OS_ENTER_CRITICAL();                                        (1)

    if ((--OSIntNesting | OSLockNesting) == 0) {                        (2)

        OSIntExitY    = OSUnMapTbl[OSRdyGrp];                        (3)

        OSPrioHighRdy = (INT8U)((OSIntExitY << 3) +

                        OSUnMapTbl[OSRdyTbl[OSIntExitY]]);

        if (OSPrioHighRdy != OSPrioCur) {

            OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

            OSCtxSwCtr++;

            OSIntCtxSw();                                             (4)

        }

    }

    OS_EXIT_CRITICAL();

}

< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" /> 

6、   什么是时钟节拍,μC/OS-Ⅱ是怎样处理时钟节拍的?

    μC/OS 需要用户提供周期性信号源,用于实现时间延时和确认超时。而这个信号源所给其提供的时钟信号,时钟节拍源可以是专门的硬件定时器,也可以是来自 50/60Hz 交流电源的信号。

用户必须在多任务系统启动以后再开启时钟节拍器,也就是在调用 OSStart()之后。换

句话说,在调用 OSStart()之后做的第一件事是初始化定时器中断。μC/OS-Ⅱ中的时钟节拍服务是通过在中断服务子程序中调用 OSTimeTick()实现的。程序清单 L3.20 时钟节拍中断服务子程序的示意代码:

void OSTickISR(void)

{

    保存处理器寄存器的值;

    调用OSIntEnter()或是将OSIntNesting1;

    调用OSTimeTick();

 

    调用OSIntExit();

    恢复处理器寄存器的值;

    执行中断返回指令;

}

7、   μC/OS-Ⅱ是怎样初始化的,以及怎样启动多任务?

先调用系统初始化函数OSIint(),初始化μC/OS-Ⅱ所有的变量和数据结构(见 OS_CORE.C)。详细讲解请参看邵贝贝编写的μC/OS-Ⅱ书中第三章相关内容。

多任务的启动是用户通过调用 OSStart()实现的。值得注意的是:启动μC/OS-Ⅱ之前,用户至少要建立一个应用任务。以下是一个程序清单:

void main (void)

{

    OSInit();           /* 初始化uC/OS-II  */

    .

    .

    通过调用OSTaskCreate()OSTaskCreateExt()创建至少一个任务;

    .

    .

    OSStart();          /* 开始多任务调度!OSStart()永远不会返回 */

}

OSStart()的代码如程序清单:

void OSStart (void)

{

    INT8U y;

    INT8U x;

 

    if (OSRunning == FALSE) {

        y             = OSUnMapTbl[OSRdyGrp];

        x             = OSUnMapTbl[OSRdyTbl[y]];

        OSPrioHighRdy = (INT8U)((y << 3) + x);

        OSPrioCur     = OSPrioHighRdy;

        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];                (1)

        OSTCBCur      = OSTCBHighRdy;

        OSStartHighRdy();                                          (2)

    }

}

         OSStart()从任务就绪表中找出那个用户建立的优先级最高任务的任务控制块[L3.25(1)]。然后,OSStart()调用高优先级就绪任务启动函数 OSStartHighRdy()[L3,25(2)](见汇编语言文件OS_CPU_A.ASM),这个文件与选择的微处理器有关。实质上,函数 OSStartHighRdy()是将任务栈中保存的值弹回到 CPU 寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码。注意OSStartHighRdy()将永远不返回到 OSStart()。下面是调用OSStart()之后的数据结构图:

2011年10月02日 - a232346261 - 弓手称菜

(以上是自己针对邵贝贝μC/OS-Ⅱ第三章七个问题的分析,大部分内容均摘自于那本书上)

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:15042次
    • 积分:213
    • 等级:
    • 排名:千里之外
    • 原创:1篇
    • 转载:27篇
    • 译文:0篇
    • 评论:0条
    文章分类