ARM9嵌入式Linux開發-嵌入式GUI開發

2020-08-09 23:17:27

GUI的一般架構

圖形用戶介面GUI(Graphics User Interface)是迄今爲止計算機系統中最爲成熟的人機互動技術。一個好的圖形用戶介面的設計不僅要考慮到具體硬體環境的限制,而且還要考慮到使用者的喜好等。

由於圖形用戶介面的引入主要是從使用者角度出發的,因此使用者自身的主觀感受對圖形用戶介面的評價佔了很大比重,比如,易用性、直觀性、友好性,等等。另外,從純技術的角度看,仍然也會有一些標準需要考慮,比如,跨平臺性、對硬體的要求等。在嵌入式系統開發和應用中,我們所考慮的問題主要集中在圖形用戶介面對硬體的要求,以及對硬體型別的敏感性方面,在提供給使用者的最終介面方面只是要求簡單實用就夠了。

雖然不同的GUI系統因爲其使用場合或服務目的不同,具體實現互有差異,但是總結起來,一般在邏輯上可以分爲以下幾個模組:底層I/O裝置驅動(顯示裝置驅動、滑鼠驅動、鍵盤驅動等)、基本圖形引擎(畫點、畫線、區域填充)、訊息驅動機制 機製、高層圖形引擎(畫視窗、畫按鈕),以及GUI應用程式介面(API)。 

  • 底層I/O裝置驅動,例如,顯示驅動、滑鼠驅動、鍵盤驅動等構成了GUI的硬體基礎。由於此類裝置的多樣性,需要對其進行抽象,並提供給上層一個統一的呼叫介面;而各類裝置驅動則自成一體,形成一個GUI裝置管理模組。當然,從操作系統內核的角度看,GUI裝置管理模組則是操作系統內核的I/O裝置管理的一部分。

  • 基本圖形引擎模組完成一些基本的圖形操作,如畫點、畫線、區域填充等。它直接和底層I/O裝置打交道,同時,多執行緒或者多進程機制 機製的引入也爲基本圖形模組的實現提供了很大的靈活性。

  • 訊息不僅是底層I/O硬體和GUI上層進行互動的基礎,同時也是各類GUI元件如視窗、按鈕等相互作用的重要途徑。一個GUI系統的訊息驅動機制 機製的效率對該系統的效能,尤其是對響應速度等效能的影響很大。

  • 高階圖形引擎模組則在訊息傳遞機制 機製和基本圖形引擎的基礎上完成對諸如視窗、按鈕等的管理。

  • GUI API則是提供給最終程式設計師的程式設計介面,使得他們能夠利用GUI體系所提供的GUI高階功能快速開發GUI應用程式。

另外,爲了實現GUI系統,一般需要用到操作系統內核提供的功能,如執行緒機制 機製、進程管理。當然,不可避免地需要用到記憶體管理、I/O裝置管理,甚至還可能有檔案管理。

從使用者的觀點來看,圖形用戶介面(GUI)是系統的一個至關重要的方面:由於使用者通過GUI與系統進行互動,所以GUI應該易於使用並且非常可靠。此外,它不能佔用太多的記憶體,以便在記憶體受限的微型嵌入式裝置上無縫執行。由此可見,它應該是輕量級的,並且能夠快速裝入。

嵌入式GUI要求簡單、直觀、可靠、佔用資源小且反應快速,以適應系統硬體資源有限的條件。另外,由於嵌入式系統硬體本身的特殊性,嵌入式GUI應具備高度可移植性與可裁減性,以適應不同的硬體條件和使用需求。總體來講,嵌入式GUI具備以下特點:

  • 體積小;
  • 執行時耗用系統資源小;
  • 上層介面與硬體無關,高度可移植;
  • 高可靠性;
  • 在某些應用場合應具備實時性。

