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

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

(2014-12-29 12:39:41)
标签:

stm32f7

ucos-iii

ucgui

emwin

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.6  空闲任务

几乎所有的小型RTOS中都会有一个空闲任务,空闲任务应该属于系统任务,是必须要执行的,用户程序不能将其关闭。不光小型系统中有空闲任务,大型的系统里面也有的,比如XP,下面的截图就是XP中的空闲进程。

 

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

 

 

空闲任务主要有以下几个作用:

l  我们不能让系统一直在执行各个应用任务,这样的话系统利用率就是100%,系统就会一直的超负荷运行,所以空闲任务很有必要。

l  为了更好的实现低功耗,空闲任务也很有必要,我们可以在空闲任务中实现睡眠,待机等低功耗措施。

11.6.1       创建空闲任务OS_IdleTaskInit ()

 

 

void  OS_IdleTaskInit (OS_ERR  *p_err)

{

#ifdef OS_SAFETY_CRITICAL

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

        OS_SAFETY_CRITICAL_EXCEPTION();

        return;

    }

#endif

 

    OSIdleTaskCtr = (OS_IDLE_CTR)0;

                                           

    OSTaskCreate((OS_TCB     *)&OSIdleTaskTCB,

                 (CPU_CHAR   *)((void *)"uC/OS-III Idle Task"),

                 (OS_TASK_PTR)OS_IdleTask,

                 (void       *)0,

                 (OS_PRIO     )(OS_CFG_PRIO_MAX - 1u),

                 (CPU_STK    *)OSCfg_IdleTaskStkBasePtr,

                 (CPU_STK_SIZE)OSCfg_IdleTaskStkLimit,

                 (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,

                 (OS_MSG_QTY  )0u,

                 (OS_TICK     )0u,

                 (void       *)0,

                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),

                 (OS_ERR     *)p_err);

}

关于任务的创建,本期教程暂时还不做介绍。

11.6.2       空闲任务OS_IdleTask ()

 

 

void  OS_IdleTask (void  *p_arg)

{

    CPU_SR_ALLOC();                                                                               1

 

 

 

    p_arg = p_arg;                                                            2

 

    while (DEF_ON) {                                                                              3

        CPU_CRITICAL_ENTER();

        OSIdleTaskCtr++;                                                                          4

#if OS_CFG_STAT_TASK_EN > 0u

        OSStatTaskCtr++;                                                                          5

#endif

        CPU_CRITICAL_EXIT();

 

        OSIdleTaskHook();                                                       6

    }

}

1.     函数CPU_SR_ALLOC()是为CPU_CRITICAL_ENTER()CPU_CRITICAL_EXIT()申请一个变量:

#define  CPU_SR_ALLOC()           CPU_SR  cpu_sr = (CPU_SR)0

这个是临界代码段,在下面一个小节有详细讲解。

2.     这样做是为了防止编译器警告。

3.     μCOS-III中主要有以下布尔宏定义:

宏定义

数值

DEF_FALSE

0u

DEF_TRUE

1u

DEF_NO

0u

DEF_YES

1u

DEF_DISABLED

0u

DEF_ENABLED

1u

DEF_INACTIVE

0u

DEF_ACTIVE

1u

DEF_INVALID

0u

DEF_VALID

1u

DEF_OFF 

0u

DEF_ON

1u

DEF_CLR

0u

DEF_SET 

1u

DEF_FAIL

0u

DEF_OK

1u

4.     空闲任务计数。

5.     统计任务计数,这两个计数都是为了计算CPU利用率。

 

6.     用户可以在钩子函数中加入待机,休眠等低功耗操作。

11.7  临界段

11.7.1       临界段基本概念

代码的临界段也称为临界区,指处理时不可分割的代码。一旦这部分代码开始执行,则不允许任何中断打入。为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。

从代码上来看,处在关中断和开中断之间的代码段就是临界段。

由于各厂商的CPUC编译器的关中断和开中断的方法以及指令不尽相同,为增强μCOS-III的可移植性(即在μCOS-III的各个C语言函数中尽可能地不出现汇编语言代码),μCOS-IIICPU_INT_DIS()CPU_INT_EN()这两个宏封装了与系统硬件相关的关中断和开中断指令。

另外,不要在临界段中调用μCOS-III提供的功能函数,防止系统崩溃。

11.7.2       临界段相关的宏定义

CPU_INT_DIS()CPU_INT_EN()可以以下四种不同的实现方法。

#define  CPU_CRITICAL_METHOD_NONE                  0u 

#define  CPU_CRITICAL_METHOD_INT_DIS_EN            1u 

#define  CPU_CRITICAL_METHOD_STATUS_STK            2u 

#define  CPU_CRITICAL_METHOD_STATUS_LOCAL          3u 

        至于在实际应用时使用哪种方法,取决于用户使用的处理器和C编译器。用户可以通过cpu.h文件中的宏定义进行选择:

