解放軍信息工程大學基礎部 吳升艷 胡冰 岳春生
前 言
目前,伴隨著各種手持、無線及嵌入式設備的迅猛發展,相應的軟硬件設計也發生了很大的變化。許多設備都使用了 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網站上還有更多的內容。