在386单板机上实现非抢先式消息驱动机制实时操作系统(RTOS)

时间:2022-06-06 01:29:54

在386单板机上实现非抢先式消息驱动机制实时操作系统(RTOS)

摘要:针时SBC386EX系列单板机的特点,用面向对象的思维方法,构建一个基于非抢先式消息驱动机制的RTOS。具体阐述RTOS中消息的封装和消息队列、主循环、消息的获取和发送、定时处理以及核心管理模块的构成。

关键词:非抢先式;RTOS;单板机(SBC);消息驱动;VxWorks

中图分类号:TP311 文献标识码:A文章编号:1009-3044(2010)19-5361-04

1 应用背景

受到对华武器禁运及军工技术封锁,我国军用计算机技术(特别是军用嵌入式单板计算机)发展非常缓慢。智明达公司生产的基于DOS 6.22的SBC386EX系列计算机板由于成本低廉、系列完整,在军工机载产品中应用广泛。

SBC386EX系列计算机板选用国产化的军用级386EX CPU,由于受到空间尺寸限制只有512~640K BYTE内存,不能安装VxWorks操作系统,而采用标准BIOS和DOS6操作系统,使用Borland C++ 3.1 为开发环境,应用程序的实时性、可靠性和安全性完全由用户负责。为了提高软件实时性、软件模块化和简化软件开发,作者在多个工程应用的基础上编写了一个简单的基于386的RTOS,并应用于实际的工程开发。

作者参考操作系统原理,分析了VxWorks微内核任务接口和RTX51 RTOS内核。VxWorks 和RTX51都是基于抢占式优先级的多任务操作系统,操作系统提供给用户的服务主要有3类:系统服务、消息服务和功能接口,各个任务之间的参数传递是通过邮箱操作来实现的。但是二者对于作者的工程应用来说都相对复杂一些,而且要求的内存资源太多,对于只有512K的386计算机板来说根本不可能实现。而SBC386EX系列计算机板提供Borland C++ 3.1 为开发环境,使按照系统服务、消息服务和功能调用的结果利用C++开发自己的RTOS成为可能。

2 RTOS简介

通过查阅资料,作者在某刊物上看到对一种51单片机RTOS的介绍,实际上就是设置几个标志位,将中断产生的标志作为消息,在主程序中不断地查询这些标志位;当查询到某个标志位改变时,就调用相应的模块棋块。首先,这种方式不是严格意义上的消息驱动机制,而且对于各个模块之问的参数传递没有定义,不利于功能模块的划分;此外.由于标志位的查询是顺序进行的,例如当某中断发生,设置了标准BIT0和BIT5, 假设标志位的查询顺序是从BIT0开始的,本来按照正常次序要先执行模块0和模块5,但是在运行模块0时有另一中断发生,设置标志位BIT3,造成模块3比模块5先执行,这样就有可能造成不可预料的后果。将按位设置的标志改为先进先出FIFO的消息队列,并对模块调用进行抽象化,于是提出了以下基于非抢占式消息驱动机制的RTOS。

基于非抢占式消息驱动机制的RTOS是采用面向对象的思维方法,把各个功能模块看成是不同的对象,对象之间的通信称为发送消息。对象包含自己的数据和代码,数据表征对象的特征,代码用于相应消息,使对象进行某些动作。对象响应消息进行处理时不被中断,消息没有优先级之分,除非是中断到来,即消息驱动是非抢先式的。RTOS严格按照消息队列的顺序来“唤醒”相应的对象。基于非抢先式消息驱动机制的RTOS的系统运行框图如图l所示。

这样做有几个好处:

l)绝大多数应用场合下,“实时性”只是体现在对外部事件的及时响应和对数据的及时接收或者发送。这可借助SBC386单板机的硬件中断来实现,中断处理程序只对外部事件作必要的处理或者只是将接收数据放到预定的缓冲区立即返回,同时向消息队列发送一条信息通知系统。

2)采用面向对象的思维方法有利于模块的划分。在多人协同编程时显得尤为重要,它使得除管理模块外的模块负责人员只需要关心自己的那一个部分,各个功能模块的交互是透明的。

3)具备相当的通用性,如果需要增加新的功能模块,只需要编写新模块的代码.在RTOS中增加新模块的标志ID号,而其他部分可以保持不变。

3 RTOS构成

3.1 消息的封装和消息队列

基于非抢先式消息驱动机制的RTOS,其消息由4个部分组成:TaskID、FunCMD、Data1、Data2。其中,TaskID标志功能模块号,操作系统中的消息循环根据TaskID来确定这个消息发送给哪个功能模块的,占用1Byte;FunCMD指示该功能模块执行什么样的操作,占用1Byte;传递的参数Data1和Data2各占用1Byte的空间。