#define  CPU_CFG_CRITICAL_METHOD    CPU_CRITICAL_METHOD_STATUS_LOCAL

 

typedef  CPU_INT32U                 CPU_SR;

 

#if  (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)

#define  CPU_SR_ALLOC()             CPU_SR  cpu_sr = (CPU_SR)0

#else

#define  CPU_SR_ALLOC()

#endif

 

 

 

#define  CPU_INT_DIS()         do { cpu_sr = CPU_SR_Save(); } while (0)                                (1)

#define  CPU_INT_EN()          do { CPU_SR_Restore(cpu_sr); } while (0)                                (2)

 

#ifdef   CPU_CFG_INT_DIS_MEAS_EN

 

#define  CPU_CRITICAL_ENTER()  do { CPU_INT_DIS();         \

CPU_IntDisMeasStart(); }  while (0)

 

#define  CPU_CRITICAL_EXIT()   do { CPU_IntDisMeasStop();  \

CPU_INT_EN();          while (0)

 

#else

 

#define  CPU_CRITICAL_ENTER()  do { CPU_INT_DIS(); } while (0)

#define  CPU_CRITICAL_EXIT()   do { CPU_INT_EN();  } while (0)

#endif

1.     CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_INT_DIS_EN

这种方式最简单,即直接使用处理器的开中断和关中断指令来实现宏。但是不推荐使用这种方式,因为不支持中断嵌套,但是考虑到有些处理器或者编译器仅支持这种方式,不得不选择这种方式。

2.     CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_INT_DIS_EN

这种方法稍复杂些,但可使CPU中断使能标志的状态在临界段前和临界段后不发生变化。

进入临界段前:

  (1) Push/save   中断状态保存到堆栈中

   (2) Disable     关闭中断

退出临界段:

 (3) Pop/restore  恢复中断标志

3.     CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL

这种方法的前提是,用户使用的C编译器具有扩展功能。用户可获得程序状态字的值,这样就可把该值保存在C函数的局部变量中,而不必压到堆栈里。上面的宏定义就是采用的这种方式,也就是(1),(2)注释的地方。

4.     关于临界段,在文件os.h中也有几个相关的宏定义,这几个宏定义的含义会在后面跟大家讲

#if      OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u && defined(CPU_CFG_INT_DIS_MEAS_EN)

#define  OS_SCHED_LOCK_TIME_MEAS_START()    OS_SchedLockTimeMeasStart()

#else

#define  OS_SCHED_LOCK_TIME_MEAS_START()

#endif

 

 

#if      OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u && defined(CPU_CFG_INT_DIS_MEAS_EN)

#define  OS_SCHED_LOCK_TIME_MEAS_STOP()     OS_SchedLockTimeMeasStop()

#else

#define  OS_SCHED_LOCK_TIME_MEAS_STOP()

#endif

 

#if OS_CFG_ISR_POST_DEFERRED_EN > 0u                      

                                                          

