当前位置:Linux教程 - Linux系统 - 嵌入式系统下Microwindows的实现

嵌入式系统下Microwindows的实现

解放军信息工程大学基础部 吴升艳 胡冰 岳春生



前 言
目前,伴随着各种手持、无线及嵌入式设备的迅猛发展,相应的软硬件设计也发生了很大的变化。许多设备都使用了 Intel,MIPS,摩托罗拉的32位微处理器及大屏幕的液晶图形显示器。由于在过去10年中,桌面操作模式取得了巨大成功,于是许多开发者在嵌入式设计中开始使用类似于桌面的操作系统。其中一个比较好的方案就是使用Linux操作系统。由于Linux在嵌入式系统上具有桌面电脑的全部功能和特性,同时作为一种开放式源码,它允许各种各样的修改,并且还可以根据用户的特定要求进行定制。

Microwindows概述
Microwindows 是一个著名的开放式源码的嵌入式GUI软件,目的是把现代图形视窗环境引入到运行Linux的小型设备和平台上。作为X Window的替代品,Microwindows可以使用更少的RAM和文件存储空间(100K-600K)提供与X Window相似的功能。该体系结构允许设计者轻松加入各种显示设备、鼠标、触摸屏和键盘等。Linux2.2以上版本的内核代码允许用户将图形显存作为帧缓存进行存取,这样当用户对显示设备进行写入、控制时可以避免对内存映射区进行操作,因此用户可以在不了解底层图形硬件或没有使用过X Window的情况下进行图形程序的开发。这就是为什么现在在嵌入式系统中广泛使用Microwindows的原因。Microwindows 的可移植性非常好,基本上用 C 语言实现,只有某些关键代码使用了汇编以提高速度。Microwindows支持 Intel 16位、32位CPU,MIPS R4000 以及 ARM 芯片。
Microwindows 已经移植到一些掌上电脑。它的图形引擎被设计成能够运行在任何支持readpixel, writepixel, drawhorzline 和drawvertline, 和setpalette 的系统之上。在底层函数的支持之下,上层实现了位图,字体,光标以及颜色的支持。Microwindows支持新的Linux内核帧缓存结构,目前提供每像素1、2、4、8、16、24和32位的支持,另外还支持彩色显示和灰度显示,其中彩色显示包括真彩色(每像素15、16和32位)和调色板(每像素1, 2, 4 和 8 位)两种模式。在彩色显示模式下,所有的颜色用RGB格式给出,系统再将它转换成与之最相似的可显示颜色,而在单色模式下中则是转换成不同的灰度级。Microwindows支持窗口覆盖和子窗口概念、完全的窗口和客户区剪切、比例和固定字体,还提供了字体和位图文件处理工具。系统使用了优化的绘制函数,这样当用户在移动窗口时可以提供更好的响应。内存图形绘制和移动的实现使得屏幕画图显得很平滑,这点特别在显示动画、多边形绘制、任意区域填充、剪切时有用。
尽管Microwindows完全支持Linux,但是它内部的可移植结构是基于一个相对简单的屏幕设备接口,可在许多不同的RTOS和裸机上运行。这种特性的优点突出体现在用户设计的图形程序不需重写就可以被不同的工程共享,甚至可以运行在不同RTOS的不同对象上。
Microwindows系统以图形方式支持在主机平台上的仿真目标平台。这样,为Linux设计的Microwindows应用程序就可以在台式机上进行编写和开发而不用进行交叉编译就可测试和运行,并且直接在目标平台上运行。台式主机上运行的目标应用程序在X window中显示,这一点通过由Microwindows提供的X window屏幕驱动来完成,而不是通过帧缓存驱动。该驱动可以在像素位素和颜色深浅方面准确的模拟目标平台上的显示器,这样即使桌面系统是24位颜色,它也可以预览显示2bpp灰度级的目标应用程序。
Microwindows采用分层设计方法。在最底层,屏幕,鼠标/触摸屏以及键盘驱动程序提供了对物理设备访问的能力。在中间层,实现了一个可移植的图形引擎,支持行绘制,区域填充,剪切以及颜色模型等。在上层,实现多种API以适应不同的应用环境。

两种API:Win32和Nano-X
Microwindows中使用两种流行的图形编程接口:Microsoft Windows Win32/WinCE图形显示接口(GDI)和Xlib-like接口。前者应用于所有的Windows CE和Win32应用程序,后者就象Nano-X,应用于所有Linux X插件集的最底层。这样做可以让大量的Windows程序员开发图形应用程序,类似地也可以让Linux图形程序员用X接口开发图形应用程序。