一個能夠移植到多種硬體平臺上的嵌入式GUI系統,應至少抽象出兩類裝置:基於圖形顯示裝置(如VGA卡)的圖形抽象層GAL(Graphic Abstract Layer)和基於輸入裝置(如鍵盤,觸控層等)的輸入抽象層IAL(Input Abstract Layer)。GAL層完成系統對具體的顯示硬體裝置的操作,最大限度地隱藏各種不同硬體的技術實現細節,爲程式開發人員提供統一的圖形程式設計介面。IAL層則需要實現對於各類不同輸入裝置的控制操作,提供統一的呼叫介面,如下圖所示。GAL層與IAL層設計概唸的引入,可以顯著提高嵌入式GUI的可移植性。

嵌入式GUI的支援庫

X Window是一種以點陣圖方式顯示的軟體視窗系統,最初是1984年麻省理工學院的研究成果,之後變成UNIX、類UNIX、以及OpenVMS等操作系統所一致適用的標準化軟體工具包及顯示架構的運作協定。X Window通過軟體工具及架構協定來建立操作系統所用的圖形用戶介面,此後則逐漸擴充套件適用到各形各色的其他操作系統上,幾乎所有的操作系統都能支援與使用X Window,GNOME和KDE也都是以X Window爲基礎。

FrameBuffer是出現在2.2.xx內核中的一種驅動程式介面。由於Linux工作在保護模式,所以使用者態進程無法像DOS那樣使用顯示卡BIOS裡提供的中斷呼叫來實現直接寫屏,Linux抽象出FrameBuffer這個裝置來供使用者態進程實現直接寫屏。在使用Framebuffer時,Linux將顯示卡置於圖形模式。Framebuffer就是模仿顯示卡的功能,相當於抽象的顯示卡硬體結構,實現了通過Framebuffer的讀寫直接對視訊記憶體進行操作。使用者可以將Framebuffer看成是顯示記憶體的一個映像,將其對映到進程地址空間之後,就可以直接進行讀寫操作,而寫操作可以立即反映在螢幕上。這種操作是抽象的、統一的。使用者不必關心物理視訊記憶體的位置、換頁機制 機製等具體細節,因爲這些都是由Framebuffer裝置驅動來完成的。

SVGALib是Linux系統中最早出現的非X圖形支援庫,是Linux下的VGA驅動函數庫。雖然它的品質有點低,支援顯示卡種類也不多,但是有許多的遊戲及程式都是用它來做開發,可以算是非官方的標準了。這個庫從最初對標準VGA相容晶片的支援開始,已經發展到對老式SVGA晶片,以及現今流行的高階視訊晶片的支援。它爲使用者提供了在控制檯上進行圖形程式設計的介面,使使用者可以在PC相容系統上方便地獲得圖形支援。

SVGALib作爲一個老的圖形支援庫,目前的應用範圍越來越小,尤其在Linux內核增加了FrameBuffer驅動支援之後,有逐漸被其他圖形庫替代的趨勢。

LibGGI是一個跨平臺的繪相簿,可以建立一個一般性的圖形介面,這個抽象介面連同相關的輸入(滑鼠、鍵盤、遊戲桿等)介面一起,可以方便地執行在X Window、SVGALib、FrameBuffer等之上。建立在LibGGI之上的應用程式,不經重新編譯,就可以在上述這些底層圖形介面上執行。

在Linux上,LibGGI是通過呼叫FrameBuffer或SVGALib來完成圖形操作的,可能速度比較慢。但在某些不支援FrameBuffer或vga的系統上,採用LibGGI仍然是一種不錯的選擇。

Xlib及其他相關函數庫.X Window系統中進行圖形程式設計時,可以選擇直接使用Xlib。Xlib實際上是對底層 X 協定的封裝,可通過該函數庫進行一般的圖形輸出。如果使用者的X Server支援DGA,則可以通過DGA擴充套件直接存取顯示裝置,從而獲得加速支援。

SDL是一個跨平臺的多媒體遊戲支援庫。其中包含了對圖形、聲音、遊戲桿、執行緒等的支援,目前可以執行在許多平臺上。SDL支援圖形的功能強大,高階圖形處理能力尤爲突出,可以實現Alpha混合、透明處理、YUV覆蓋、Gamma校正等。在SDL環境中能夠非常方便地載入支援OpenGL的Mesa庫,從而提供對二維和三維圖形的支援。