#define  OS_CRITICAL_ENTER()                                       \

         do {                                                      \

             CPU_CRITICAL_ENTER();                                 \

             OSSchedLockNestingCtr++;                              \

             if (OSSchedLockNestingCtr == 1u) {                    \

                 OS_SCHED_LOCK_TIME_MEAS_START();                  \

                                                                \

             CPU_CRITICAL_EXIT();                                  \

         } while (0)

                                                          

#define  OS_CRITICAL_ENTER_CPU_EXIT()                              \

         do {                                                      \

             OSSchedLockNestingCtr++;                              \

                                                                   \

             if (OSSchedLockNestingCtr == 1u) {                    \

                 OS_SCHED_LOCK_TIME_MEAS_START();                  \

                                                                \

             CPU_CRITICAL_EXIT();                                  \

         } while (0)

 

                                                         

#define  OS_CRITICAL_EXIT()                                        \

         do {                                                      \

             CPU_CRITICAL_ENTER();                                 \

             OSSchedLockNestingCtr--;                              \

             if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) {     \

                 OS_SCHED_LOCK_TIME_MEAS_STOP();                   \

                 if (OSIntQNbrEntries > (OS_OBJ_QTY)0) {           \

                     CPU_CRITICAL_EXIT();                          \

                     OS_Sched0();                                  \

                 } else {                                          \

                     CPU_CRITICAL_EXIT();                          \

                                                                \

             } else {                                              \

                 CPU_CRITICAL_EXIT();                              \

                                                                \

         } while (0)

 

#define  OS_CRITICAL_EXIT_NO_SCHED()                               \

         do {                                                      \

             CPU_CRITICAL_ENTER();                                 \

             OSSchedLockNestingCtr--;                              \

             if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) {     \

                 OS_SCHED_LOCK_TIME_MEAS_STOP();                   \

                                                                \

             CPU_CRITICAL_EXIT();                                  \

         } while (0)

 

 

#else                                             

 

 

#define  OS_CRITICAL_ENTER()                    CPU_CRITICAL_ENTER()

 

#define  OS_CRITICAL_ENTER_CPU_EXIT()

 

#define  OS_CRITICAL_EXIT()                     CPU_CRITICAL_EXIT()

 

#define  OS_CRITICAL_EXIT_NO_SCHED()            CPU_CRITICAL_EXIT()

 

#endif

11.8  安全关键IEC61508

关于IEC61508,大家有个了解即可,更详细的资料可以查阅wiki百科进行了解或者查找相关的文档说明。

11.8.1       IEC61508基本概念

IEC 61508是一项用于工业领域的国际标准,其名称是《电气/电子/可编程电子安全相关系统的功能安全》。由国际电工委员会发布,其目的要建立一个可应用于各种工业领域的基本功能安全标准。它将功能安全定义为:“是受控设备(EUC)或受控设备系统总体安全中的一部分;其安全性是依赖于电气/电子/可编程电子(E/E/PE)安全相关系统、其他技术的安全相关系统或外部风险降低措施的正确机能。

() IEC61508中的基本定义

1.  安全功能 (safety function)

针对规定的危险事件,为达到或保持受控设备(EUC)的安全状态,由E/E/PE安全系统、其他技术安全系统或外部风险降低设施实现的功能。

2.  安全完整性 (Safety integrity)

在规定的条件下、规定的时间内,安全系统成功实现所要求的安全功能的概率。这一定义着重于安全系统执行安全功能的可靠性。在确定安全完整性过程中,应包括所有导致非安全状态的因素,如随机的硬件失效,软件导致的失效以及由电气干扰引起的失效,这些失效的类型,尤其是硬件失效可用测量方法来定量,如在危险模式中的失效和系统失效率,或按规定操作的安全防护系统失效的概率。但是,系统的安全完整性还取决于许多因素,这些因素无法精确定量,仅可定性考虑。

3.  E/E/PE系统

基于电气/电子和可编程电子装置的用于控制、防护或监视的系统,包括系统中所有的元素如电源、传感器、所有其他输入输出装置及所有通信手段。

4.     EUC(Equipment Under Control)

        受控设备,指用于制造、运输、医疗或其他领域的设备、机器、装置或装备。
5.
可接受凤险 (ACCeptable risk)

风险指的是出现伤害的概率及该伤害严重性的组合。可接受风险指根据当今社会的水准所能够接受的风险。

6.     安全 (Safety)

不存在不可接受的风险。

7.     安全系统 (Safely-elated-syStem)

是用于两个目的:一是执行要求的安全功能以达到或保持EUC的安全状态;二是自身或与其他E/E/PES安全系统、其他技术安全系统或外部风险降低设施一道,对于要求的安全功能达到必要的安全完整性。安全系统是在接受命令后采取适当的动作以防止EUC进入危险状态。安全系统的失效应被包括在导致确定的危险事件中。尽管可能有其他系统具备安全功能,但仅是指用其自身能力达到要求的允许风险的安全系统。安全系统可大致分为安全控制系统和安全防护系统。

安全系统可以是EUC控制系统的组成部分,也可用传感器和/或执行器与EUC的接口,既可用在EUC控制系统中执行安全功能的方式达到要求的安全完整性水平,也可用分离的/独立的专门用于安全的系统执行安全功能。

()IEC61508的基本概念

IEC61508标准规定随机失效的后果必须定量评估,使用随机存取测量系统 (RAMS)方法计算有效性。量化与故障相关的系统失效是没有用的,应当通过组织指导来避免系统失效,或通过技术措施来控制。

1.风险和安全完整性慨念 

2.功能安全保证的内容

功能安全保证主要包括两部分内容:失效识别和安全完整性水平。

(1)失效识别。

失效就是功能单元失去实现其功能的能力。一些功能是根据所达到的行为进行规定的,在执行功能时,某些特定的行为是不允许的,这些行为的出现就是失效。失效可能是随机失效,这种失效通常由于硬件装置的耗损所致。也可能是系统失效,这在硬件和软件中都可能出现。失效识别就是要分辨出不同部件的各种失效原因,估算出系统失效概率。

(2)安全完整性水平 (SIL) (safety integrity level)

一种离散的水平,用于规定分配给E/E/PE安全系统的安全功能的安全完整性要求,安全系统的安全完整性水平越高,安全系统实现所要求的安全功能失败的可能性就越低。IEC61508中规定系统有4种安全完整性水平,SIL4是最高的,安全完整性水平1是最低的。

11.8.2       启动安全关键OSSafetyCriticalStart

源码中加入了部分安全关键的代码,内容如下:

 

 

#ifdef OS_SAFETY_CRITICAL_IEC61508

void  OSSafetyCriticalStart (void)

{

    OSSafetyCriticalStartFlag = DEF_TRUE;

}

 

#endif

1.     这个函数是供用户调用的,用户一旦调用了这个函数就表示所有的初始化已经完成。内核对象不再运行被创建。后面讲任务间通信机制的时候会看到相关代码,比如创建定时器的函数中:

#ifdef OS_SAFETY_CRITICAL_IEC61508

    if (OSSafetyCriticalStartFlag == DEF_TRUE) {

       *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;

        return;

    }

#endif

11.8.3       函数使用举例

void AppStartTaskvoid *p_arg

{

     (void)& p_arg

     OSSafetyCriticalStart (); //直接在启动函数中调用即可

     while(DEF_ON)

     {

}

}

11.9  任务切换

如果大家认真学习了前面几期教程,μCOS-III中的任务切换还是很好理解的。μCOS-III中的任务切换主要分为两部分,一个是中断级任务切换,另一个是任务级中断切换。

11.9.1       任务级任务切换OSSched()

任务级的任务切换主要通过下面的函数实现:

 

 

void  OSSched (void)

{

    CPU_SR_ALLOC();                                                                                   1

 

 

 

    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {                                 2

        return;                                       

    }

 

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {                                 3

        return;                                        

    }

 

    CPU_INT_DIS();

    OSPrioHighRdy   = OS_PrioGetHighest();                      4

    OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;                                               5

    if (OSTCBHighRdyPtr == OSTCBCurPtr) {             

        CPU_INT_EN();                                  

        return;

    }

 

#if OS_CFG_TASK_PROFILE_EN > 0u

    OSTCBHighRdyPtr->CtxSwCtr++;                      

#endif

    OSTaskCtxSwCtr++;                                  

 

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)

    OS_TLS_TaskSw();

#endif

 

    OS_TASK_SW();                                                                  6

    CPU_INT_EN();

}

1.     这句话在前面临界段小节有讲,这里是定义一个局部变量。

2.     存在中断嵌套的情况下调用这个函数会返回。

3.     调度器被锁的情况下调用这个函数会返回。

4.     这个函数是获取当前需要执行的最高优先级任务的方法,也算是调度器的核心,在下期教程会给大家详细的讲解。

5.     由于μCOS-III已经支持时间片调度,这句话是从同优先级的链表中获得当前需要执行的任务。

6.     执行任务级任务切换。

11.9.2       中断级任务切换OSIntExit()

中断级任务切换是由中断函数调用的,内容如下:

 

 

void  OSIntEnter (void)                                                                               1

{

    if (OSRunning != OS_STATE_OS_RUNNING) {      

        return;                                  

    }

 

    if (OSIntNestingCtr >= (OS_NESTING_CTR)250u) {  

        return;                                     

    }

 

    OSIntNestingCtr++;                                                      2

}

 

 

 

void  OSIntExit (void)

{

    CPU_SR_ALLOC();

 

 

 

    if (OSRunning != OS_STATE_OS_RUNNING) {         

        return;                                     

    }

 

    CPU_INT_DIS();

    if (OSIntNestingCtr == (OS_NESTING_CTR)0) { 

        CPU_INT_EN();

        return;

    }

    OSIntNestingCtr--;                                                                               3

    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {   

        CPU_INT_EN();                            

        return;

    }

 

    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {  

        CPU_INT_EN();                                  

        return;

    }

 

    OSPrioHighRdy   = OS_PrioGetHighest();             

    OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;

    if (OSTCBHighRdyPtr == OSTCBCurPtr) {              

        CPU_INT_EN();                                  

        return;

    }

 

#if OS_CFG_TASK_PROFILE_EN > 0u

    OSTCBHighRdyPtr->CtxSwCtr++;                 

#endif

    OSTaskCtxSwCtr++;                            

 

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)

    OS_TLS_TaskSw();

#endif

 

    OSIntCtxSw();                                                             4

    CPU_INT_EN();

}

1.     OSIntEnter() OSIntExit()必须配套的使用,要不会造成嵌套计数错误。

2.     嵌套计数加一。

3.     嵌套次数减一。

4.     执行中断级任务切换。

11.9.3       函数使用举例

函数OSSched ()不是供用户调用的,而函数OSIntEnter()OSIntExit()是供用户在中断任务中调用的,下面举个例子:

 

void SDIO_IRQHandler(void)

{

     CPU_SR_ALLOC();

    

     CPU_CRITICAL_ENTER(); 

     OSIntEnter()

     CPU_CRITICAL_EXIT();

 

     SD_ProcessIRQSrc();

 

    

     OSIntExit();

}


0

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

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

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

新浪公司 版权所有