基于单片机的占先式实时内核的设计与实现

引言
在过去的10多年里,嵌入式技术得到飞速发展 。随着嵌入式应用的不断深入,嵌入式系统的复杂性、不确定性在不断提高,系统规模也在逐步增大;而产品的研发周期又在不断地缩短,这给嵌入式应用软件的开发带来了新的挑战 。因此,最近几年,对占先式实时内核的研究、开发与应用逐渐成为嵌入式系统的重点研究方向 。
面向对象技术由于内在地支持了对系统的抽象、分层和复用技术,能够很好地控制系统的复杂性,可很好地减轻嵌入式软件的开发者必然面临的由于芯片性能的提高、嵌入式操作系统平台等技术方面不断变化所带来的各种压力,因此在嵌入式领域得到越来越广泛的应用 。其中,统一建模语言是当今世界上面向对象系统开发领域中应用最为广泛的工具之一 。
1 统一建模语言UML
1.1 UML简介
UML(Unified Modeling Language)是一种标准的、用于面向对象和基于构件的软件系统建模工具,是一种用于对软件系统模型绘制可视化描述的工具 。UML以标准的、易于理解的方式建立能够描述复杂系统结构和过程的可视化模型,广泛用于描述信息管理系统、具有实时要求的工业系统过程、嵌入式系统、分布式系统、系统软件等 。
UML由图、视图、模型元素、通用机制和扩展机制等几个部分组成 。其中图是UML建模的关键,视图由图来描述;而图由模型元素结合通用机制、扩展机制等表示和语义组成 。
根据图在系统开发过程中不同阶段的应用,可以分为五类:用例图、静态图、行为图、交互图与实现图 。
这些图为系统的分析、设计提供了多种图形表达形式,应用于建模的不同阶段 。运用UML 。我们可以分析、设计几乎所有的软件和非软件系统 。当然,对于具体的系统应该根据系统的类型、系统的规模和开发需要绘制相应的图,不一定在一个系统中画出所有种类的图 。
1.2 UML与占先式实时内核
开发一个占先式实时内核与一般软件的开发一样,必然要经过开发的分析、设计、编码、测试四个阶段 。在嵌入式软件开发过程中,一般采用的是一种顺序开发方法 。然而,由于嵌入式产品更新很快,研发周期要求尽可能的短,同时在开发过程中应能动态地调整,所以,开发初斯所做的需求分析和设计,在后期的实现和测试中往往要做变动 。这反映了在软件开发过程中的需求分析、设计与具体实现之间有某种程度上的脱节,对软件实现后面的验证往往会带来很大风险 。另外,传统的嵌入式系统软件开发环境主要是对开发过程中软件实现和测试阶段的支持,是以源程序的开发和测试为核心的,缺少必要的需求分析和设计工具 。
UML为占先式实时内核的设计和实现提供了一套功能强大的建模工具 。由于UML融合了面向对象方法中的数据驱动和行为驱动两种方式,可以从各个方面描述实时系统的功能及反映实时系统的结束条件,可以为具有静态结构和动态结构的系统以面向对象图形的方式建模 。因此,使用面向对象的UML可以很好地完成占先式实时内核的建模 。
图2 占先式实时内核的类图
2 UML建模的具体应用
2.1 占先式实时内核概述
占先式实时内核具有多任务机制 。多任务机制是基于面向任务对象的,以任务为对象,以事件和时间为驱动,通过实时内核管理时间关键任务和任务的优先级,实现任务之间的调度、协同和仲裁 。
多任务机制的具体实现由调度程度来完成 。在系统程序运行过程中,当前任务完成之后,调度程序调出任务就绪表中优先级最高任务的入口地址,把CPU资源分析给该任务,使之执行 。如果该任务在执行过程中引起比它优先级高的任务进入就绪态,或者是中断服务程序使一个更高优先级任务进入就绪态,调度程序会把任务的当前程序指针、寄存器压入到该任务的任务堆栈指针指向的栈空间,保证现场,再把CPU资源分配给更高优先级任务,使高优先级任务开始执行 。高优先级任务执行完毕后,下一个在任务就绪表中的最高级任务先从该任务的堆栈数据区恢复寄存器、程序指针、程序状态,然后切换并执行该任务 。
采用占先式实时内核的思想设计单片机软件,可以合理应用单片机的有限资源并达到很高的实时响应,能降低软件设计的难度 。在本设计中,要求占先式实时内核应用于单片机中,任务数量最多不超过16个任务 。
占先式实时内核的功能需求是:
*支持外部异步事件中断、定时器中断、消息传递及任务消息到达时的调度;
*支持基于任务优先级占先调度,多种实时调度策略;
*任务的创建、运行、悬挂、唤醒与撤销;
*定时器时钟管理,任务延时处理;
*共享资源管理,保证任务的同步运行 。
由于占先式实时内核主要体现在软件设计方面,所以在此省略了构件图和配置图 。
2.2 用例图
图1为用例图,列出了该系统最基本的功能及功能描述,包括一系列用例和从系统中抽象出来的执行者 。

