加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【ucos-III教程】第11章 uCOS-III内核函数分析(下)

(2014-12-29 13:00:22)
标签:

ucos-iii

emwin

ucgui

dsp

示波器

分类: uC0S-III


特别说明:
1.  本教程是安富莱电子原创。
2.  安富莱STM32F407开发板资料已经全部开源,开源地址:地址链接
3.  当前共配套300多个实例,4套用户手册。

第11章   uCOS-III内核函数分析(下)

本期教程开始分析μCOS-III的内核函数,源码的分析采用先对源码进行注释,然后讲解函数实现的功能和相关的原理分析,最后是举一个例子(如果这个函数是供外部函数调用的)。内核函数很重要,是学习任务管理,任务间通信机制的基础。希望初学的同学认真学习,这部分应该算是μCOS-III的核心代码。

11.1 系统配置文件

11.2 源码文件

11.3 μCOS-III初始化

11.4 μCOS-III启动

11.5 获取系统版本

11.6 空闲任务

11.7 临界段

11.8 安全关键IEC61508

11.9 任务切换

11.10 调度锁

11.11 Round-Robin调度

11.12 总结

11.10  调度锁

给调度器加锁后将禁止任务调度,直到任务完成后,调用调度器解锁函数才能重新开始任务调度。这里一定要明白一点,调度锁只是将调度器关闭,并不影响中断的执行,该进中断还是要进的,只是不会执行任务切换。

11.10.1       调度器加锁OSSchedLock()

 

 

void  OSSchedLock (OS_ERR  *p_err)

{

    CPU_SR_ALLOC();

 

 

 

#ifdef OS_SAFETY_CRITICAL

    if (p_err == (OS_ERR *)0) {

        OS_SAFETY_CRITICAL_EXCEPTION();

        return;

    }

#endif

 

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u

    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {                  (1)

       *p_err = OS_ERR_SCHED_LOCK_ISR;

        return;

    }

#endif

 

    if (OSRunning != OS_STATE_OS_RUNNING) {      

       *p_err = OS_ERR_OS_NOT_RUNNING;

        return;

    }

 

    if (OSSchedLockNestingCtr >= (OS_NESTING_CTR)250u) {         (2)

       *p_err = OS_ERR_LOCK_NESTING_OVF;

        return;

    }

 

    CPU_CRITICAL_ENTER();

    OSSchedLockNestingCtr++;                                                       (3)

#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u

    OS_SchedLockTimeMeasStart();                                                                     (4)

#endif

    CPU_CRITICAL_EXIT();

   *p_err = OS_ERR_NONE;

}

1.     这里表示不允许在中断服务程序中调用调度锁函数。

2.     调度锁的嵌套次数范围0~250,用户不能超出250。

3.     嵌套次数加一。

4.     开启调度锁时间测量,这里是记录一下起始时间。

11.10.2       调度器解锁OSSchedUnlock

 

 

void  OSSchedUnlock (OS_ERR  *p_err)

{

    CPU_SR_ALLOC();

 

 

 

#ifdef OS_SAFETY_CRITICAL

    if (p_err == (OS_ERR *)0) {

        OS_SAFETY_CRITICAL_EXCEPTION();

        return;

    }

#endif

 

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u

    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {     

       *p_err = OS_ERR_SCHED_UNLOCK_ISR;

        return;

    }

#endif

 

    if (OSRunning != OS_STATE_OS_RUNNING) {        

       *p_err = OS_ERR_OS_NOT_RUNNING;

        return;

    }

 

    if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) {  

       *p_err = OS_ERR_SCHED_NOT_LOCKED;

        return;

    }

 

    CPU_CRITICAL_ENTER();

    OSSchedLockNestingCtr--;                                                   (1)

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {

        CPU_CRITICAL_EXIT();                           

       *p_err = OS_ERR_SCHED_LOCKED;

        return;

    }

 

#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u

    OS_SchedLockTimeMeasStop();

#endif

 

    CPU_CRITICAL_EXIT();                                 

    OSSched();                                                                      (2)

   *p_err = OS_ERR_NONE;

}

1.     调度锁嵌套计数减一。

2.     调度器被锁期间,有些任务可能会就绪,所以这里加上了调度函数。

11.10.3       调度器被锁时间测量OS_SchedLockTimeMeas

函数比较简单,这里就直接将内容贴上,特别注意函数的注释。

 

 

#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u

void  OS_SchedLockTimeMeasStart (void)

{

    if (OSSchedLockNestingCtr == 1u) {

        OSSchedLockTimeBegin = CPU_TS_TmrRd();

    }

}

 

void  OS_SchedLockTimeMeasStop (void)

{

    CPU_TS_TMR  delta;

 

 

    if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) {   

        delta = CPU_TS_TmrRd()                          

              - OSSchedLockTimeBegin;

        if (OSSchedLockTimeMax    < delta) {            

            OSSchedLockTimeMax    = delta;

        }

        if (OSSchedLockTimeMaxCur < delta) {             

            OSSchedLockTimeMaxCur = delta;

        }

    }

}

