图形用户接口---MiniGUI在ECOS上的移植
[size=2]有了操作系统、TCP/IP协议栈、文件系统,再加上GUI就比较全面了。本节讲述MiniGUI在ecos上的移植。
MiniGUI是一款开源的GUI中间件,它将鼠标、键盘等设备驱动抽象为输入抽象层IAL引擎,将各种显示设备驱动抽象成图形抽象层GAL引擎,用户基于MiniGUI接口开发的程序可以跨平台使用。本来,最新版本的MiniGUI已经支持ecos了,但我使用的是MiniGUI学习版软件(MiniGUI-STR),它是MiniGUI的精简版本,只支持Linux和uClinux操作系统,此外还缺少许多高级特性。我用的开发板是SMARTARM2200,LCD驱动可以直接拿来使用,但我要玩挖雷,需要自己写鼠标和键盘驱动,为此,在移植上下了一番功夫。
MiniGUI属于中间件,它屏蔽了各种操作系统平台的差异,目前可以在Linux、uClinux、ecos、VxWorks、ThreadX、uC/OS-II等操作系统上运行,可以配置成三种运行模式:
1、MiniGUI-Threads 程序可以在不同线程中建立多个窗口,但所有窗口在同一个地址空间中运行。ecos、uC/OS-II、VxWorks就适合使用这种模式,根据MiniGUI作者的实际应用经验,ecos操作系统在一定程度上可以用来替代传统实时嵌入式操作系统。
2、MiniGUI-Lite C/S结构,每个程序是单独的进程,每个进程也可以建立多个窗口,适合于具有完整UNIX特性的嵌入式操作系统,例如:Linux。MiniGUI作者特别指出,要清楚嵌入式Linux/uClinux的方案并不是万能的。由于Linux/uClinux在实时性等方面的缺陷,使得它还不能用来开发对实时性、性能、成本等因素非常敏感的嵌入式产品。
3、MiniGUI-Standlone 不需要多线程/多进程的支持,独立运行,有些操作系统,例如uClinux,因某种原因缺少线程库支持,或者发现其线程库存在缺陷时适用这种模式。(MiniGUI作者就为uClinux的库缺陷头疼不已,某些调用就是不能正确运行,没办法,想出这么一招解决问题,呵呵。)
现在,我们已经知道ecos使用MiniGUI-Threads运行模式,下面就可以动手移植了,不过在移植前很有必要了解一下MiniGUI的工程组织方式。MiniGUI工程(MiniGUI库本身及示例程序包)是通过GNU的Automake/Autoconf脚本组织的。现在的各种自由软件,如:Apache、MySQL等都是利用Automake/Autoconf实现自动配置和编译的。用户只需要./configure、make、install这三条命令就可以把程序或函数库编译并安装到系统中。其中,configure是自包含的,也就是说用户不需要安装Automake/Autoconf环境就可以直接运行,就象用rar生成的自解压文件解压时不需要rar软件一样。configure是用脚本语言写的,可以独立运行,只有开发者生成configure文件时才需要Automake/Autoconf环境。
也许上面的论述令你感到复杂,其实核心思想就是要得到各个目录下的Makefile文件和一个config.h配置头文件。Makefile文件描述了各个文件之间的依赖关系、编译规则、编译选项和配置参数。config.h使用宏定义的方法给出了具体的配置参数。知道这么多足够了,就是这么简单,如果你对工程组织方式不感兴趣,现在就可以跳过这一段。因为MiniGUI要运行在各种平台上,所以不可能写一个固定的Makefile和配置文件,为了能自动探测平台环境,自动生成Makefile和config.h,需要编写configure脚本,但是手工写configure脚本太累了,有人就发明了自动生成工具Autoconf。
使用者只要书写config.h.in和Makefile.in就可以自动生成configure脚本,工作量减轻了不少。但是手工写.in文件还是嫌累,那么可以用Autoscan从源文件中抽取与函数调用和头文件有关的信息,生成configure.scan,以此为框架手写configure.in,避免疏漏,然后用Autoheader读入configure.in生成config.h.in。编写Makefile.am,使用Automake生成Makefile.in。通过这么一系列的自动化过程,开发人员只要写很少的代码就可以生成复杂的Makefile和自动化配置软件。综上,复杂化的原因是软件自动化配置引起的,好在我们只是针对SMARTARM2200板子上的ecos做MiniGUI移植,环境是固定的,第一次configure配置生成Makefile和config.h后只需关心这几个文件就行了。
首先,我们要准备ecos库,选择net模板(其支持posix兼容层),添加ROMFS和RAMFS包,为后面的编译工作准备*.h文件。从学习版光盘中拷贝或者从[url=][/url]网站下载以下文件:
libminigui-str-1.6.2.tar 源程序库
minigui-res-str-1.6.tar 资源文件:图标、光标、BMP图片、字体库、输入法
mg-samples-str-1.6.2.tar 示例程序
mde-str-1.6.2.tar 小游戏:挖雷等
将以上文件解压缩到同一个目录下,如:/t。修改各个configure配置选项,为了避免每次重复输入,可以把命令写入build文件。
#!/bin/sh
rm config.cache config.status -f
ECOS_DIR=/tmp/untitled_install
CC=arm-elf-gcc \
CFLAGS="-g -D__ecos__ -DEMBED -I ${ECOS_DIR}/include -I /tmp/minigui_lib/include" \
LDFLAGS="-L ${ECOS_DIR}/lib -Ttarget.ld -nostdlib -L /tmp/minigui_lib/lib" \
./configure \
--prefix=/tmp/minigui_lib \
--build=i386-elf \
--host=arm-elf \
--target=arm-elf \
--with-osname=ecos
配置相应的头文件位置、库文件位置、编译前缀等。操作系统选择ecos。然后运行make,根据出错情况修改源文件,主要是头文件没找到,函数不存在等错误。如果头文件不存在就注释掉对应的include,没有getpwuid函数,直接用“/”目录替代,没有random和srandom函数就用下面的方法替代:
#define srandom srand
#define random rand
总之就是调试,然后发现问题逐一解决。没有想象中的复杂,MiniGUI组织得很好,基本上只是一些小改动,或者删除,或者调整配置,本来MiniGUI就支持ecos嘛!
然后加入GAL和IAL,刚开始调试程序先把图像显示出来再说,IAL先不移植,直接使用dummy驱动。其实也不用移植,SmartARM板子的LCD驱动已经写好了,拿来就能用。不过不符合ecos规范,还可以进一步优化,基于ecos的线程效率高些,命名规则和移植性好,多少改改吧。
为了运行MiniGUI应用程序,我们还需要准备文件系统和运行时配置文件以及资源文件。一般,需要将这些文件存放到ROMFS文件系统里。我们配置的ecos支持romfs,ROMFS文件系统有两种实现方法:1、用程序头文件实现,作成数组放到H文件里,由编译器决定地址;2、用ROM映像实现,使用固定的flash地址。ecos提供了三个ROM文件系统制作工具:
mk_romfs.exe 将romfs目录转化成BIN
file2c.tcl 将BIN转化成H文件
gen 批处理,从目录直接生成H文件
先准备好文件目录:
/etc
MiniGUI.cfg MiniGUI运行时配置文件
/res
app MiniGUI应用程序资源
bmp BMP图片
cursor 光标图形
font 字体库
icon 图标
imetab 输入法
res资源文件直接由minigui-res-str-1.6安装,app应用程序资源由用户指定,MiniGUI.cfg中要配置相关项:
[system]
# GAL engine
gal_engine=commlcd 使用commlcd的LCD驱动图形引擎
# IAL engine
#ial_engine=dummy 使用dummy输入引擎,即无输入设备
ial_engine=pcsim 使用pcsim键盘鼠标仿真输入引擎
[rawbitmapfonts] 指定字体资源路径
fontfile0=/res/font/8x16-iso8859-1.bin
[varbitmapfonts]
fontfile0=/res/font/Courier-rr-10-15.vbf
[cursorinfo] 指定光标资源路径
cursorpath=/res/cursor/
[iconinfo] 指定图标资源路径
iconpath=/res/icon/
[bitmapinfo] 指定BMP图形资源路径
bitmappath=/res/bmp/
[imeinfo] 指定输入法资源路径
imetabpath=/res/imetab/
[appinfo] 指定应用程序资源路径
apprespath=/res/app
其他没有写出的项使用缺省值或进行类似设置。这样MiniGUI就可以知道各种资源被保存到了什么地方,使用什么输入输出引擎等等信息,此信息是运行时才确定的。
在cygwin中使用$ mk_romfs -v ./romfs minigui.bin将romfs目录制作成ROMFS文件系统映像minigui.bin,
在redboot中用lo -b 0x81010000 -r -h 192.168.0.1 minigui.bin下载映像到RAM中,
在redboot中用fis create -b 0x81010000 -l 0x160000 minigui将RAM中的映像烧写到flash中,并命名为minigui,
用fis list查看到redboot把此映像自动分配到了0x80020000地址。
在程序中定义CYGNUM_FS_ROM_BASE_ADDRESS为0x80020000,就可以使用这个ROMFS了。
如果想用头文件的方式实现,只要用file2c.tcl就可以转换为C头文件,如下:
sh file2c.tcl minigui.bin miniguifs.h(或者直接用gen从目录生成H文件)
把这个头文件包含在C应用程序里,并将ROMFS挂装在这个数组上即可。不过这样每次更改目录/文件都要重新编译程序。
现在可以编译调试bomb程序了,可以看到液晶屏上显示出挖雷的图片,不过速度比较慢,刷屏的过程看得很清楚,可以看到一列一列摆放地雷的过程,不过这对于了解程序运行过程有好处。
图像显示正确后,就可以加入鼠标和键盘驱动了。一种方法是编写USB键盘和USB鼠标驱动;另一种是用PC机串口模拟。两种方法都支持,本讲只介绍后一种,另一个在USB host章节再说。
上位机:
用PC机串口模拟鼠标键盘输入,上位机模拟程序用VC编写,在窗口内移动鼠标、按下/抬起按键或者有键盘输入时,模拟程序会从串口发送数据,格式为:
鼠标: 1LMRxxyy 0xxxxxxx 0yyyyyyy
键盘: 11111111 0000000k 0kkkkkkk
因为屏幕是240*320大小,所以用9位数就可以表示x、y坐标,L、M、R分别表示左中右按键情况,对应位为1表示按下,为0表示抬起,第一字节最高位为1,其他字节最高位为0,以便识别开始。键值用8位表示,第一字节为0xFF。
为了防止鼠标移出窗口,使用ClipCursor(&Rect)将鼠标限制在窗口内。
在CMainFrame::PreCreateWindow内修改窗口属性,使其显示240*320大小。
在OnLButtonDown/OnLButtonUp/OnRButtonDown/OnRButtonUp/OnMouseMove内记录鼠标位置和鼠标按键状态。
在OnCreate内打开串口,设置波特率等属性,创建一个50ms定时器。
在OnChar里判断输入键值。
在OnTimer里定时向串口发送经过编码的鼠标和/或键盘信息。
下位机:
MiniGUI等待鼠标键盘事件(中断),当发生事件时更新坐标值和/或读入键值。在ecos中使用select原语提供多个事件同时等待,select支持阻塞等待,等待期间不浪费系统资源。read原语使用非阻塞调用方式(non-blocking),因此ecos配置时要选:
serial device drivers\
support non-blocking read and write calls
注意:因为占用了串口0,所以printf在后面就不能使用了,超级中端也要关闭,以免冲突。使用open ("/dev/ser0", O_RDONLY|O_NONBLOCK)打开串口,read读串口,select等待事件,解码出x、y坐标,LMR状态和键盘键值,就能够实现MiniGUI输入引擎。因为窗口大小是240*320的,而且鼠标被锁定在窗口内,所以,可以不用看PC屏幕,直接参照LCD屏幕鼠标指针位置移动鼠标/键盘输入,实验证明,鼠标指针的反映是及时的,只是在初始化时有比较明显的延时。
执行示例程序painter,可以用鼠标画线,反映稍微有一点延迟,换个高速CPU就能解决问题。
所有的C文件都能编译通过后,就可以开始加到ecos组件仓库里了。去掉所有多余文件,只保留源文件、Makefile文件、config.h头文件,做个简单的CDL脚本,一个minigui模板,就可以开始编译库了。这样,MiniGUI就成了我们系统的一个组件,用的时候只要配置裁减一下就可以了,再也不必关心编译问题了。
MiniGUI模仿了Windows的消息驱动机制,对于理解Windows工作原理很有好处。MiniGUI的体积在嵌入式里有点偏大(有点偏向桌面系统的迹象),但经过裁减和在ecos上的移植,还是实用的轻量级GUI。加上BUG的不断减少,更多软件的加入(如浏览器等),MiniGUI还是值得使用的。[/size]