Nano-X程序设计
本文中将用Nano-X API设计一个应用程序,并且讨论底层Nano-X程序的设计问题。Nano-X允许应用程序使用client/server网络协议或本地UNIX的主域接口。这样可以让几个应用程序在嵌入式设备或远程主机上运行而连接到Microwindows 的服务器上显示,这些特点使得Nano-X的操作非常像X Windows系统。在底层和大部分有关创建、删除窗口以及基本画图函数方面,Nano-X API和X的Xlib库很相似。另外,由于Microwindows被设计得很小,很多选项可以用资源包中的配置文件来进行设置。
下面的过程是建立一个Nano-X服务器的一个具体操作。
(1)初始化
Mcrowindows的资源包可以从http://microwindows.org下载。首先通过编译为主机和嵌入式目标平台建立一个Nano-X 的服务器。大多数的设置选项在配置文件中,所以在解压缩该资源包后进入microwin/src目录里编辑配置文件。下面是一些最重要的ARCH选项:
ARCH=LINUX-NATIVE
ARCH=LINUX-ARM
ARCH=LINUX-MIPS
ARCH=LINUX-POWERPC
为LINUX-NATIVE设置ARCH选项的目的是通知系统为当前运行的主机的Linux系统生成程序,而为其他选项设置ARCH则是要给一个RISC目标平台进行交叉编译。Microwindows中使用Arch.rules文件为每一个选项确定一个具体的设置。
下面的设置项用于提供Nano-X 服务器的图像支持:
HAVE_BMP_SUPPORT=Y
HAVE_GIF_SUPPORT=Y
HAVE_JPEG_SUPPORT=Y
设置JPEG图像选项时必须给出外部jpeg解压缩库的位置,例如LIBJPEG=/usr/lib/libjpeg.a。大部分系统都提供了解压库,也可从Microwindows网站上下载。另外一个重要设置项为选择是否提供大小可变字体支持,缺省项是在drivers/genfont.c中提供固定大小的位图字体。如想显示更大的字体,例如运行一个嵌入式浏览器,则可加入对TrueType 或 Adobe Type 1字体的支持。当选项确定以后,就可以根据显示的需要指定字体文件和像素点的大小,相应Microwindows会根据外部字体文件来生成大小适当的字体。最新的版本还可支持外部中文字体,其中所有的字体可用8位ASCII 码、 Unicode-16或UTF-8确定。其中UTF-8是Unicode的字节流编码方案。
有关字符支持的选项如下:
HAVE_FREETYPE_SUPPORT=Y
HAVE_T1LIB_SUPPORT=Y
HAVE_HZK_SUPPORT=Y
FreeType 和T1lib外部库分别用于支持TrueType 和 Adobe Type 1字体。这些库必须预先编译并且在配置文件中应指定其位置。Microwindows的网站上提供的所有库函数的下载。

(2)配置输出显示设备
由于Microwindows可在帧缓存系统和X Windows下运行,每一种显示驱动需要确定不同的设置。如果已经在Linux桌面上运行了X ,最好首先用X屏幕驱动建立系统,然后再为嵌入式设备生成一个帧缓存。下列选项用以配置X屏幕驱动:
X11=Y
SCREEN_WIDTH=640
SCREEN_HEIGHT=480
SCREEN_PIXTYPE=MWPF_TRUECOLOR0888
Microwindows通过这些选项在X 桌面上生成一个640x480的虚窗口,采用了8位色彩模式(红、绿、兰各用8位表示)输出。通过改变设置,可以在自己的桌面上控制目标嵌入式设备的仿真。例如仿真一个每像素16位的显示,SCREEN_PIXTYPE=MWPF_TRUECOLOR565。其中MWPF常数在src/include/mwtypes.h 头文件中有详尽的解释。
由于必须确定Linux系统内核支持帧缓存,因此设置帧缓存显示比较复杂。其设置如下:
X11=N
FRAMEBUFFER=Y
FBVGA=Y
VTSWITCH=Y
PORTRAIT_MODE=N
FBVGA选项引入了对16色VGA平面模式屏幕驱动的支持,但是该选项不可用于嵌入式系统。VTSWITCH选项允许Microwindows在帧缓存控制器上运行,按下ALT键可打开另一个虚拟控制器。一些嵌入式系统要求该选项关闭。PORTRAIT_MODE选项利用 L/R键来指定系统偏向于左/右运行,这一点和康柏公司出品的iPAQ PDA非常类似。

