摘 要:较全面地介绍了用WindML2.0实现嵌入式实时操作系统VxWorks的显控程序设计,并给出了在WindML2.0中显示中文的办法。

关键词: 嵌入式实时操作系统,VxWorks,显控程序,WindML

1 引言

VxWorks是美国WindRiver公司(位于美国加州的Alameda市)开发的,具有工业领导地位的高性能嵌入式实时操作系统。VxWorks具有专门为实时嵌入式系统设计开发的操作系统内核,提供了高效的实时多任务调度、中断管理,实时的系统资源以及实时的任务间通信。基于VxWorks操作系统的应用程序可以在不同的CPU平台上轻松植。VxWorks只占用了很小的存储空间,并可高度裁减,保证了系统能以较高的效率运行。VxWorks以其卓越的性能被广泛地应用在通信、军事、航空、航天等实时性要求极高因而普通操作系统难以胜任的领域中,美国的F-16战斗机、B-2隐形轰炸机和爱国者导弹及火星控测器“探路者”上都使用了VxWorks。

在VxWorks的一般嵌入式应用中,可以选择Zinc For VxWorks或WindML(Wind Media Library)2.0来进行图形界面设计,它们都是VxWorks中的可裁减的多媒体组件,都可以在VxWorks操作系统上以较低的系统开销实现丰富多彩的图形界面。Zinc是一套完善的图形用户界面开发工具,适用于为高性能嵌入式设备开发低内存开销、本地编译的图形化用户接口。WindML是将 Zinc和硬件设备之间连接在一起的简捷高效的通用多媒体图形库。WindML主要提供了对芯片、输入/输出设备、音频/视频、帧缓冲器等的开放的应用程序接口(API)。因此,Zinc必须通过调用多媒体图形库WindML的应用程序接口来实现其图形功能。Zinc属于高端应用,Zine组件的价格也较贵,而WindML在大多数情况下已经足可以满足应用需求了。而且,WindML编程也比较灵活,有更大的自由度。因此,一般是选用WindML来作图形界面设计。

一个系统中的各个软件模块往往都要和界面产生一定的联系,有的模块需要反数据送到界面进行显示,界面程序有可能还要将数据处理后转送其它模块以实现模块间的通讯,还有可能根据数据的情况或键盘等的输入启动某些程序,例如,按键启动系统自检等。因此,在WindML的许多应用场合,兴驻要求WindML完成图形界面的显示,而且还要完成一定的控制功能。WindML常常被用来编写VxWorks下的显示控程序。

2 WindML的功能

WindML本身也具有可裁剪性和可配置性,以适应不同的应用要求。WindML可以为在VxWorks操作系统下开发的软件提供基本图形、视频和音频文面的操作。WindML适用于很多种CPU,可以提供独立于硬件的代码,同时它也支持鼠标、键盘等输入设备。

WindML的主要功能有二维图形API,事件服务,区域和窗口管理,多媒体,资源管理。其中,二维图形API是最常用的部分,包括基本画图操作(画线、矩形、椭图、多边形、点),选择字体输出文体,位图,光标管理,批量画图操作,图形上下文,色彩管理,双缓冲。事件服务程序是用来处理输入设备的输入请求的。它会把键盘、鼠标等输入的数据转化为事件并且传送给事件句柄,送到应用队列中。区域和窗口管理可以在界面上定义一个区域或多线程之间共享的窗口以供画图操作。多媒体API支持NTSC,PAL,SECAM等视频制式,DSP或混频器两种设备的音频输出,也支持JPEG图形格式。资源管理是指资源的建立、控制和删除。这些资源是指常规的WindML资源(例如设备和事件队列),内存管理,驱动器注册等。

3 WindML的使用

WindML的图形界面是以像素为单位的,一般采用配色表来选择颜色,先在配色表上配置好一种颜色的R、G、B值,并用其在配色表中的索引值代表这种颜色。

VxWorks支持C和C++。在WindML的编程中,用C和C++写的程序完全可以编译通过,但是C语言中的printf()等函数是无法在图形界面上输出字符的,必须用WindML提供的相应函数才行。例如,uglTextDraw(ge,x1,y1,length,text)是在屏幕上(x1, y1)处用前面已设置的字体输出英文字符串text。

WindML呆以使用多线程和多任务,但图形的资源是一定的,为防止多线程之间产生资源冲突,需要使用互斥信号量锁定资源。WindML中,一般在使用一组画图函数前,用UgiBatchStart(gc)通过互斥信号量锁定图形上下文、图形设备及缓冲,并且隐藏光标。在画图操作完成后,再用 UglBatchEnd(gc)释放被锁定资源以被其它的画图函数所使用。下面是一个基本画图操作程序:

void BasicExample(void) //程序入口
{
UGL_GC_ID gc; //定义图形上下文 gc
. . . . . .
uglInitialize(); //初始化
gc = uglGcCreate(devId); //创建图形上下文
. . . . . .
uglColorAlloc(devId, &colorTable(BLACK).rgbColor, UGL NULL, &colorTable(BLA CK).uglColor, 1); //色彩初始化, 允许已被定义的颜色被使用
. . . . . .
/* - - - - - - - - - - - - - - - - - - - - - - - 画
矩型- - - - - - - - - - - - - - - - - - - - - - - -
- - - */
uglBatchStart(gc); //锁定图形资源
uglForegroundColorSet(gc, colorTable(WHITE).uglColor); //设定前景色
uglBackgroundColorSet(gc, colorTable(GREEN).uglColor); //设定背景色
uglLineWidthSet(gc, 7); //设定线宽, 单位是像素
uglRectangle(gc, 60, 80, 240, 160); //画矩型
uglBatchEnd(gc); //释放图形资源
. . . . . .
}