基于单片机的占先式实时内核的设计与实现
文章插图
(1)角色说明
*任务 。是嵌入式系统中用户想要实现的具体功能,是一个线程 。这些功能包括:输入、输出、数据处理、通信等 。
*中断 。用来通知占先式实时内核有一个事件发生,包括内部非屏蔽中断、定时器中断与外部异步时间中断 。
*系统时钟 。用来创建中先式实时核所需要时候节拍 。
(2)使用案例说明
*中断响应 。占先式实时内核通过对异步事件的处理,获得任务运行所需要的信号与数据,使任务得以正常运行 。
*中断级调度 。中断处理使得需要该中断信号的任务就绪,调度程序判断该任务是否为当前任务就绪表中最高优先级任务,进而决定该任务否立即进行 。
*任务就绪 。这里指的不是由于中断所引起的用户任务就绪,有两种方式:一个是,用户希望应用系统完成某个任务功能时,需要通知占先式实时内核,要求它创建该任务;另一个是,当当前运行的任务唤醒另一个任务时,使后者就绪 。
*任务级调度 。任务完成创建或被别的任务唤醒之后,调度程序判断该任务是否为当前任务就绪表中最高优先级任务,进而决定该任务是否立即进行 。
*任务运行 。当任务是当前任务就绪表中优先级最高的任务时运行该任务 。
2.3 类图
图2为类图,包括一组由所讨论系统中抽象出的类和它们之间的关系 。

基于单片机的占先式实时内核的设计与实现
文章插图
类中断的属性中,中断类型包括非屏蔽中断、外部中断与定时器中断,以便占先式实时同核进行相应的中断处理;中断向量号与单片机的中断向量号相匹配;中断嵌套状态表明当前中断是处于哪一层的中断嵌套中 。类中断有一个操作:中断处理,获取外部事件的信号和数据,并使上应的任务就绪,然后判断中断嵌套数是否为0 。若不为零,执行别的中断响应;如果为零,选择相应的调度程序进行调度 。
类调度的属性中,调度策略用于选择一种实时调度方案;调度类型包括中断调度与任务级调度;任务就绪表与任务悬挂表是调度时所需要数据结构 。类调度有一个操作:调度 。当当前任务是任务就绪表中优先级最高的任务时,当前任务继续运行;如不是,将当前任务运行时的状态与数据压入该任务堆栈,挂起该任务,然后调出最高级优先权任务的任务堆栈数据与状态,使最高级任务运行 。
类任务的属性中,任务ID表明是哪一个任务;任务优先级说明任务在所有任务中的运行优先权;任务状态说明该任务在占先式实时内核中是处在何种状态;任务堆栈保存任务切换时该任务的状态与数据 。类任务有四个操作:建立任务,在占先式实时内核中确认该任务;挂起任务,是任务由就绪状态转为挂起状态;恢复任务,是任务由挂起状态转为就绪状态;任务延迟,是任务自我延迟若干个时钟节拍,同时由就绪状态转为挂起状态 。
类消息的属性中,消息类型包括信号量与消息队列;消息ID表明该消息是哪一个消息;消息发送任务ID表明消息是由哪个任务发送的;消息接收任务ID表明哪些任务接收该消息 。类消息的操作有两个:发送消息,向一个或几个任务发送信息;接收消息,消息接收到某个信息 。
类任务定时器的属性中,任务定时器ID表明该定时器是属于哪个任务的;任务定时器时长说明该任务需要多长时钟节后才能再次运行 。类任务定时器中有两个操作:时钟节拍处理,处理任务的延时时钟节拍;任务超时处理,延时时间到的任务状态转为为就绪状态 。
类之间有以下联系:
*中断与任务之间,描述中断处理使相应的任务就绪;
*中断与调度之间,描述由于中断引起的中断级调度;
*中断与消息之间,描述中断处理时,相应的消息得到信号或数据;
*调度与任务之间,描述由于调度使任务就绪表中优先级最高的任务运行,任务由于等待信号而自我挂起,也会请求调度;
*任务与任务定时器之间,描述定时器对任务延迟时间的处理;
*任务与消息之间,描述任务对消息的信息请求,消息对任务信息的接收,消息发送使接收该信息的任务就绪;
*调度与消息之间,描述由于消息到而请求调度 。
2.4 状态图
状态图表示对象的行为,被用来描述一个系统的动态视图 。由于在占先式实时内核中系统的状态转换可以通过任务的状态转换显示出来,所以这里只给出对象任务的状态务,如图3所示 。