Allegro是一個專門爲x86平臺設計的遊戲圖形庫。最初的Allegro執行在DOS環境下,目前也可執行在Linux FrameBuffe控制檯、Linux SVGALib、X Window等系統上。Allegro提供了豐富的圖形功能,包括矩形填充和樣條曲線生成等,而且具有較好的三維圖形顯示能力。由於Allegro的許多關鍵程式碼是採用彙編編寫的,所以該函數庫具有執行速度快、佔用資源少的特點。

Mesa3D是一個相容OpenGL規範的開放原始碼函數庫,是目前Linux上提供專業三維圖形支援的惟一選擇。Mesa3D也是一個跨平臺的函數庫,能夠執行在X Window、X Window with DGA、BeOS、Linux SVGALib等平臺上。

DirectFB是特別爲Linux FrameBuffer加速的一個圖形庫,正在嘗試建立一個相容GTK(GIMP Toolkit)的嵌入式GUI系統。

嵌入式GUI系統簡介

Microwindows/Nano-X

Microwindows是Century Software的開放原始碼專案,設計用於帶小型顯示單元的微型裝置。它有許多針對現代圖形視窗環境的功能部件,可被多種平臺支援。     

Microwindows體系結構是基於客戶機/伺服器(Client/Server)分層設計的。最底層是螢幕和輸入裝置,通過驅動程式來與實際硬體互動;中間層提供底層硬體的抽象介面,進行視窗管理;最上層支援兩種API:第一種支援Win32/WinCE API,稱爲Microwindows,另一種支援的API與GDK(GTK+ Drawing Kit)非常相似,用在Linux上稱爲Nano-X,用於佔用資源少的應用程式。     

Microwindows支援1、2、4和8bpp(每畫素的位數)的灰度顯示,以及8、16、24和32 bpp的真彩色顯示。Microwindows提供了相對完善的圖形功能和一些高階的特性,如Alpha混合、三維支援和TrueType字型支援等。該系統爲了提高執行速度,也改進了基於Socket通訊端的X實現模式,採用基於訊息機制 機製的Server/Client傳輸機制 機製。Microwindows還支援速度更快的幀緩衝區。     

Nano-X伺服器佔用的記憶體資源大約在100KB~150KB。原始Nano-X應用程式的平均大小在30KB~60KB。與Xlib的實現不同,Nano-X仍在每個客戶機上同步執行,這意味着一旦發送了客戶機請求包,伺服器在爲另一個客戶機提供服務之前一直等待,直到整個包都到達爲止。這使伺服器程式碼非常簡單,而執行的速度仍非常快。

MiniGUI

MiniGUI是自由軟體專案(遵循LGPL條款發佈),其目標是爲基於Linux的實時嵌入式系統提供一個輕量級的圖形用戶介面支援系統。MiniGUI爲實時嵌入式操作系統提供了完善的圖形及圖形用戶介面支援。可移植性設計使得它不論在哪個硬體平臺、哪種操作系統上執行,均能爲上層應用程式提供一致的應用程式程式設計介面(API)。 在MiniGUI幾年的發展過程中,有許多值得一提的技術創新點包括:

圖形抽象層。圖形抽象層對頂層API基本沒有影響,但大大方便了MiniGUI應用程式的移植、偵錯等工作。目前包含三個圖形引擎,SVGALib、LibGGI,以及直接基於Linux FrameBuffer的Native Engine。

多字型和多字元集支援。這部分通過裝置上下文(DC)的邏輯字型(LOGFONT)實現,不管是字型型別還是字元集,都可以非常方便地進行擴充。應用程式在啓動時,可切換系統字元集,比如GB、BIG5、EUCKR、UJIS。MiniGUI的這種字元集支援,這種實現更適合於嵌入式系統。

兩個不同架構的版本。最初的MiniGUI執行在PThread庫之上,這個版本適合於功能單一的嵌入式系統,但存在系統健壯性不夠的缺點。在0.9.98版本中,引入了MiniGUI-Lite版本,這個版本在提高系統健壯性的同時,通過一系列創新途徑,避免了傳統C/S結構的弱點,爲功能複雜的嵌入式系統提供了一個高效、穩定的GUI系統。