4 中文显示

WindML本身不支持中文,甚至在VxWorks的开发环境Tornado中都无法直接输入中文。而在国内的应用场合,图形界面中不能显示中文往往是不符合要求的。这里可以用调用点阵字库的办法解决这样的问题。国家标准规定:汉字库分94个区,每个区有94个汉字(以位作区别),每个汉字在汉字库中有确定的区和位编号,这就是汉字的区们码。每个汉字在库中是以点阵字模型式存储的,一般采用16×16点阵(32字节)、24×24点阵(72字节),每个点用一个二进制位(0或1)表示,地应在屏幕上显示出来,就是相应的汉字。由于在中文环境下,输入的是汉字的内码,我们必须将之转换成区位码,算出偏移量,从字库中找到对应的汉字,将其字模显示即可。采用这种方法就需要有字库文件,还必须自己写一个调用字库显示汉字的函数。这样,在主程序中将需要显示的汉字用引号表示成一个字符串,调用显示汉字的函数即可。由于Tornado中无法直接输入中文,需要在其它的编辑器中(如UltraEdit)输入汉字字符串保存后在Tornado中打开即可。这种方法使用起来比较方便,但是字体大小受制于字库点阵大小及屏幕分辨率。想改变大小的话,要么换点阵字库,要么将汉字点阵在屏幕上的显示步长乘一个比例因子,不过,这样显示出来的汉字有时会有一些轻微变形。下面是一个在WindML中显示汉字的程序:

void PutChinese(int x,int y,int color, unsigned short chinese)
{ . . . . . .
cw = chinese/256; //内码第一字节
cq = chinese&0xff; //内码第二字节
cw = cw 20xa0; //算出位码, 位码cw 为: 内码第一字节20xa0
cq = cq20xa0; //算出区码, 区码cq 为: 内码第二字节20xa0
chpos = (cq *94 + cw - 95) * 32; //算出点阵字模在字库中的起始位置
rewind(clib); //回到字库的起点位置
fseek(clib, chpos, SEEK SET); //指针移动到点阵字模在字库中的起始位置
fread(dot, sizeof(unsigned short), 16, clib); //读入点阵字模

for(i=0; i<16; i++)
{
. . . . . .
for(j=x, pos=0x8000; j<x+16; j++, pos/=2)
{
if(dot(i) & pos) //如果点阵上该象素为1
uglPixelSet(gc, j, i+y, colorTable(color).uglColor); //用颜色color画一个象素
}
}
}

5 输入设备

一般来讲,嵌入式实时系统的人机交互性较弱。但是,在大多数情况下,还是需要通过键盘及鼠标等输入设备实现一定的人机交互。WindML支持键盘及鼠标等输入设备,下面是一个在WindML中识别有键按下并执行相应操作的程序:

void KeyPress(UGL_GC_ID gc)
{
UGL EVENT event;
if(uglEventGet(qId, &evenet, sizeof(event), 0)!=UGL_STATUS_Q_EMPTY && event_header_type==UGL_EVENT_TYPE KEYBOARD)//有键按下
{
UGL_INPUT_EVENT * pInputEvent = (UGL IN_PUT EVENET *)&event; //写入输入事件

if(pInputEvent->header_type == pInputEvent->modifiers&UGL_KEYBOARD_KEYDOWN)
{
if(pInputEvent-> tyep_keyboard_key == 48+i))// 若按数字键i
......//相应操作
}
}
}

6 实时时钟

在许多应用中,需要在界面上显示地钟,而WindML提供了符合POSIX 1003.1b标准的API以提供系统时间。首先,需要定义一个如下的结构用以存储时间:

struct timespec
{
    time_t tvsec;
    long tvnsec;
}
time_t 为WindML的类型定义, 实际上就是unsigned long型, 其定义的变量tvsec用以存储秒数。long型变量tvnsec用以存储纳秒数。然后就可以用

clock gettime(clock_id_t clock id, struct t imespec *tp)函数将系统时间存入time2 spec型结构中。
下面是一个按时钟方式显示系统自开机以来的时间的程序:

void DisplaySystemTime(UGL GC ID gc)
{
    struct_timespec NewSystemT ime; //定义结构
    clock gettime(CLOCK_REAL_TIME, &NewSystemTime);//获取系统时间
    if(NewSystemTime.tvsec != OldSystemTime) //如果系统时间有更新
    {
        int TempTime j;
        char buffer[20];
        TempTime = int(NewSystemTime.tvsec/(60*60)); //小时数

        if(TempTime < 10)
        {
            j = sprintf(buffer, "%d", 0) ; //小时数小于10 时, 首位补0
            j += sprintf(buffer+j, "%d", TempTime) ; //写入小时数
        }
        else
        {
            j = sprintf(buffer, "%d", TempTime) ; //写入小时数
        }
        j += sprintf(buffer+ j, "%c", ’:’); //写入时钟分割符号“: ”
        TempTime = int((NewSystemTime.tvec2TempTime3(60*60))/60) ; //分钟数
	......
        uglBatchStart(gc); //锁定图形资源
        uglTextDraw(gc, 580, 40, 21, buffer); //显示时间
        uglBatchEnd(gc); //释放图形资源
        OldSystemTime = NewSystemTime.tvsec;
        }
    }
}

7 结束语

本文对用来实现VxWorks下的显控程序设计的WindML2.0作了比较全面的介绍。并针对WindML中不能显示中文的缺点给出了在实用中已经得到验证的解决办法。并且以实例说明了用WindML进行显控程序设计时所要用到的基本图形操作、响应键盘输入和实时时钟的编程,这些实例是我们的在骗程实践中经过反复探索得到的。