0 引 言
随着近年来计算技术、通信技术的飞速发展,特别是互联网的迅速普及和3C(计算机、通信、消费电子)合一的加速,微型化和专业化成为发展的新趋势,嵌入式产品成为信息产业的主流。嵌入式系统被定义为以应用为中心,以计算机技术为基础,软件硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。
嵌入式系统是面向用户、面向产品、面向应用。而广泛用于制造工业、过程控制、通信、仪器、仪表等,消费类产品,如果独立于应用自行发展,则会失去市场。嵌入式处理器在功耗、体积、成本、可靠性、速度、处理能力、电磁兼容性等方面均受到应用要求的制约,这些也是各个半导体厂商之间的竞争热点。
嵌入式处理器的应用软件是实现嵌入式系统功能的关键。软件要求固化存储,软件代码要求高质量、高可靠性。
1 开发平台
一个完整的嵌人式系统结构如图1所示,设计中采用的硬件平台为基于Intel Xscale架构的PXA255开发板,CPU运算速度为400 MHz,FLASH为32 MB容量的Intel Strata FLASH,SDRM容量为64 MB,USBSlave支持USB1.1,LCD支持640×480分辨率。
![](http://www.picavr.com/upload/2009_10/091015175678531.jpg)
由图1可以看出,一个完整的嵌入式系统不仅包含有硬件平台,还有运行于该硬件平台的操作系统和基于该操作系统的应用软件,而嵌入式LinUX只是众多嵌入式操作系统中的一个。
从20世纪80年代末开始,陆续出现了一些嵌入式操作系统,例如比较著名的有Vxwork,pSOS,Neculeus,QNX,ECOS,LYNX,Palm OS和Windows CE,这些专用操作系统都是商业化产品,其高昂的价格使许多低端产品的小公司望而却步;而且源代码的封闭性也大大限制了开发者的积极性。另外,结合国内实情,当前国家对自主操作系统的大力支持,也为源码开放的Linux推广提供广阔的发展前景。再者,对上层应用开发者而言,嵌入式系统需要的是一套高度简练,界面友善,质量可靠,应用广泛,易开发、多任务,并且价格低廉的操作系统。基于以上情况,采用嵌入式Linux操作系统作为开发的软件平台。
2 交叉编译工具链
在嵌入式系统软件的开发过程中,交叉编译工具链是极为重要的一环,设计并制作良好的交叉编译工具链是顺利实现软件开发的重要保障。
2.1 ARM-Linux的gcc交叉工具链
设计采用的Linux操作系统是经过修改与裁剪的ARM-Linux;使用的开发工具是非图形开发工具gcc。gcc交叉编译工具一般情况下需自行制作,制作方法较为简单,这里不做详细介绍。制作一条比较完整的ARM-Linux gcc交叉工具链主要用到如下软件包:
binutils工具包(ftp://ftp.gnu.org/gnu/binutils); gcc编译器(ftp://ftp.gnu.org/gnu/gcc); glibc函数库(ftp://ftp.gnu.org/gnu/glibc); glibc-linuxthreads包(ftp://ftp.gnu.org/gnu/glibe); linux内核(ftp://ftp.kernle.org/pub/linux/kernel)。
如果Linux内核低于2.6版本,还应下载相应的内核补丁。(ftp://ftp.arm.linux.org.uk/pub/linux/arm/kernel/v2.4/)
2.2 SDL图形库
为使程序运行的界面更加友好和美观,在设计中要使用到图形函数接口,这就意味着要向前面的工具链(采用gcc工具链版本为3.3.2)添加第三方的图形函数库。设计中采用的SDL(Simple DirectMedia Layer)图形库为免费的跨平台多媒体应用编程接口,具有丰富的函数库,便于开发者使用。
2.2.1 SDL常用到的开发包
(1)SDL_Image:提供显示多种格式的图像显示接口,它支持bmp,png,jpeg,gif,tiff等; (2)SDL_Draw:提供画点线圆等几何图形的接口(SDL_gfx也含有这样的功能http://www.ferzkopp.net/joomla/content/view/19/14/); (3)SDL_ttf:提供显示TTF文字的接口; (4)SDL_mixer:提供播放各种声音文件的接口。
把SDL编译到工具链用的并非PC机本身带的gcc编译器,而是要用到第2.1节已经做好了的交叉编译工具链。其中使用的pkg-Config工具版本要在0.15.0版本或以上。
SDL以及与SDL安装相关或有依赖关系的软件有:alsa-lib-1.0.15,audiofile-0.2.6,esound-0.2.38,freetype-2.1.9,jpegsrc.v6b,libid3tag-0.15.1b,libmad-0.15.1b,libpng-1.2.22,madplay-0.15.2b,SDL-1.2.12,SDL_draw-1.2.11,SDL_gfx-2.0.15,SDL_image-1.2.6,sdl_mad-0.1,SDL_mixer-1.2.8,SDL_ttf-2.0.9,tiff-3.8.2,tslib-1. 3,zlib-1.2.3。
2.2.2 交叉编译的主要步骤
主要步骤为:
(1)设定环境变量:PREFIX为安装目录;CROSS为ARM-Linux-;PKG_CONFIG_PATH为pkgcon-fig的路径;ARCH在这里设成ARM;HOST注意要是ARM-Linux,而不是i386-linux(这与前面做ARM-Linux-gcc不一样);BUILD设置为i386-linux。编译时一定要指定CC,NM,AR等变量,让它们跟交叉编译器对应的工具关联起来,否则编译时将会采用PC机Linux的gcc编译器进行编译,不能达到交叉编译的目的。
(2)按顺序依次编译如下软件:
zlib编译命令行:
./configure—shared—prefix=$PREFIX;make;make install。
freetype编译命令行:
./configure—host=$(HOST)--build=$(BUILD)--prefix=$PREFIX;make;make install。
libpng编译命令行:
./configure —host=$(HOST)—build=$(BUILD)--prefix=$PREFIX;make;make install。
libtiff编译命令行:
./configure—host=$(HOST)—build=$(BUILD)--prefix=$(PREFIX)/usr--without-x --enable-zlib --with-zlib-include-dir=$(PREFIX)/include—with-zlib-lib-dir=$(PREFIX)/lib--with-jpeg-include-dir=$(PREFIX)/include —with-jpeg-lib-dir=$(PREFIX)/lib;make;make install。
tslib编译命令行:
./configure—host=$(HOST)—build=$(BUILD)--prefix=$(PREFIX)--cache-file=$(ARCH)-linux.cache --sysconfdir=$(PREFIX}/etc--enable-static &&;make;make install。
Libmad编译命令行:
./configure—host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX);make;make install
libid3tag编译命令行:
./configure—host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX);make make install
madplay编译命令行:
./configure--host=$(HOST)--build$(BUILD)--prefix=$(PREFIX);make;make install
alsa编译命令行:
./configure--host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX)--disable-esd--disable-video-di-rectfb;make;make install。
audiofile编译命令行:
./configure--host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX)--disable-esd--disable-video-di-rectfb;make;make install。
esound编译命令行:
./configure--host=$(HOST)--build=$(BUILD)-prefix=$(PREFIX)--disable-esd--disable-video-di-rectfb;make;make install。
SDL编译命令行:
./configure--host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX)--with-esd-exec-prefix=$(PREFIX)--disable-video-directfb;make;make install。
sdl_image编译命令行:
./configure--host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX)--with-sdl-exec-prefix=$(PREFIX)--enable-sdhest;make;make install。
sdl_tff编译命令行:
./configure--host=$(HOST)--build=$(BUILD)--prefix=$(PREFIX)--with-freetype-exec-prefix=$(PREFIX);make;make install。
sdl_draw编译命令行:
./configure-host=$(HOST)--build=$(BUILD)prefix=$(PREFIX)--with-sdl-exec-prefix=$(PREFIX);make;make installsdl_mixer编译命令行:
./configure--host=$(HOST)--build=$(BUIUD)--prefix=$(PREFIX)--with-sdl-exec-prefix=$(PRE FIX);make;make install
sdl_mad编译命令行:
./configure--host=$(HOST)--build$(BUILD)--prefix=$(PREFIX)--with-sdl-exec-prefix=$(PREFIX);make;make install
至此,整个工具链就制作完成,打包并做好备份。这个工具链并不限于只用于制作该工具的Linux操作系统上使用,同样可用于别的Linux环境。
3开发环境设置
嵌入式系统通常为一个资源受限的系统,直接在嵌入式系统的硬件平台上编写软件比较困难,有时甚至是不可能的。一般嵌入式软件开发采用的办法是先在通用计算机上编写程序,然后通过交叉编译,生成目标平台上可运行的二进制代码格式,最后下载到目标平台上的特定位置上运行。
仅安装好Linux系统和开发工具,还没有真正完成设计所需要的开发环境。SDL只是一个图形的函数库,它向上提供图形函数接口,向下调用系统图形引擎来画图。它支持的图形引擎有很多,这里采用Linux内核自带的FrameBuffer,代码简洁,十分适用于嵌入式软件开发。
如果在开发板上的Linux的/dev/目录及其子目录下没有找到fb0或fb1或其他类似名称的设备时,则很有可能正在使用的内核没有FrameBuffer驱动。此时只能重新定制内核,选择对FrameBuffer支持和相关的驱动,再进行内核编译。
4主要解决问题
对于一个人机对弈的嵌入式五子棋游戏来说,主要应解决图形显示、人工智能算法、键盘事件处理3个问题。
4.1 图形显示
图形显示问题包括如何设计友好的人机交互界面;如何将光标和棋子显示在正确的位置上;如何在棋盘移动光标时去掉旧位置上的光标痕迹;如何在光标与棋子叠加时去除光标痕迹;如何进行下棋后的图像处理问题;如何从方形图片得到圆形棋子;如何进行汉字的显示问题等。
由于采用的开发板LCD规格为640×480像素,根据这个规格设计所使用的背景图片、黑棋子、白棋子、光标。为了使界面更加友好,采用图片字体显示,而不使用SDL_ttf中的字体。
棋盘与棋子采用3D效果,黑、白棋子与光标三者的图片大小一致,都是25×25像素,且背景色的色度空间都选用RGB(255,0,255),也就是粉红色。通过调用SDL函数库中的SDL_SetColorKey函数把粉红色作为过滤色。因此在显示这些图片时,看不到粉红色的背景,看起来就像图片做了切割一样。
光标在新的位置重画后,即使使用SDL_UpdateRect函数把整个屏幕都刷新,原来的位置仍然还有光标的图像存在,一直到程序的退出。解决这个问题采用的办法是当光标要在某个位置显示时,先把这个位置上与光标图片大小一样的区域记录起来,再显示光标,当光标移动时,把记录起来的图片重新画回到原来的位置,然后在画光标之前记录新的目标区域,如此重复。
用上面的方法解决光标的重画还存在一些问题,也就是当下棋时,光标离开这个位置时,使用下棋之前所记录的图片来重画了这个位置,结果就是当光标离开时,这个位置的棋子突然消失。一个简单而又实用的方法就是在选择下棋时,同时把棋子画到棋盘还有先前记录区域的图片上。这样就算棋盘上的棋子擦掉了,还可以从记录区域的图片上将它重新画出来。
4.2 对弈算法
对于一个对弈游戏来说,算法的智能性是非常重要的,但高智能的算法往往意味着要花费更多的CPU资源和更多的内存资源,而这两项对嵌入式系统来说,往往都是非常缺乏的。
由于嵌入式硬件资源的限制,使用了一个较简单的算法。利用一个15×15的二维全局数组来记录下棋的情况,1表示是人下的棋子;2表示是机器下的棋子,0表示是空位。当机器下棋时,使用4个函数linex,liney,line45,linel35从水平、垂直、45°角、135°角4个方向搜索,遇到对手的棋子就把分数加10。记录每个方向上可下棋位置的分数,选择分数高的位置下棋子。
4.3键盘事件处理
键盘事件响应问题包含如何及时响应键盘敲击,如何得到键值,如何作出正确的响应。考虑到软件的可移植性,没有直接使用Linux系统的事件处理函数来处理键盘事件,而是采用SDL本身的键盘响应事件函数,代码简洁清晰。
5五子棋游戏设计与实现
main主函数主要调用初始化函数InitGraph()与控制函数GameControl(),这两个都是全局函数,返回类型都是void。InitGraph()主要进行程序初始化和图片装载;GameControl()是主要的游戏控制函数。
5.1 InitGraph()函数
InitGraph()先调用SDL_Init(SDL_INIT_AUDI-O∣SDI_INIT_VIDEO)初始化一个终端屏幕,再使用SDL_SetVideoMode(640,480,16,SDL_SWSUR-FACE)把它设置成合适的模式。其中的“640,480”表示这是个640×480像素的屏幕;16代表的是色深。
SDL中使用SDI_Surface结构来记录屏幕区域或图片,过程用到的所有图片都使用这个结构来存取。
SDL_CreateRGBSurface函数用来创建一个SDL_Surface实例,而IMG_Load函数则可以把一张图片装载到一个SDI_Surface中去,接着使用SDL_SetColor-Key函数来设置透明色,这里把RGB(255,0,255)设置成透明色。因此,显示出来的图片中颜色为RGB(255,0,255)的区域都成了透明的。
SDL_BlitSurface函数用来把一个SDL_Surface的某一部分或全部画到另一个SDL_Surface上去。如果目标SDL_Surface是屏幕,那就是要在屏幕上显示此SDL_Surface,当然要使用SDL_UpdateRect把这个区域刷新一下才能看到结果。因为经常要显示图片和擦除图片,所以这也比较麻烦,再加上要光标移动时不仅要画出光标,更是要在画之前保存这块区域的图片。所以把SDL_BlitSurface包装成一个可以保存区域图片的画图函数——ShowPicture。
5.2 GameControl()函数
GameControl()是程序的主要控制模块。SDL_Event是记录事件的数据结构,通过SDL_PollEvent(&event)可以得到键盘和鼠标事件。对event结构的判断可得到想要的按键值和按键的动作。
电脑下棋的位置主要是通过调用ComputerThink函数得到的。CornputerThink函数采用第4.2节的对弈算法,调用linex,liney,Iine45,line135对某个位置进行水平、垂直、45°角和135°角4个方向的5个棋子内的范围进行扫描。如扫描水平这条线时,先从这个点向左扫描,遇到对手下的棋子把分数加10;遇到自己下的棋子,则停止这个方向的扫描,进而扫描相反的方向。如果这条线上有空地方可以下棋,则记下它的横坐标x、纵坐标y和它的分数。水平、垂直、45°与135°四条线都扫描完后,通过比较这几个可下棋的点的分数,选择分数高的点来下棋。
5.3 ShowPicture()函数
ShowPicture函数主要用显示图片,这个函数不仅实现了将图片画到屏幕指定的位置,还可以把目标区域备份起来,并可以自动更新屏幕。
5.4运行结果
设定宿主机ARM-Linux-gcc的路径为/usr/local/arm/3.3.2/bin;sdl-config的路径为/usr/local/arm/3.3.2/ARM-Linux/bin。交叉编译之前先设置好交叉编译工具的路径,并进行交叉编译。
#PATH=/usr/local/arm/3.3.2/bin:/usr/local/arm/ARM-Linux/bin:$PATH
ARM-Linux-gccsdl-config--libs--cflags-ISDL_image fivechess.c-0 fivechess
下载到开发板的运行截图如图2所示。
![](http://www.picavr.com/upload/2009_10/091015175678532.jpg) 6结 语
开发嵌入式软件有基本固定的流程,并需要软硬件平台的相互配合。设计过程中出现的问题有可能是硬件设置的不合理引起的,也有可能是软件代码设计的不合理引起的。
在嵌入式软件开发过程中,工具链的制作扮演了十分重要的角色。一个好的开发工具可以加快软件的开发速度,提高软件的质量。反之,则不但有可能会延长整个产品的开发时间,降低产品质量,严重的话还可能导致整个项目的失败。
基于嵌入式系统的五子棋程序采用标准C语言来编写,其中使用的SDL图形库本身也是个跨平台的图形库,整个程序的可移植性比较高。游戏已经开发完成,运行比较流畅,具有一定的实用价值. |