在MiniGUI 1.1.0版本的開發中,參照SDL和Allegro的圖形部分,重新設計了圖形抽象層,增強了圖形功能,同時增強了MiniGUI-Lite版本的某些特性。增強的MiniGUI-Lite支援層的設計,同一層可以容納多個同時顯示的客戶程式,並平鋪在螢幕上顯示。新的GAL支援硬體加速能力,並能夠充分使用顯示記憶體;新GAL之上的新GDI介面得到進一步增強,可以支援Alpha混合、透明位塊傳輸、光柵操作、YUV覆蓋、Gamma校正,以及高階圖形功能(橢圓、多邊形、樣條曲線),等等。 

OpenGUI

OpenGUI在Linux系統上已經應用很長時間了。OpenGUI基於一個用匯編實現的x86圖形內核,提供了一個快速、32位元、高層的C/C++圖形介面。OpenGUI也是一個公開原始碼(LGPL)專案,最初的名字叫FastGL,只支援256色的線性視訊記憶體模式。目前,OpenGUI也支援其他顯示模式,並且支援多種操作系統平臺,比如,MS-DOS、QNX和Linux,等等,不過目前只支援x86硬體平臺。

OpenGUI也分爲三層:最低層是由彙編編寫的快速圖形引擎;中間層提供了圖形繪製API,包括線條、矩形、圓弧等,並且相容於Borland的BGI API;第三層用C++編寫,提供了完整的GUI物件集。OpenGUI提供了訊息驅動的API和BMP檔案格式支援,OpenGUI比較適合基於x86平臺的實時系統,可移植性稍差,目前的發展也基本停滯。

Qt/Embedded

Qt/Embedded(簡稱QtE)是一個專門爲嵌入式系統設計的圖形用戶介面的工具包,由挪威Trolltech(奇趣)公司開發,最初作爲跨平臺的開發工具用於Linux臺式機。它支援各種有UNIX 和Microsoft Windows特點的系統平臺。

Qt/Embedded以原始Qt爲基礎,是Qt的嵌入式版本,許多基於Qt的X Window程式可以非常方便地移植到Qt/Embedded上,因此,自從Qt/Embedded以GPL條款形式發佈以來,就有大量的嵌入式Linux開發商轉到了Qt/Embedded系統上,比如,韓國的Mizi公司。

Qt/Embedded通過Qt API與Linux I/O裝置直接互動,是物件導向程式設計的理想環境。物件導向的體系結構使程式碼結構化、可重用並且執行快速,與其他GUI相比,Qt GUI非常快,沒有分層,這使得Qt/Embedded成爲基於Qt的程式的最緊湊環境。

Qt/Embedded延續了Qt在X上的強大功能,在底層摒棄了X lib,僅採用FrameBuffer作爲底層圖形介面。同時,將外部輸入裝置抽象爲keyboard和mouse輸入事件,底層介面支援鍵盤、GPM滑鼠、觸控式螢幕,以及使用者自定義的裝置等。 Qt/Embedded類庫完全採用C++封裝,豐富的控制元件資源和較好的可移植性是Qt/Embedded最爲突出的優點。它的類庫介面完全相容於同版本的Qt-X11,使用X下的開發工具可以直接開發基於Qt/Embedded的應用程式GUI。

Qt/Embedded與QT/X11比較

