μC/OS-II
μC/OS-II是一种基于优先级的抢占式多任务实时操作系统,包含了实时内核、任务管理、时间管理、任务间通信同步(信号量,邮箱,消息队列)和内存管理等功能。它可以使各个任务独立工作,互不干涉,很容易实现准时而且无误执行,使实时应用程序的设计和扩展变得容易,使应用程序的设计过程大为减化。
中文名:μC/OS-II
外文名:基于优先级的抢占式多任务
分类:任务类消息类同步类
1、简介
μC/OS-II是一个完整的、可移植、可固化、可裁剪的抢占式实时多任务内核。μC/OS-II绝大部分的代码是用ANSII的C语言编写的,包含一小部分汇编代码,使之可供不同架构的微处理器使用。至今,从8位到64位,μC/OS-II已在超过40种不同架构上的微处理器上运行。μC/OS-II已经在世界范围内得到广泛应用,包括很多领域,如手机、路由器、集线器、不间断电源、飞行器、医疗设备及工业控制上。实际上,μC/OS-II已经通过了非常严格的测试,并且得到了美国航空管理局(FederalAviationAdministration)的认证,可以用在飞行器上。这说明μC/OS-II是稳定可靠的,可用于与人性命攸关的安全紧要(safetycritical)系统。除此以外,μC/OS-II的鲜明特点就是源码公开,便于移植和维护。
2、内核结构
多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。μC/OS-II可以管理多达64个任务。由于它的作者占用和保留了8个任务,所以留给用户应用程序最多可有56个任务。赋予各个任务的优先级必须是不相同的。这意味着μC/OS-II不支持时间片轮转调度法(round-robinscheduling)。μC/OS-II为每个任务设置独立的堆栈空间,可以快速实现任务切换。μC/OS-II近似地每时每刻总是让优先级最高的就绪任务处于运行状态,为了保证这一点,它在调用系统API函数、中断结束、定时中断结束时总是执行调度算法,μC/OS-II通过事先计算好数据简化了运算量,通过精心设计就绪表结构使得延时可预知。
3、特点
1、源代码:µC/OS-II全部以源代码的方式提供给使用者(约5500行)。该源码清晰易读,结构协调,且注解详尽,组织有序;
2、可移植(portable):µC/OS-II的源代码绝大部分是用移植性很强的ANSIC写的,与微处理器硬件相关的部分是用汇编语言写的。µC/OS-II可以移植到许许多多不同的微处理器上,条件是:该微处理器具有堆栈指针,具有CPU内部寄存器入栈、出栈指令,使用的C编译器必须支持内嵌汇编,或者该C语言可扩展和可链接汇编模块,使得关中断和开中断能在C语言程序中实现;
3、可固化(ROMable):µC/OS-II是为嵌入式应用而设计的,意味着只要具备合适的系列软件工具(C编译、汇编、链接以及下载/固化)就可以将µC/OS-II嵌入到产品中作为产品的一部分;
4、可裁减(scalable):可以只使用µC/OS-II中应用程序需要的系统服务。可裁减性是靠条件编译实现的,只需要在用户的应用程序中定义那些µC/OS-II中的功能应用程序需要的部分就可以了;
5、可抢占性(preemptive):µC/OS-II是完全可抢占型的实时内核,即µC/OS-II总是运行就绪条件下优先级最高的任务;
6、多任务:µC/OS-II可以管理64个任务。赋予每个任务的优先级必须是不相同的,这就是说µC/OS-II不支持时间片轮转调度法(该调度法适用于调度优先级平等的任务);
7、可确定性:绝大多数µC/OS-II的函数调用和服务的执行时间具有可确定性。也就是说用户能知道µC/OS-II的函数调用与服务执行了多长时间。进而可以说,除了函数OSTimeTick()和某些事件标志服务,µC/OS-II系统服务的执行时间不依赖于用户应用程序任务数目的多少;
8、任务栈:每个任务都有自己单独的栈。µC/OS-II允许每个任务有不同的栈空间,以便降低应用程序对RAM的需求;
9、系统服务:µC/OS-II提供许多系统服务,比如信号量、互斥信号量、事件标志、消息邮箱、消息队列、时间管理等等;
10、中断管理:中断可以使正在执行的任务暂时挂起。如果优先级更高的任务被该中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可以达255层;
11、稳定性和可靠性:µC/OS-II的每一种功能、每一个函数以及每一行代码都经过了考验和测试,具有足够的安全性与稳定性,能用于与人性命攸关、安全性条件极为苛刻的系统中。
4、μC/OS-IIAPI
任何一个操作系统都会提供大量的API供程序员使用,μC/OS-II也不例外。由于μC/OS-II面向的是嵌入式开发,并不要求大而全,所以内核提供的API也就大多和多任务息息相关。主要有以下几类:
1)任务类
2)消息类
3)同步类
4)时间类
5)临界区与事件类
对初级程序员而言,任务类和事件类是必须要首先掌握的两种API。
5、实验内容
任务管理实验
此实验的目的是让读者理解嵌入式操作系统中任务管理的基本原理,了解任务的各个基本状态及其变迁过程;掌握µC/OS-II中任务管理的基本方法(创建、启动、挂起和解挂任务);熟练使用µC/OS-II任务管理的基本系统调用。
优先级反转实验
通过此实验读者可以了解在基于抢占式嵌入式实时操作系统并有共享资源的应用中,出现优先级反转现象的原理。优先级反转发生在有多个任务共享资源的情况下,高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。
优先级继承实验
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II解决优先级反转的策略——优先级继承的原理,以此解决低优先级任务在占用了共享资源的情况下,被高优先级任务抢占了CPU使用权而导致的优先级反转的问题。
哲学家就餐实验
通过经典的哲学家就餐应用,读者可以了解如何利用嵌入式实时操作系统µC/OS-II的信号量机制来对共享资源进行互斥访问。
内存管理实验
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中的内存管理的原理,包括对内存的分配和回收。
时钟中断实验
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中,时钟中断的使用情况。
消息队列实验
通过此实验读者可以了解嵌入式实时操作系统µC/OS-II中的消息队列机制。读者可以了解一个应用中的任务是如何进行通信的,如何能使它们相互协调工作。
6、μC/OS-Ⅱ中的任务描述
一个任务通常是一个无限的循环,由于任务的执行是操作系统内核调度的,因此务是绝不会返回的,其返回参数必须定义成void。在μC/OS-Ⅱ中,当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU使用权就会被抢占,高优先级任务会立刻得到CPU的控制权(在系统允许调度和任务切换的前提下)。μC/OS-Ⅱ可以管理多达64个任务,但目前版本的μC/OS-Ⅱ有两个任务已经被系统占用了(即空闲任务和统计任务)。必须给每个任务赋以不同的优先级,任务的优先级号就是任务编号(ID),优先级可以从0到OS_LOWEST_PR10-2。优先级号越低,任务的优先级越高。μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。
7、配置手册
由于μC/OS-II向用户提供源代码,初始化配置项由一系列#defineconstant语句构成,都在文件OS_CFG.H中。用户的工程文件组中都应该包含这个文件。
注意编译工程文件时要包含OS_CFG.H,使定义的常量生效。
杂相
OSInit()无OS_MAX_EVENTS
OS_Q_ENandOS_MAX_QS
OS_MEM_EN
OS_TASK_IDLE_STK_SIZE
OS_TASK_STAT_EN
OS_TASK_STAT_STK_SIZE
OSSchedLock()无无
OSSchedUnlock()无无
OSStart()无无
OSStatInit()OS_TASK_STAT_EN&&
OS_TASK_CREATE_EXT_ENOS_TICKS_PER_SEC
OSVersion()无无
中断处理
OSIntEnter()无无
OSIntExit()无无
消息邮箱
OSMboxAccept()OS_MBOX_EN无
OSMboxCreate()OS_MBOX_ENOS_MAX_EVENTS
OSMboxPend()OS_MBOX_EN无
OSMboxPost()OS_MBOX_EN无
OSMboxQuery()OS_MBOX_EN无
内存块管理
OSMemCreate()OS_MEM_ENOS_MAX_MEM_PART
OSMemGet()OS_MEM_EN无
OSMemPut()OS_MEM_EN无
OSMemQuery()OS_MEM_EN无
消息队列
OSQAccept()OS_Q_EN无
OSQCreate()OS_Q_ENOS_MAX_EVENTS
OS_MAX_QS
OSQFlush()OS_Q_EN无
OSQPend()OS_Q_EN无
OSQPost()OS_Q_EN无
OSQPostFront()OS_Q_EN无
OSQQuery()OS_Q_EN无
信号量管理
OSSemAccept()OS_SEM_EN无
OSSemCreate()OS_SEM_ENOS_MAX_EVENTS
OSSemPend()OS_SEM_EN无
OSSemPost()OS_SEM_EN无
OSSemQuery()OS_SEM_EN无
任务管理
OSTaskChangePrio()OS_TASK_CHANGE_PRIO_ENOS_LOWEST_PRIO
OSTaskCreate()OS_TASK_CREATE_ENOS_MAX_TASKS
OS_LOWEST_PRIO
OSTaskCreateExt()OS_TASK_CREATE_EXT_ENOS_MAX_TASKS
OS_STK_GROWTH
OS_LOWEST_PRIO
OSTaskDel()OS_TASK_DEL_ENOS_LOWEST_PRIO
OSTaskDelReq()OS_TASK_DEL_ENOS_LOWEST_PRIO
OSTaskResume()OS_TASK_SUSPEND_ENOS_LOWEST_PRIO
OSTaskStkChk()OS_TASK_CREATE_EXT_ENOS_LOWEST_PRIO
OSTaskSuspend()OS_TASK_SUSPEND_ENOS_LOWEST_PRIO
OSTaskQuery()OS_LOWEST_PRIO
时钟管理
OSTimeDly()无无
OSTimeDlyHMSM()无OS_TICKS_PER_SEC
OSTimeDlyResume()无OS_LOWEST_PRIO
OSTimeGet()无无
OSTimeSet()无无
OSTimeTick()无无
用户定义函数
OSTaskCreateHook()OS_CPU_HOOKS_EN无
OSTaskDelHook()OS_CPU_HOOKS_EN无
OSTaskStatHook()OS_CPU_HOOKS_EN无
OSTaskSwHook()OS_CPU_HOOKS_EN无
OSTimeTickHook()OS_CPU_HOOKS_EN无
8、OS_MAX_EVENTS
OS_MAX_EVENTS定义系统中最大的事件控制块的数量。系统中的每一个消息邮箱,消息队列,信号量都需要一个事件控制块。例如,系统中有10个消息邮箱,5个消息队列,3个信号量,则OS_MAX_EVENTS最小应该为18。只要程序中用到了消息邮箱,消息队列或是信号量,则OS_MAX_EVENTS最小应该设置为2。
OS_MAX_MEM_PARTS
OS_MAX_MEM_PARTS定义系统中最大的内存块数,内存块将由内存管理函数操作(定义在文件OS_MEM.C中)。如果要使用内存块,OS_MAX_MEM_PARTS最小应该设置为2,常量OS_MEM_EN也要同时置1。
OS_MAX_QS
OS_MAX_QS定义系统中最大的消息队列数。要使用消息队列,常量OS_Q_EN也要同时置1。如果要使用消息队列,OS_MAX_QS最小应该设置为2。
OS_MAX_TASKS
OS_MAX_MEM_TASKS定义用户程序中最大的任务数。OS_MAX_MEM_TASKS不能大于62,这是由于μC/OS-II保留了两个系统使用的任务。如果设定OS_MAX_MEM_TASKS刚好等于所需任务数,则建立新任务时要注意检查是否超过限定。而OS_MAX_MEM_TASKS设定的太大则会浪费内存。
OS_LOWEST_PRIO
OS_LOWEST_PRIO设定系统中的任务最低优先级(最大优先级数)。设定OS_LOWEST_PRIO可以节省用于任务控制块的内存。μC/OS-II中优先级数从0(最高优先级)到63(最低优先级)。设定OS_LOWEST_PRIO小于63意味着不会建立优先级数大于OS_LOWEST_PRIO的任务。μC/OS-II中保留两个优先级系统自用:OS_LOWEST_PRIO和OS_LOWEST_PRIO-1。其中OS_LOWEST_PRIO留给系统的空闲任务(Idletask)(OSTaskIdle())。OS_LOWEST_PRIO-1留给统计任务(OSTaskStat())。用户任务的优先级可以从0到OS_LOWEST_PRIO-2。OS_LOWEST_PRIO和OS_MAX_TASKS之间没有什么关系。例如,可以设OS_MAX_TASKS为10而OS_LOWEST_PRIO为32。此时系统最多可有10个任务,用户任务的优先级可以是0到30。当然,OS_LOWEST_PRIO设定的优先级也要够用,例如设OS_MAX_TASKS为20,而OS_LOWEST_PRIO为10,优先级就不够用了。
OS_TASK_IDLE_STK_SIZE
OS_TASK_IDLE_STK_SIZE设置μC/OS-II中空闲任务(Idletask)堆栈的容量。注意堆栈容量的单位不是字节,而是OS_STK(μC/OS-II中堆栈统一用OS_STK声明,会根据不同的硬件环境,OS_STK可为不同的长度----译者注)。空闲任务堆栈的容量取决于所使用的处理器,以及预期的最大中断嵌套数。虽然空闲任务几乎不做什么工作,但还是要预留足够的堆栈空间保存CPU寄存器的内容,以及可能出现的中断嵌套情况。
OS_TASK_STAT_EN
OS_TASK_STAT_EN设定系统是否使用μC/OS-II中的统计任务(statistictask)及其初始化函数。如果设为1,则使用统计任务OSTaskStat()。统计任务每秒运行一次,计算当前系统CPU使用率,结果保存在8位变量OSCPUUsage中。每次运行,OSTaskStat()都将调用OSTaskStatHook()函数,用户自定义的统计功能可以放在这个函数中。详细情况请参考OS_CORE.C文件。统计任务OSTaskStat()的优先级总是设为OS_LOWEST_PRIO-1。
当OS_TASK_STAT_EN设为0的时候,全局变量OSCPUUsage,OSIdleCtrMax,OSIdleCtrRun和OSStatRdy都不声明,以节省内存空间。
OS_TASK_STAT_STK_SIZE
OS_TASK_STAT_STK_SIZE设置μC/OS-II中统计任务(statistictask)堆栈的容量。统计任务堆栈的容量取决于所使用的处理器类型,以及如下的操作:
进行32位算术运算所需的堆栈空间。
调用OSTimeDly()所需的堆栈空间。
调用OSTaskStatHook()所需的堆栈空间。
预计最大的中断嵌套数。
如果想在统计任务中进行堆栈检查,判断实际的堆栈使用,用户需要设OS_TASK_CREATE_EXT_EN为1,并使用OSTaskCreateExt()函数建立任务。
OS_CPU_HOOKS_EN
此常量设定是否在文件OS_CPU_C.C中声明对外接口函数(hookfunction),设为1为声明。μC/OS-II中提供了5个对外接口函数,可以在文件OS_CPU_C.C中声明,也可以在用户自己的代码中声明:
OSTaskCreateHook()
OSTaskDelHook()
OSTaskStatHook()
OSTaskSwHook()
OSTimeTickHook()
OS_MBOX_EN
OS_MBOX_EN控制是否使用μC/OS-II中的消息邮箱函数及其相关数据结构,设为1为使用。如果不使用,则关闭此常量节省内存。
OS_MEM_EN
OS_MEM_EN控制是否使用μC/OS-II中的内存块管理函数及其相关数据结构,设为1为使用。如果不使用,则关闭此常量节省内存。
OS_Q_EN
OS_Q_EN控制是否使用μC/OS-II中的消息队列函数及其相关数据结构,设为1为使用。如果不使用,则关闭此常量节省内存。如果OS_Q_EN设为0,则语句#defineconstantOS_MAX_QS无效。
OS_SEM_EN
OS_SEM_EN控制是否使用μC/OS-II中的信号量管理函数及其相关数据结构,设为1为使用。如果不使用,则关闭此常量节省内存。
OS_TASK_CHANGE_PRIO_EN
此常量控制是否使用μC/OS-II中的OSTaskChangePrio()函数,设为1为使用。如果在应用程序中不需要改变运行任务的优先级,则将此常量设为0节省内存。
OS_TASK_CREATE_EN
此常量控制是否使用μC/OS-II中的OSTaskCreate()函数,设为1为使用。在μC/OS-II中推荐用户使用OSTaskCreateExt()函数建立任务。如果不使用OSTaskCreate()函数,将OS_TASK_CREATE_EN设为0可以节省内存。注意OS_TASK_CREATE_EN和OS_TASK_CREATE_EXT_EN至少有一个要为1,当然如果都使用也可以。
OS_TASK_CREATE_EXT_EN
此常量控制是否使用μC/OS-II中的OSTaskCreateExt()函数,设为1为使用。该函数为扩展的,功能更全的任务建立函数。如果不使用该函数,将OS_TASK_CREATE_EXT_EN设为0可以节省内存。注意,如果要使用堆栈检查函数OSTaskStkChk(),则必须用OSTaskCreateExt()建立任务。
OS_TASK_DEL_EN
此常量控制是否使用μC/OS-II中的OSTaskDel()函数,设为1为使用。如果在应用程序中不使用删除任务函数,将OS_TASK_DEL_EN设为0可以节省内存。
OS_TASK_SUSPEND_EN
此常量控制是否使用μC/OS-II中的OSTaskSuspend()和OSTaskResume()函数,设为1为使用。如果在应用程序中不使用任务挂起-唤醒函数,将OS_TASK_SUSPEND_EN设为0可以节省内存。
OS_TICKS_PER_SEC
此常量标识调用OSTimeTick()函数的频率。用户需要在自己的初始化程序中保证OSTimeTick()按所设定的频率调用(即系统硬件定时器中断发生的频率----译者注)。在函数OSStatInit(),OSTaskStat()和OSTimeDlyHMSM()中都会用到OS_TICKS_PER_SEC。