基于单片机的占先式实时内核的设计与实现
文章插图
任务在占先式实时内核中具有就绪、运行、挂起三种状态 。任务正在运行时,由于等待消息、自我延时或自我挂起,可以由运行状态进入挂起状态 。当等待的消息到、等待超时或延迟到时,任务状态就由挂起状态进入就绪状态,任务如果是任务就绪表中优先级最高的任务,通过调度和任务切换,进入运行状态 。
任务处于运行状态时,如中断发生,通过中断响应处理使任务就绪,并进行中断级调度:如果是任务就绪表中优先级最高的任务,继续运行;如不是,进行任务切换,任务由运行状态转变为就绪状态 。
处于就绪状态时的任务,也可以由于当前运行任务的请求从就绪状态转变为挂起状态 。
2.5 顺序图
占先式实时内核强调的是时间和事件 。在UML中,顺序图是具有这种特性的动态交互模型 。占先式实时内核的顺序力图4所示 。
图4展示了中断和任务、任务和任务之间的交互情况,这也是占先式实时内核的基本内容 。参与流程的对象在框图顶部的矩形中显示,共有五个对象:中断对象用来通知正在运行的任务有异步事件发生,并将正在运行任务的当前状态保存,暂停任务的运行;中断响应对象处理任务的中断服务代码,并将与中断信号相应的任务置为就绪状态,然后调度任务运行;低优先级任务和高优先级任务对象是用户希望系统所能实现的功能,低优先级任务只有当所有高优先级任务运行完毕或处于悬挂状态后才能运行;消息对象处理消息的发送与接收,消息接收表明有消息到来,然后消息发送使接收消息的任务状态转为就绪状态,进而进行任务级的调度,使高优先级的任务得以运行 。

基于单片机的占先式实时内核的设计与实现
文章插图
图4 占先式实时内核顺序图
3 占先式实时内核代码实现
在设计建模完成后,系统中对象的行为和它们之间的逻辑关系都已经清楚和确定下来,类和类的关系也得到进一步的抽象,这时需要选择具体的开发工具来实现占先式实时内核 。在设计建模完成后,系统中对象的行为和它们之间的逻辑关系都已经清楚和确定下来,类和类的关系也得到进一步的抽象,这时需要选择具体的开发工具来实现占先式实时内核 。我使用Cygnal IDE(集成开发环境)作为系统的软件开发平台 。IDE支持C语言和汇编语言的源程序级调试,具有丰富的开发与测试工具 。本系统采用C语言作为开发工具,具体设计不再详述 。
结语
占先式实时内核在嵌入式系统开发应用中一直被认为是个难点,这主要是由于其内在关系比较复杂和繁琐,采用以往的方法往往会陷入反复设计和调试的过程中,最后实现的程序也会由于种种原因存在不易觉察的隐患,这样就会限制软件的推广和应用 。
通过对占先式实时内核采用UML语言建模,可以比较清晰地认识到占先式实时内核在工作机制和具体实现过程各个阶段的工作内容,能够有效降低开发占先式实时内核所冒的风险,提高占先式实时内核编码和测试的效率与稳定性,缩短开发周期,这在嵌入式技术中有较好的实际意义 。
【基于单片机的占先式实时内核的设计与实现】 责任编辑:gt

    推荐阅读