總的來說,QtE擁有下面 下麪一些特徵:

  • 擁有同Qt一樣的API;開發者只需要瞭解Qt的API,不用關心程式所用到的系統與平臺。
  • 它的結構很好地優化了記憶體和資源地利用。
  • 擁有自己的視窗系統:QtE不需要一些子圖形系統。它可以直接對 底層的圖形驅動進行操作。
  • 模組化:開發者可以根據需要自己定製所需要的模組。
  • 程式碼公開以及擁有十分詳細的技術文件幫助開發者。
  • 強大的開發工具。
  • 與硬體平臺無關:QtE可以應用在所有主流平臺和CPU上。支援所 有主流的嵌入式Linux,對於在Linux上的QtE的基本要求只不過是 FrameBuffer裝置和一個C++編譯器(如gcc)。Qte同時也支援很 多實時的嵌入式系統,如QNX和WindowsCE。
  • 提供壓縮字型格式:即使在很小的記憶體中,也可以提供一流的字型 支援。
  • 支援多種硬體和軟體的輸入。
  • 支援Unicode,可以輕鬆地使程式支援多種語言。
  • 支援反鋸齒文字和Alpha混合的圖片。
  • QtE雖然公開程式碼和技術文件,但是它不是免費的,當開發者的商業化產品需要用到他的執行庫時,必須支付license費用,如果開發的東西不用於商業用途則不需要付費。
  • QtE由於平臺無關性和提供了很好的GUI程式設計介面,在許多嵌入式系統中得到了廣泛的應用,是一個成功的嵌入式GUI產品。

Qt/Embedded嵌入式圖形開發基礎

建立Qt/Embedded開發環境

交叉編譯器:arm-linux-gcc-4.3.3 

原始碼包:

  • qt-x11-opensource-src-4.5.3
  • qt-embedded-linux-opensource-src-4.5.3
  • tslib-1.4

原始碼包存放路徑:/usr/local/qt

編譯安裝qt-x11

qt-x11版本可以產生Qt開發工具,如designer、qvfb,嵌入式的開發有了qvfb,就可以不需 要實際的開發板,也可以開發Qt應程式。 進入解壓目錄,作如下執行:

# ./configure
# make
# cd tools/qvfb
# make
# cd ../ ..
# make install

一般不會出現錯誤,完了之後複製qvfb相關檔案到安裝目錄,命令如下:

# cp tools/qvfb/qvfb* /usr/local/Trolltech/Qt-4.5.3/bin

至此,qt-x11環境搭建好,時間比較長。安裝好的路徑預設爲:/usr/local/Trolltech/Qt-4.5.3

編譯觸控式螢幕軟體tslib

進入解壓目錄,執行如下命令:

	# ./autogen.sh# CC=arm-linux-gcc
	# echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
	# ./configure --prefix=/opt/ts/ --host=arm-linux --cache-file=arm-linux.cache
	# make
	# make install

編譯安裝qt-embedded

進入解壓目錄,執行如下命令:

	# ./configure -xplatform qws/linux-arm-g++ -embedded arm -shared -little-endian -qt-kbd-usb -qt-mouse-tslib -qt-libpng -qt-libjpeg -qt-gif -depths 4,8,16,32 -confirm-license -prefix /usr/local/Trolltech/QtEmbedded-4.5.3-arm/ -I/opt/ts/include -L/opt/ts/lib# make
	# make install
	# make clean

qt-embedded安裝在 /usr/local/Trolltech/QtEmbedded-4.5.3-arm下。

編寫指令碼檔案設定環境變數

在qt-x11安裝的目錄下新建setenv.sh指令碼檔案。

# gedit setenv.sh

新增如下內容:

PATH=/usr/local/Trolltech/Qt-4.5.3/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/Qt-4.5.3/lib:$LD_LIBRARY_PATH

在qt-embedded目錄下新建setenv-arm.sh。

# gedit setenv-arm.sh

新增如下內容:

QTEDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
PATH=/usr/local/Trolltech/QtEmbedded-4.5.3-arm/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib:$LD_LIBRARY_PATH

當我們要使用相應版本的Qt時,先執行對應指令碼。比如要用到qt-x11,在Qt-4.5.3中執行:

# . setenv.sh 或者source setenv.sh

之後就可以直接在終端輸入qmake之類了。

移植Qt庫檔案和tslib檔案

1:首先在目標板中建立跟ubuntu中一樣的目錄路徑(tslib安裝的路徑和QtEmbedded-4.5.3-arm的路徑)。

2:將交叉編譯好的tslib檔案cp到開發板中,include和lib下的pkgconfig不需拷貝。

3:拷貝字型檔檔案,把PC上/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib/fonts目錄下的字型檔拷貝到開發板的對應的目錄下(只需一種就可以)。

