新浪博客

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

2014-12-29 12:39阅读:
特别说明:
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();
}

我的更多文章

下载客户端阅读体验更佳

APP专享