#endif

11.10.4       函数使用举例

下面这个例子是为了防止截屏的过程中被其它任务打断,加入了调度锁功能。

 

static void AppTaskGUIUpdate(void *p_arg)

{

     OS_ERR        err;

     uint8_t       Pic_Name = 0;

     char buf[20];

     CPU_BOOLEAN SemFlag;

 

     (void)p_arg;

          

     while(1)

      

         SemFlag = BSP_OS_SemWait(&SEM_SYNCH, 0);

         if(SemFlag == DEF_OK)

          

              sprintf(buf,"0:/Picture/%d.bmp",Pic_Name);

              OSSchedLock(&err);

              GUI_SaveBMP(0, 0, LCD_GetXSize(), LCD_GetYSize(),buf);

              OSSchedUnlock(&err);       

                                                                                                                         

     

}

11.11  Round-Robin调度

μCOS-III中的时间片调度功能做的很完善,支持全局的时间片设置,也支持每个任务的单独设置。关于时间片调度,咱们在前几期教程也有讲解。

11.11.1       配置参数OSSchedRoundRobinCfg()

 

 

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u

void  OSSchedRoundRobinCfg (CPU_BOOLEAN   en,

                            OS_TICK       dflt_time_quanta,

                            OS_ERR       *p_err)

{

    CPU_SR_ALLOC();

 

 

#ifdef OS_SAFETY_CRITICAL

    if (p_err == (OS_ERR *)0) {

        OS_SAFETY_CRITICAL_EXCEPTION();

        return;

    }

#endif

 

    CPU_CRITICAL_ENTER();                                                                        (1)

    if (en != DEF_ENABLED) {                                                                     (2)

        OSSchedRoundRobinEn = DEF_DISABLED;

    } else {

        OSSchedRoundRobinEn = DEF_ENABLED;

    }

 

    if (dflt_time_quanta > (OS_TICK)0) {                                                         (3)

        OSSchedRoundRobinDfltTimeQuanta = dflt_time_quanta;

    } else {

        OSSchedRoundRobinDfltTimeQuanta = (OS_TICK)(OSCfg_TickRate_Hz / (OS_RATE_HZ)10);

    }

    CPU_CRITICAL_EXIT();

   *p_err = OS_ERR_NONE;

}

#endif

1.     由于这几个参数是全局变量,所以必须关闭中断。

2.     根据形参设置是否使能时间片调度。

3.     变量OSSchedRoundRobinDfltTimeQuanta是用来设置默认的时间片个数,也就是说,如果程序中没有单独配置任务的时间片个数,就会使用这个默认时间片个数。

11.11.2       放弃剩余时间片OSSchedRoundRobinYield ()

