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分辨率。 由圖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。 li^iff編譯命令行: ./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所示。 6結 語 開發嵌入式軟件有基本固定的流程,并需要軟硬件平臺的相互配合。設計過程中出現的問題有可能是硬件設置的不合理引起的,也有可能是軟件代碼設計的不合理引起的。 在嵌入式軟件開發過程中,工具鏈的制作扮演了十分重要的角色。一個好的開發工具可以加快軟件的開發速度,提高軟件的質量。反之,則不但有可能會延長整個產品的開發時間,降低產品質量,嚴重的話還可能導致整個項目的失敗。 基于嵌入式系統的五子棋程序采用標準C語言來編寫,其中使用的SDL圖形庫本身也是個跨平臺的圖形庫,整個程序的可移植性比較高。游戲已經開發完成,運行比較流暢,具有一定的實用價值。 |