(3)Linux内核帧缓存支持
如果在运行Nano-X 服务器时显示“Can’t open /dev/fb0”,说明没有打开帧缓存或是系统内核没有引入帧缓存驱动。最简单的识别方式是当启动系统时是否看见一个企鹅图标,如果没有图标则确认下列选项是否在/usr/src/linux/.config文件中:
CONFIG_FB=y
CONFIG_FB_VGA16=y
CONFIG_FBCON_VGA=y
CONFIG_FBCON_CFB4=y
CONFIG_FBCON_CFB8=y
如果系统支持图形卡而不是标准的老式VGA,可以不用CONFIG_FB_VGA16选项。在重建内核之前,需要备份旧的内核,并且在lilo.conf文件中写明备份位置。启用帧缓存是大部分嵌入式系统的标准设置。
最后一项重要配置是为Microwindows指定鼠标或触摸屏输入的驱动程序。目前Microwindows上的鼠标是通过GPM工具或直接使用串口。指令GPMMOUSE=Y是选择GPM支持,设置之后运行gpm工具,例如‘gpm –R –t ps2’(支持 PS/2 鼠标);指令SERMOUSE=Y 是选择串口,同时还要在src/drivers/mou_ser.c 中设置MOUSE_TYPE和MOUSE_PORT两个环境变量。

(4)创建一个完整的演示系统
一旦在配置文件中设置好选项后,只要用户不再改动,参数就保持不变。同时在src目录中还有很多针对不同平台的样本配置文件。要创建一个 Nano-X 服务器并且运行演示程序,首先进入microwin/src目录,然后键入“make”。所有的程序在microwin/src/bin目录中生成,客户链接库也放在microwin/src/lib目录下。要运行演示程序,首先运行Nano-X 服务器 (在bin/nano-X下),然后再运行应用程序。

bin/nano-X & sleep 1; bin/world
在运行demonstration world plotting程序之前运行休眠命令以便服务器有一段时间来进行初始化。

创建一个简单的Nano-X应用程序
目的:绘制一个带兰边的白色方块。源代码如下
filename :sample.c
#define MWINCLUDECOLORS
#include
#include “nano-X.h”
int main(int ac,char **av)
{
GR_WINDOW_ID w;
GR_EVENT event;
if (GrOpen() < 0) {
printf(“Can’t open graphics ”);
exit(1);
}
w = GrNewWindow(GR_ROOT_WINDOW_ID, 20, 20, 100, 60, 4, WHITE, BLUE);
GrMapWindow(w);
for (;;) { GrGetNextEvent(&event); }
GrClose();
return 0;
}
在配置和检测了Microwindows初始安装程序以后,用“make install”指令来创建Nano-X 服务器、客户库和头文件,然后键入下述字符编译、链接和运行示例程序:
gcc sample.c –o sample –lnano-X
nano-X & sleep 1; sample
键入escape将退出服务器。
GrOpen()函数用于打开一个与正在运行中的Nano-X 服务器的连接,如果服务器没有运行,则返回-1值,应用程序此时显示错误信息并退出。GrNewWindow函数在屏幕(20,20)的位置上创建一个100x60像素的窗口。它的边界大小为4像素宽,兰边白底。然后调用GrMapWindow函数将刚刚创建的窗口在屏幕上显示出来。这是因为有时生成一组窗口很方便,但是要根据用户指令在屏幕上显示和移动。在屏幕显示窗口以后,程序进入“event loop”状态,等待下一个鼠标或键盘事件。

