完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
第11章 μCOS-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.1 系统配置文件 下面先简单说明下μCOS-III中几个配置文件的作用,方便分析源码的时候查看,配置文件主要有以下几个: lib_cfg.h文件内容如下:
lib_cfg.h是用于给uC/LIB做配置的头文件。如果程序中使用uC/LIB的话,需要调用函数Mem_Init()进行初始化。 11.1.2 os_cfg.h配置文件 os_cfg.h文件中的内容如下:
这个配置文件比较的重要,主要用于μCOS-III源码中相关函数的配置。 11.1.3 os_cfg_app.h配置文件 os_cfg_app.h文件的内容如下:
这个文件主要是内核任务的配置,包括中断管理任务,空闲任务,统计任务,嘀嗒定时器任务已经定时器任务。 11.1.4 app_cfg.h配置文件 app_cfg.h文件夹中的内容如下:
这个文件主要用于用户任务的配置。 11.1.5 cpu_cfg.h配置文件 cpu_cfg.h文件中的内容如下:
这个文件里面主要是uC/CPU相关的配置。 11.2 源码文件 μCOS-III中的源码主要包含如下文件: 上面截图中是μCOS-III相关的源码文件,后面的几期教程就是把这几个文件讲清楚。本期教程主要是讲解os_core文件中的相关函数。学期源码前还有看一下os_type.h文件中的内容(主要是变量的类型,这个一定得了解下):
11.3 μCOS-III-III初始化 下面开始讲解os_core.c文件中的函数,这个文件里面的函数比较多,为了方便大家更好的学习这个文件中的相关函数,这里对其进行了分类,先从μCOS-III的初始化开始讲解。 11.3.1 初始化 根据前面os_cfg.h文件中的配置,用户可以配置程序中需要执行的函数。μCOS-III的初始化函数OSInit()中涉及的全局变量和函数的初始化比较多,这里将其汇总一下,下面是全局变量的初始化:
函数OSInit()中的其它初始化会在后面讲到的时候再做详细阐述。下面把初始化函数做一下分析。 11.3.2 初始化函数OSInit()
1. 如果用户在工程预定义中加入了OS_SAFETY_CRITICAL,那么函数就会执行参数检查功能,如果参数不满足条件的话就会进入安全关键异常OS_SAFETY_CRITICAL_EXCEPTION()。安全关键的代码是需要用户去实现的。 2. OSIntNestingCtr 用于记录中断嵌套次数,在μCOS-III中使用中断的话,一般采用如下的方式: void OS_CPU_SysTickHandler (void) { CPU_SR_ALLOC(); CPU_CRITICAL_ENTER(); OSIntNestingCtr++; CPU_CRITICAL_EXIT(); //添加用户代码 OSIntExit(); } 也就是说进入中断后,先将OSIntNestingCtr加一,退出的时候再执行减一。为什么要这么做?为了说明这个问题,这里举一个例子,假如有低优先级中断A和高优先级中断B,A中断执行的时候,中断B抢占了A的执行,中断B执行完退出的时候会执行函数OSIntExit();(这个函数最主要的作用就是中断级任务切换)。可以想象,如果没有中断嵌套计数变量,退出高优先级中断B的时候运行函数OSIntExit();有可能会执行中断级任务切换,此时执行任务切换的话就会破换正常的寄存器入栈和出栈顺序,从而使得系统崩溃。其实进入Cortex-M3/M4时代已经不存在这个问题了,因为我们设置PendSV中断为最低的优先级(任务切换是在PendSV中断里面执行的),PendSV中断会在所有其它高优先级中断执行完后才会得到执行。 为了使得μCOS-III可以适应各种处理器内核和复杂的系统设置,中断嵌套计数还是很有必要的。 3. 当前任务控制块指针OSTCBCurPtr和最高就绪任务的任务控制块指针OSTCBHighRdyPtr都指向了0地址空间。初始为指向0地址空间主要是为了使这两个指针变量都有个初始量。而且方便后面用到这两个指针变量的函数进行检测。 4. 默认安全关键的标志OSSafetyCriticalStartFlag是关闭的。 5. 这里个两个变量:OSSchedRoundRobinEn = DEF_FALSE; OSSchedRoundRobinDfltTimeQuanta= OSCfg_TickRate_Hz / 10u; 是用于配置时间片的,默认时间片调度是关闭的,时间片大小是OSCfg_TickRate_Hz / 10u。这两个关于时间片的配置变量大家一定要记住。 6. 这里是几个钩子函数指针,初始化钩子函数,关于钩子函数,咱们在上期教程有详细讲解。 函数中还有有很多其它的初始化,我们会在后面再跟大家详细的讲述。 11.4 μCOS-III启动11.4.1 启动 不像其它RTOS,μCOS-III有一个标准的初始化过程,所以特别建议初学的同学按照这个过程进行初始化,如果不按照这个过程进行初始化,往往会出现莫名其妙的后果,标准的初始化流程如下:
上面的五步就是标准的初始化流程,一定要按照这个顺序进行,这个流程是官方推荐的。为什么要按照这个流程进行初始化? 随着后面源码的讲解,大家会深刻的领悟到这一点。 11.4.2 启动函数OSStart ()
1. 后面大家会看到,只要是供用户调用的函数和部分内部函数里面都会有这个安全关键代码,主要是用来防止错误的输入形参。 2. 前面初始化的时候,设置OSRunning = OS_STATE_OS_STOPPED,所以按照正常的初始化顺序,程序会进入到if语句中。 3. 通过函数OS_PrioGetHighest()可以获得当前需要执行的最高优先级任务,这个会在下期教程跟大家详细讲述。 4. 从任务的就绪链表中获得当前需要执行的最高优先级任务。 5. 设置系统的运行状态为OSRunning = OS_STATE_OS_RUNNING,然后开始μCOS-III多任务的执行。 6. 关于这个函数,咱们在上期教程中有详细的讲解,这个函数执行完就会进入到PendSV中断。 7. 正常情况下程序是不会执行到这里的。 8. 如果系统已经启动了,用户程序要是再次调用这个函数会进入到这里。 |
||
相关推荐
|
||
11.5 获取系统版本 有时候我们需要获取系统的版本号,下面就是系统版本的获取函数。这个函数比较简单,特别注意函数注释。
11.6 空闲任务 几乎所有的小型RTOS中都会有一个空闲任务,空闲任务应该属于系统任务,是必须要执行的,用户程序不能将其关闭。不光小型系统中有空闲任务,大型的系统里面也有的,比如XP,下面的截图就是XP中的空闲进程。 空闲任务主要有以下几个作用: A、我们不能让系统一直在执行各个应用任务,这样的话系统利用率就是100%,系统就会一直的超负荷运行,所以空闲任务很有必要。 B、为了更好的实现低功耗,空闲任务也很有必要,我们可以在空闲任务中实现睡眠,待机等低功耗措施。 11.6.1 创建空闲任务OS_IdleTaskInit ()
关于任务的创建,本期教程暂时还不做介绍。 11.6.2 空闲任务OS_IdleTask ()
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中主要有以下布尔宏定义:
4. 空闲任务计数。 5. 统计任务计数,这两个计数都是为了计算CPU利用率。 6. 用户可以在钩子函数中加入待机,休眠等低功耗操作。
|
|
|
|
|
|
11.7 临界段11.7.1 临界段基本概念
代码的临界段也称为临界区,指处理时不可分割的代码。一旦这部分代码开始执行,则不允许任何中断打入。为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。 从代码上来看,处在关中断和开中断之间的代码段就是临界段。 由于各厂商的CPU和C编译器的关中断和开中断的方法以及指令不尽相同,为增强μCOS-III的可移植性(即在μCOS-III的各个C语言函数中尽可能地不出现汇编语言代码),μCOS-III用CPU_INT_DIS()和CPU_INT_EN()这两个宏封装了与系统硬件相关的关中断和开中断指令。 另外,不要在临界段中调用μCOS-III提供的功能函数,防止系统崩溃。 11.7.2 临界段相关的宏定义 CPU_INT_DIS()和CPU_INT_EN()可以以下四种不同的实现方法。
至于在实际应用时使用哪种方法,取决于用户使用的处理器和C编译器。用户可以通过cpu.h文件中的宏定义进行选择:
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中也有几个相关的宏定义,这几个宏定义的含义会在后面跟大家讲
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(EquipmentUnder 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) (safetyintegrity level)。 一种离散的水平,用于规定分配给E/E/PE安全系统的安全功能的安全完整性要求,安全系统的安全完整性水平越高,安全系统实现所要求的安全功能失败的可能性就越低。IEC61508中规定系统有4种安全完整性水平,SIL4是最高的,安全完整性水平1是最低的。 11.8.2 启动安全关键OSSafetyCriticalStart 源码中加入了部分安全关键的代码,内容如下:
1. 这个函数是供用户调用的,用户一旦调用了这个函数就表示所有的初始化已经完成。内核对象不再运行被创建。后面讲任务间通信机制的时候会看到相关代码,比如创建定时器的函数中:
|
|
|
|
|
|
11.9 任务切换 如果大家认真学习了前面几期教程,μCOS-III中的任务切换还是很好理解的。μCOS-III中的任务切换主要分为两部分,一个是中断级任务切换,另一个是任务级中断切换。 11.9.1 任务级任务切换OSSched() 任务级的任务切换主要通过下面的函数实现:
1. 这句话在前面临界段小节有讲,这里是定义一个局部变量。 2. 存在中断嵌套的情况下调用这个函数会返回。 3. 调度器被锁的情况下调用这个函数会返回。 4. 这个函数是获取当前需要执行的最高优先级任务的方法,也算是调度器的核心,在下期教程会给大家详细的讲解。 5. 由于μCOS-III已经支持时间片调度,这句话是从同优先级的链表中获得当前需要执行的任务。 6. 执行任务级任务切换。 11.9.2 中断级任务切换OSIntExit()中断级任务切换是由中断函数调用的,内容如下:
1. OSIntEnter()和OSIntExit()必须配套的使用,要不会造成嵌套计数错误。 2. 嵌套计数加一。 3. 嵌套次数减一。 4. 执行中断级任务切换。 11.9.3 函数使用举例 函数OSSched ()不是供用户调用的,而函数OSIntEnter() 和 OSIntExit()是供用户在中断任务中调用的,下面举个例子:
11.10 调度锁 给调度器加锁后将禁止任务调度,直到任务完成后,调用调度器解锁函数才能重新开始任务调度。这里一定要明白一点,调度锁只是将调度器关闭,并不影响中断的执行,该进中断还是要进的,只是不会执行任务切换。 11.10.1 调度器加锁OSSchedLock()
1. 这里表示不允许在中断服务程序中调用调度锁函数。 2. 调度锁的嵌套次数范围0~250,用户不能超出250。 3. 嵌套次数加一。 4. 开启调度锁时间测量,这里是记录一下起始时间。 11.10.2 调度器解锁OSSchedUnlock
1. 调度锁嵌套计数减一。 2. 调度器被锁期间,有些任务可能会就绪,所以这里加上了调度函数。 11.10.3 调度器被锁时间测量OS_SchedLockTimeMeas 函数比较简单,这里就直接将内容贴上,特别注意函数的注释。
下面这个例子是为了防止截屏的过程中被其它任务打断,加入了调度锁功能。
|
|
|
|
|
|
11.11 Round-Robin调度 μCOS-III中的时间片调度功能做的很完善,支持全局的时间片设置,也支持每个任务的单独设置。关于时间片调度,咱们在前几期教程也有讲解。 11.11.1 配置参数OSSchedRoundRobinCfg()
1. 由于这几个参数是全局变量,所以必须关闭中断。 2. 根据形参设置是否使能时间片调度。 3. 变量OSSchedRoundRobinDfltTimeQuanta是用来设置默认的时间片个数,也就是说,如果程序中没有单独配置任务的时间片个数,就会使用这个默认时间片个数。 11.11.2 放弃剩余时间片OSSchedRoundRobinYield ()这个函数的主要功能就是任务在完成工作的情况下,如果还有剩余的时间片,可以放弃这些时间去执行另外的同优先级任务(切记,是另外的同优先级任务)
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。 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个时间片,图片上面画的不是很准确。 有了上面基础后,在解析一下相关函数。
1. 检测Round-Robin调度是否使能。 2. 确保此优先级下存在任务。 3. 这句话的意思是说:不允许用户将应用任务的优先级设置的和空闲任务优先级一样,也就是说空闲任务的优先级下不能有其它任务。 4. 时间片减一。 5. 任务的时间片还没有用完,退出继续执行。 6. 同优先级下必须有两个及其以上的任务才能继续往下执行。 7. 如果调度器被锁,不能执行Round-Robin调度。 8. 通过调整同优先级下的就绪链表获得下一个要执行的任务。 9. 判断使用全局的默认时间片个数还是使用单独设计的时间片个数。 11.11.4 函数使用举例
11.12 总结 本期教程涉及到的内容较多,如果是初学的,一定要多花点时间消耗下,如果学习过程中遇到很多问题,不要担心,随着后面教程的进行会理解的更深刻。 |
|
|
|
|
|
798 浏览 0 评论
4836 浏览 0 评论
如何使用python调起UDE STK5.2进行下载自动化下载呢?
2622 浏览 0 评论
开启全新AI时代 智能嵌入式系统快速发展——“第六届国产嵌入式操作系统技术与产业发展论坛”圆满结束
2958 浏览 0 评论
获奖公布!2024 RT-Thread全球巡回线下培训火热来袭!报名提问有奖!
31763 浏览 11 评论
73109 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-26 00:38 , Processed in 0.806930 second(s), Total 74, Slave 55 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号