一个消息占用4Byte的内存,256Byte的消息队列可以容纳64条消息,借助2个16位指针:pWR、pRD:pWR=pRD时消息队列空;pWR≠pRD时消息队列非空。

3.2 RTOS主循环

RTOS的主循环主要的任务就是定时查询消息队列。若消息队列非空,则取出最“旧”的那一条消息,并唤醒相应的任务。设置3byte的当前消息区gMsgData和1byte当前任务gCurrTaskID。处理时,用当前那条信息的FunCMD、Data1、Data2来填充“当前消息区”,同时TaskID=gCurrTaskID。当调用功能接口FunCall时,FunCall根据gCurrTaskID唤醒相应的任务,同时把gMsgData区域中的参数传递给这个任务。

RTOS的主循环程序代代码如下:

RTOSMain()

{

Init();

while(TRUE)

{

dwgDogFeed(); //喂看门狗

if(!m_Time10msOverMaker)

{ //10ms 定时器到否

osGetMsg();//查询并处理消息

}

else

{ //10ms定时到

Time10msOverClear();

osPriTreat(); //定周期处理

}

}

}

osGeMsg()

{

if(!GetMsg())//从消息队列中取消息

{

return; //信息队列为空.返回

}

FunCall(gCurrTaskID);//唤醒相应的任务

}

注1:定周期处理函数osPriTreat在第4小节“RTOS的定周期处理”中有详细的介绍。

注2 : FunCall为功能接口,它根据gCurrTaskID查询对应功能函数的首地址,执行相应的任务。

注3:原则上1个任务的完成时间不应该超过10ms,如果超过10ms ,则必须进行任务的分割.具体见第4小节。

3.3 消息的获取和发送

消息获取函数仅仅由RTOS使用。主要功能是检查消息队列:如果消息队列为空.则时钟CP=l后立即返问;若消息队列非空.则取出FIFO中最“旧”的那一条消息。赋值gCurrTaskID=TaskID;FunCMD、Data1、Data2放在当前消息区gMsgData中。

名称:GetMsg

功能:从系统非空消息队列中取出最“旧”消息

入口参数: 无

出口参数:gCurrTaskID、gMsgData

返回;FALSE表示消息队列为空

BOOL GETMSG()

{

if(pWR==pRD)

{//信息队列空

return FALSE;

}

gCurrTaskID = MsgList[pRD].TaskID; //消息ID

gMsgData.FunCMD = MsgList[pRD].FunCMD;//命令

gMsgData.Data1= MsgList[pRD].Data1; //参数1

gMsgData.Data2= MsgList[pRD].Data2; //参数2

pRD++;

if(pRD>=MSGLENGTH)

{//测试信息队列的读写指针是否越界

pRD = 0;

}

}

消息发送函数SndMsg声明为PUBLIC函数,供外部模块调用。消息发送函数只是负责向消息队列发送消息,人口参数TaskID、FunCMD 、Data1、Data2。在消息发送过程中禁止任何中断响应,以免发送消息的过程被打断,在中断服务程序又向消息队列发送新的消息,这样会破坏消息的完整性,带来灾难。

名称:SendMSG

功能:发送消息到系统消息队列

人口参数:TaskID、FunCMD、Data1、Data2

出口参数:无

Void SendMsg(TaskID,FunCMD,Data1,Data2)

{

DisableInit(); //禁止中断

MsgList[pWR].TaskID= TaskID; //消息ID

MsgList[pWR].FunCMD = FunCMD; //命令

MsgList[pWR].Data1 = Data1; // Data1 ―> 参数1

MsgList[pWR].Data2 = Data2; // Data2 ―> 参数2

pWR++;

if(pWR>=MSGLENGTH)

{//测试信息队列的读写指针是否越界

pWR = 0;

}

enableInit(); //开放中断

}

3.4 RTOS的定周期处理

RTOS的定时处理是整个RTOS中相当重要的一个环节。SBC386EX系列计算机板I8253提供有两个16位定时器,我们选用TO作为RTOS的任务定时器,定时周期为10ms。对消息队列的操作、任务间的同步和长时任务(指执行时间超过10ms的任务)的分割都使用到任务定时器。另外一个定时器定时周期为1ms,以中断方式提供系统时钟和各种不同的延时时间基准,这样可以提供1――65535ms的延时时间间隔。

对周期事件通过定周期处理任务,查询系统时钟时间,如果设定的周期到则进行相应的周期事件处理,然后清楚对应周期标志。有多个周期事件,先处理要求响应快的短周期事件,下一个定周期处理任务再处理长的周期事件。