这个函数的主要功能就是任务在完成工作的情况下,如果还有剩余的时间片,可以放弃这些时间去执行另外的同优先级任务(切记,是另外的同优先级任务

 

 

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u

void  OSSchedRoundRobinYield (OS_ERR  *p_err)

{

    OS_RDY_LIST  *p_rdy_list;

    OS_TCB       *p_tcb;

    CPU_SR_ALLOC();

 

 

 

#ifdef OS_SAFETY_CRITICAL

    if (p_err == (OS_ERR *)0) {

        OS_SAFETY_CRITICAL_EXCEPTION();

        return;

    }

#endif

 

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u

    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {     

       *p_err = OS_ERR_YIELD_ISR;

        return;

    }

#endif

 

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { 

       *p_err = OS_ERR_SCHED_LOCKED;

        return;

    }

 

    if (OSSchedRoundRobinEn != DEF_TRUE) {           

       *p_err = OS_ERR_ROUND_ROBIN_DISABLED;

        return;

    }

 

    CPU_CRITICAL_ENTER();

    p_rdy_list = &OSRdyList[OSPrioCur];                               (1)

    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {

        CPU_CRITICAL_EXIT();

       *p_err = OS_ERR_ROUND_ROBIN_1;

        return;

    }

 

    OS_RdyListMoveHeadToTail(p_rdy_list);                           (2)

    p_tcb = p_rdy_list->HeadPtr;                       

    if (p_tcb->TimeQuanta == (OS_TICK)0) {                              (3)

        p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;

    } else {

        p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;      

    }

 

    CPU_CRITICAL_EXIT();

 

    OSSched();                                                                       (4)

   *p_err = OS_ERR_NONE;

}

#endif

1.     获取此优先级的就绪链表。从而得到此优先级下任务的个数,如果同优先级下只有一个任务,将退出这个函数。

2.     移动同优先级就绪链表中任务的位置,从实现同优先级下任务的切换。

3.     参数p_tcb->TimeQuanta = 0的时候就会使用默认的时间片个数,如果非0,就会给这个任务的时间片计数器赋予相应的时间片个数。

4.     执行任务调度。

11.11.3       Round-Robin调度算法OS_SchedRoundRobin ()

当多个任务有相同的优先级时,μCOS-III允许任务在切换到另一个任务前运行特定的时间,也就是大家常说的时间片。这个过程就是Round-Robin调度或者时间片调度。如果任务不需要将所有的时间片用完,可以调用上面讲的函数OSSchedRoundRobinYield (),放弃剩余时间片从而切换到同优先级的另一个任务。μCOS-III支持用户在系统运行过程中使能或者禁止时间片调度,同时也支持全局的时间片设置,也支持每个任务的单独设置。

为了更好的说明Round-Robin调度算法,下面举一个例子(截图来自官方书籍):Task #1,Task #2,Task #3都运行在优先级 X,任务运行的时间片个数都是4。

 【ucos-III教程】第11章 <wbr>uCOS-III内核函数分析(下)


1.     一开始是Task #3在运行,运行期间每个嘀嗒定时器中断都会让Task #3的时间片计数减一。

2.     第四次进入嘀嗒定时器中断后,Task #3的4个时间片已经用完。

3.     切换到同优先级就绪链表中下一个任务Task #1。

4.     Task #1开始运行直到时间片用完。

5.     切换到Task #3运行。

6.     Task #3运行一段时间后,调用函数OSSchedRoundRobinYield ()放弃剩余时间片。

7.     切换到Task #1运行。

8.     这里要特别注意:Task #1会运行4个时间片,图片上面画的不是很准确。

有了上面基础后,在解析一下相关函数。

 

 

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u

void  OS_SchedRoundRobin (OS_RDY_LIST  *p_rdy_list)

{

    OS_TCB   *p_tcb;

    CPU_SR_ALLOC();

 

 

 

    if (OSSchedRoundRobinEn != DEF_TRUE) {                       (1)

        return;

    }

 

    CPU_CRITICAL_ENTER();

    p_tcb = p_rdy_list->HeadPtr;                                  

 

    if (p_tcb == (OS_TCB *)0) {                                                                      (2)

        CPU_CRITICAL_EXIT();

        return;

    }

 

    if (p_tcb == &OSIdleTaskTCB) {                                                                   (3)

        CPU_CRITICAL_EXIT();

        return;

    }

 

    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {                                     (4)

        p_tcb->TimeQuantaCtr--;

    }

 

    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {                                  (5)

        CPU_CRITICAL_EXIT();

        return;

    }

 

    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {                     (6)

        CPU_CRITICAL_EXIT();                          

        return;

    }

 

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {           (7)

        CPU_CRITICAL_EXIT();

        return;

    }

 

    OS_RdyListMoveHeadToTail(p_rdy_list);                     (8)

    p_tcb = p_rdy_list->HeadPtr;                      

    if (p_tcb->TimeQuanta == (OS_TICK)0) {                         (9)

        p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;

    } else {

        p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;      

    }

    CPU_CRITICAL_EXIT();

}

#endif

1.     检测Round-Robin调度是否使能。

2.     确保此优先级下存在任务。

3.     这句话的意思是说:不允许用户将应用任务的优先级设置的和空闲任务优先级一样,也就是说空闲任务的优先级下不能有其它任务。

4.     时间片减一。

5.     任务的时间片还没有用完,退出继续执行。

6.     同优先级下必须有两个及其以上的任务才能继续往下执行。

7.     如果调度器被锁,不能执行Round-Robin调度。

8.     通过调整同优先级下的就绪链表获得下一个要执行的任务。

9.     判断使用全局的默认时间片个数还是使用单独设计的时间片个数。

11.11.4       函数使用举例

 

 

int main(void)

{

    OS_ERR  err;

   

OSInit(&err); 

                                            

    

    OSSchedRoundRobinCfg(DEF_ENABLED,

                             8,

                             &err); 

 

     OSTaskCreate((OS_TCB       *)&AppTaskStartTCB,           

                 (CPU_CHAR     *)"App Task Start",

                 (OS_TASK_PTR   )AppTaskStart,

                 (void         *)0,

                 (OS_PRIO       )APP_CFG_TASK_START_PRIO,

                 (CPU_STK      *)&AppTaskStartStk[0],

                 (CPU_STK_SIZE  )APP_CFG_TASK_START_STK_SIZE / 10,

                 (CPU_STK_SIZE  )APP_CFG_TASK_START_STK_SIZE,

                 (OS_MSG_QTY    )0,

                 (OS_TICK       )0,

                 (void         *)0,

                 (OS_OPT        )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),

                 (OS_ERR       *)&err);

 

OSStart(&err);  

                                                 

    return (0);

}

 

 

 

 

static void AppTaskCOM(void *p_arg)

  

     OS_ERR  err;

 

(void)p_arg;

    

      

     while(1)

     {

          ……

         OSSchedRoundRobinYield (&err);

         ……

     }

                                                                                                      

  

}

11.12  总结

本期教程涉及到的内容较多,如果是初学的,一定要多花点时间消耗下,如果学习过程中遇到很多问题,不要担心,随着后面教程的进行会理解的更深刻。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有