Expose event
更为复杂的Nano-X应用程序基本上遵从上述同样的逻辑结构。首先生成窗口,接着显示出来,然后程序进入“event loop”等待用户发出指令。在该例中并没有真正写出明确的代码在窗口上画出东西。在实现这一点之前,首先应当理解关于Expose event的概念。Nano-X API提供一套完整的在屏幕上划线、写字、画圆和显示图像的函数。当一个窗口被遮住时,Microwindows剪辑该窗口图画并保存下来,当被遮掩部分再次需显示时,可以重新刷新以显示以前的内容。刷新发生时,服务器首先向应用程序发送一个Expose event,要求重新绘制窗口中的内容。Microwindows在首次显示窗口后就发出一个expose event指令,这样重新绘制时使用的代码和原来显示时所用的代码完全一样,所以可以实现在原先位置上显示同样的内容。下例是用该机制显示一些文字,并在窗口移动后重新显示出来(sample2.c).
#define MWINCLUDECOLORS
#include
#include “nano-X.h”
int main(int ac,char **av)
{
GR_WINDOW_ID w;
GR_GC_ID gc;
GR_EVENT event;
if (GrOpen() < 0) {
printf(“Can’t open graphics ”);
exit(1);
}
w = GrNewWindow(GR_ROOT_WINDOW_ID, 20, 20, 100, 60,4, WHITE, BLUE);
gc = GrNewGC();
GrSetGCForeground(gc, BLACK);
GrSetGCUseBackground(gc, GR_FALSE);

GrSelectEvents(w, GR_EVENT_MASK_EXPOSURE);
GrMapWindow(w);

for (;;) {
GrGetNextEvent(&event);
switch (event.type) {
case GR_EVENT_TYPE_EXPOSURE:
GrText(w, gc, 10, 30, “Hello World”, -1, GR_TFASCII);
break;
}
GrClose();
return 0;
}
为了检测expose event代码,要运行带有NanoWM窗口管理器的Nano-X,这样就可以移动窗口了。命令如下:
bin/nano-X & sleep 1; bin/nanowm & sleep 1; sample2
在上例中用GrSelectEvents函数发送 GR_EVENT_TYPE_EXPOSURE事件到客户(client)程序中。为了保持client/ server之间的通信,server只向每个client窗口发送选择过的事件。程序中只有一句处理“Hello World”显示文本,该句在expose event例程中。GrMapWindow调用后立即产生一个expose event,这样即使窗口实际上并没有真正移动文字还是会被显示出来。

Graphics contexts
当绘制类似于线条或文字的图形时,所调用的每个画图函数都定义了一些可以影响操作的参数。每个调用除了一些类似于线条起始点这样的最基本信息以外,还由系统确定一些其它信息。前景色、背景色、与或画图模式以及一些其它参数保存在一个graphics context结构中。上例中graphics context用GrNewGC生成。该函数按标准设置生成graphics context,然后调用GrSetGCForeground设置前景文字颜色。在expose event处理过程中,graphics context通过window id设置文本绘制参数。

画图函数
上面已经介绍了生成程序、窗口和graphics context的基本知识,下面介绍Microwindows图形库中提供的其它函数。
GrClearWindow 清除窗口(Clear a window to it’s background color)
GrPoint 画点(Draw a single point)
GrLine 画线(Draw a line)
GrRect 画矩形(Draw a rectangle outline)
GrFillRect 填充矩形(Draw a filled rectangle)
GrEllipse 画圆/椭圆(Draw an ellipse or circle outline)
GrFillEllipse 填充圆/椭圆(Draw a filled ellipse or circle)
GrArc 画弧形(Draw an arc outline or pie wedge)
GrArcAngle 画弧形,指定浮点及角度(Like GrArc, but uses floating point and angles)
GrPoly 画多边形(Draw a polygon outline)
GrFillPoly 填充多边形(Draw a filled polygon)

GrBitmap 绘制位图( Draw a bitmap image)
GrDrawImageFromFile 绘制存盘的BMP、GIF、JPEG图形(Draw a BMP, GIF or JPEG file from disk)
GrDrawImageToFit 绘制cache中的图形,并适当调整其大小(Draw a cached image and stretch to fit)
GrArea 绘制内存队列中像素点(Draw from a memory array of pixels)
GrCopyArea 将某一窗口中的矩形区域复制到另一窗口(Copy a rectangular area fromonewindow to another)
Microwindows也支持一种从来不在屏幕上显示的窗口,即像素映射(pixmap)。像素映射窗口有时也称作虚窗口(offscreen),虚窗口不能在屏幕上显示出来,但是可以使用GrCopyArea函数复制到别的窗口。有时在expose events期间,CPU太忙而无法保存显示窗口的内容,普通窗口在被遮掩时又从不保存它们的内容,这时就可以使用像素映射。用GrNewPixmap函数可以生成一个像素映射。
希望上面的介绍可以帮助你理解在嵌入式Linux里,一个小型系统如何运行复杂的应用程序。Microwindows网站上还有更多的内容。