RTOS定周期处理以10ms 的定时周期延时间隔作为基准提供给所需要的任务使用。工程中的按键消除抖动的处理,延时时间通常在50ms 以上,在按键处理任务中,就有1个子函数GetKey50ms专门负责消除抖动的延时处理。

长时任务的分割也常常使用到任务定时处理。如果RTOS的定时基准设定为10 ms ,那么单个任务的完成时间和可能的中断处理时间总和不能超过20ms , 否则会使周期处理任务延迟10ms 。必须长时任务分割成多个短时任务,每个短时任务执行时间不超过10ms。前一个短时任务返回之前向RTOS发送一条消息,TaskID仍为该任务的TaskID,但是FunCMD参数变为下一个短时任务的FunCMD, Data1和Data2则存放下一个短时任务执行时所需要的参数。当RTOS从消息队列中取出这条消息时,就可以唤醒下一个短时任务继续执行,这样直到该长时任务执行完毕。

3.5 核心管理模块

核心管理模块是基于RTOS上的一个重要任务模块。主要功能是系统初始化,数据初始化;记录当前RTOS的一些关键参数和状态;提供一些公共功能函数供外部任务调用;协调各个任务的运行。

4 工程应用

某工程应用中要接收并处理一个设备R通过RS232串口以50ms周期上报的数据;接收并处理两个设备A和B分别通过RS422以1s周期上报的数据;查询接收并处理设备M数据包时间间隔大于100ms的数据,实时上报到设备E;实时采集按键状态,处理并分发到设备M、设备E、设备R、设备A和设备B;以600ms周期融合设备R和 设备A、设备B的数据,并上报融合结果到设备E,同时引导设备M。

该工程中的软件系统功能设计为:

1)有设备R、设备A和设备B共3个中断服务;

2)有数据融和、按键处理和429通信共3个处理任务;

3)有设备R、设备A和设备B数据准备好消息、融合数据上报和引导数据下发消息和按键发生消息,共3类消息;

4)有50ms定周期的设备R接收数据查询、GetKey50ms 50ms延迟抖动消除处理、500ms周期定时的设备A或者B接收数据查询、500ms定周期数据融合处理,共4类定周期处理。

5)设备R、设备A和设备B中断收数缓冲区,设备R、设备A和设备B中断发数数缓冲区,设备R、设备A和设备B数据报链表,融合数据上报和引导数据队列。

该工程中的软件工作工程详述如下:

对设备R和设备A、设备B的通信采用用中断收中断发,中断服务程序只完成将接收的数据放缓冲区并记录数据个数和将缓冲区待发数据发送,发送完成关发送中断。

对设备R和设备A、设备B的数据采用定周期处理方式:设定50ms周期定时对设备R的数据进行检查和数据包解析,将该完整数据包放到待处理数据链表,并向系统发融和处理任务发设备R数据准备好消息;分别设定两个500ms周期定时对设备A和B的数据进行检查和数据包解析,将该完整数据包放到待处理数据链,并向系统融和处理任务发设备A或者设备B数据准备好消息。

数据融和处理任务对设备R和设备A、设备B的数据进行去重叠处理;每600ms的周期到则进行融合处理,并将上报设备E的数据放到上报队列,将引导设备M的数据放到引导数据队列,然后向429通信任务发消息;429通信任务分别对引导数据队列和上报队列进行处理。

采用10ms周期的主任务循环对按键状态进行查询,查询到状态变化则启动GetKey50ms进行消除抖动处理,50ms延迟处理确认按键状态变化后向按键处理任务发按键状态消息。

按键处理任务处理按键消息,将要下发到设备R和设备A、设备B的数据,放到对应的中断发缓冲区,对要上报设备E和下发设备M到信息,向429通信任务发按键变化消息。

采用10ms周期的主任务循环对设备M的429接收数据进行查询,将要上报设备E的数据到上报队列,并向然后向429通信任务发消息。

此RTOS除在该工程项目中成功应用外,还用在其它工程中成功应用,并正用于新开发的工程中。

本文介绍的基于非抢先式消息驱动机制的RTOS已经应用于军工产品,在提高软件的模块化、增强软件运行的可靠性等方面具有很大的优越性。

参考文献:

[1] 汤荷美.操作系统基础[M].北京:清华大学出版社,2001.

[2] 马忠梅,马凯.单片机的C语言应用程序设计[M].北京:北京航空航天大学出版社,2007.

[3] 马蕾,佟振声.Windows应用程序中的数据交换机制及其应用[J].微计算机信息,2001(6).

上一篇:医学院校计算机教学改革初探 下一篇:邮件服务器无法连接故障及解决方法