4:拷貝執行庫:在PC 機上覆制下列檔案到開發板的/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib目錄下:libQtGui.so.4,libQtCore.so.4,libQtNetwork.so.4

修改環境變數

export TSLIB_TSDEVICE=/dev/input/event0
export TSLIB_CONFFILE=/home/coco/tslib/etc/ts.conf
export TSLIB_PLUGINDIR=/home/coco/tslib/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0

export QWS_MOUSE_PROTO=tslib:/dev/input/event0
export QWS_DISPLAY=LinuxFb:/dev/fb0
export QWS_SIZE=320x240

export QTDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
export QPEDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:/home/coco/tslib/lib:/lib:$LD_LIBRARY_PATH

交叉編譯程式

進入pc端/usr/local/Trolltech/QtEmbedded-4.5.3-arm/examples/widgets/digitalclock目錄,執行如 下:

	# qmake -o Makefile digitalcloc.pro
	# make

然後將digitalclock拷貝至開發板/usr/local/Trolltech/QtEmbedded-4.5.3-arm/bin目錄中。發佈能在arm板上執行的程式一般步驟如下:

1:進入qt-x11,藉助qmake可將.pro工程檔案生成Makefile;方法如下     qmake -o Makefile xx.pro 。     之後執行make會生成x86平臺上可執行的程式。在qvfb下可以檢視該程式執行狀態。

2:進入qt-embedded,同樣藉助qmake,也可生成Makefiel,然後make之後,交叉編譯生成的就是arm平臺可執行的程式。所以從這裏可以看出,qt-x11不但提供了qvfb,designer等工具,更主要的是在發佈程式之前進行測試與偵錯,完美之後,再用交叉編譯工具將整個工程生成arm平臺可執行的程式。

測試

1:測試觸控式螢幕程式,進入\home\coco\tslib\bin ,執行:

# ./ts_calibrate

進行螢幕校正。

# ./ digitalclock

Qt/Embedded的使用

範例一

1 #include <QApplication>
2 #include <QLabel>
3 int main(int argc, char *argv[])
4 {
5 		QApplication app(argc, argv);
6 		QLabel *label = new QLabel("Hello Qt!");
7 		label->show();
8 		return app.exec();
9 }

範例二

第二個例子演示如何讓程式響應使用者的動作,這個程式由一個按鈕組成,當使用者 點選它時會退出程式。程式程式碼和Hello相似,只不過這裏會用QpushButton代替 Qlabel作爲主控制元件,並且我們將會把使用者的動作和一些程式聯繫起來。

1 #include <QApplication>
2 #include <QPushButton>
3 int main(int argc, char *argv[])
4 {
5 		QApplication app(argc, argv);
6 		QPushButton *button = new QPushButton("Quit");
7		QObject::connect(button, SIGNAL(clicked()),&app, SLOT(quit()));
8 		button->show();
9 		return app.exec();
10 }

QT控制元件會通過發射一個信號(signal)來表明使用者做個一個操作或控制元件的狀態發生 了改變。例如:當使用者點選按鈕時,QpushButton就會發射clicked()信號。信號可 以連線到一個函數上(一般叫做槽(slot)(國內一般稱之爲信號槽)),當信號發射後, 與之相兩的信號槽就會被執行。例如:我們將按鈕的clicked()信號和Qapplication 物件的quit()信號槽連線起來。

Qt/Embedded開發模型

嵌入式軟件開發通常都採用交叉編譯的方式進行,基於Qt/Embedded的GUI應用開發也採用這樣的模式。先在宿主機上偵錯應用程式,偵錯通過後,經過交叉編譯移植到目標板上。

Qt/Embedded直接寫入幀快取,在宿主機上則是通過qvfb(virtual framebuffer)來模擬幀快取。qvfb是X視窗用來執行和測試應用程式的系統程式。qvfb使用了共用記憶體儲存區域(虛擬的幀快取)來模擬幀快取並且在一個視窗中模擬一個應用程式來顯示幀快取,顯示的區域被週期性的改變和更新。