摘 要:在嵌入式PC/104選用的操作系統中,建立圖形界面的人機接口軟件在實際中有很大的意義,本文針對相關工業控制項目,討論了有關基于消息處理機制的圖形界面人機接口的建立方法和相關問題。
Abstract: Establishing Graphics User Interface software used on PC/104 is significant in the real industry project. This article discussed the method and problem of designing GUI based on message handling mechanism.
關鍵詞:圖形界面;操作系統;控制系統
Keywords: Graphic User Interface. OS. Control System
1 引言
在新興的嵌入式PC/104應用領域,因其硬件的獨特特點,往往要求使用者的軟件操作系統要具備以下特點:
● 操作系統占用空間小,大約占用幾M或十幾M;
● 系統穩定性好;
● 對硬件配置要求低;
● 軟件開發靈活性高;
● 可以直接對硬件操作,控制響應速度快。
基于這樣操作系統的軟件有時又要求為用戶提供一個良好的人機界面,而這類操作系統往往在圖形界面的能力上與Windows相比有著明顯的不足,針對這一難點,本人在基于注塑機械手控制軟件的圖形界面開發上,進行了積極的探索,希望對相關的軟件開發有所幫助。
建立與用戶交互的圖形界面,除了要在系統上畫出圖形外(這類系統往往提供繪畫函數),最關鍵的是要在圖形上建立軟件對用戶操作的響應,即類以于Windows系統中,系統針對用戶對圖形上的各種功能圖案(如按鈕)的點擊要給予響應。所以建立圖形界面關鍵是要在用戶與軟件之間建立起一種交互機制,針對這一問題,本文以下部分將詳細進行討論。
2 事件控制機制
軟件程序有兩種基本控制機制,即順序驅動(或過程驅動)機制和事件控制機制,采用順序驅動機制設計的程序有一個明顯的開始、明顯的過程和明顯的結束,這樣的控制機制不利于建立友好的人機接口。還有一種程序控制的機制,即事件驅動機制,采用事件驅動機制設計的程序,由事件的發生來控制。這樣的程序控制機制能夠為用戶提供更好的人機接口。
采用事件驅動機制在人機交互很強的程序設計中比較適用,它給用戶在程序的運行上以選擇的余地。事件驅動控制機制多在基于Windows的軟件上使用,Windows上的軟件開發大多使用VC、VB等一些可視化開發平臺,這些平臺往往已經為用戶提供了事件驅動機制,用戶只需設計相關事件處理程序,在程序編制過程中并不涉及事件驅動機制的內部運行機理。而在本文所討論的開發環境下,必須自己搭建事件驅動機制平臺,在這一點上增加了程序設計的難度和復雜性。
采用事件驅動機制可以大大減少消息丟失的可能性,如當程序正在處理某一個消息時,又產生了新的消息,這時事件驅動機制會將新消息放入消息循環隊列,等到當前消息處理完后再處理新來的消息,從而防止了消息丟失的可能。這種程序控制方式非常適用于系統及時響應用戶的請求,與用戶建立友好的人機接口。這比采用順序驅動機制設計的程序對用戶來說更友好。
3 搭建事件驅動機制平臺
在用戶界面設計中,事件就是用戶通過各種輸入設備進行操作時所產生的各種信號,又稱消息。該消息是一種激發性的用于聯系用戶、計算機系統以及應用軟件之間交互活動的最基本信號。比如,用戶按下鍵盤上的某個鍵就可以形成一個消息,因為用戶的這一行為會對系統產生一個激發性的信號,使得系統由原來的某種狀態轉為另一種狀態。
消息的產生方式很多,主要由用戶通過按下鍵盤上的某個鍵或移動鼠標器并按下其上的某個鍵來產生。對于消息的產生者我們又稱之為消息源。消息源包括鼠標器、鍵盤、串行端口中斷、有關的軟件工具、設備驅動器、其它的輸入設備、其它的定位設備等等,甚至還可以是應用程序自身。
由于事件是激發人機交互活動最基本的因素,因而人機交互活動的關鍵就是研究由事件產生的消息處理技術及其算法。用戶界面的質量高低,在某種程度上取決于事件及消息管理技術的優劣
3.1 事件驅動機制的實現方法
以下列出本人所參與的注塑機械手控制軟件中,用于裝載鍵盤和鼠標事件所產生消息的消息隊列的數據結構:
struct event_define
{
struct event_define *Last;
unsigned char ScanCode;
unsigned char KeyState;
int MouseState;
int CursorX;
int CursorY;
struct event_define *Next;
};
下面介紹這一結構體中各個元素的功能:
● struct event define *Last 中的指針Last用于裝指向上一個消息隊列記錄的地址;
● struct event define *Next 中的指針Next用于裝指向下一個消息隊列記錄的地址。這兩個指針中的內容是在消息隊列初始化過程中完成的,使消息隊列形成首尾相鏈的隊列,同時頭指針與尾指針都指向消息隊列中的默認為0的消息數據記錄;
● unsigned char ScanCode中的ScanCode用于裝鍵盤敲擊事件發生時,被敲擊鍵的鍵值;
● unsigned char KeyState中的KeyState用于裝鍵盤敲擊事件發生時當時鍵的狀態(壓下或抬起);
● int MouseState中的MouseState用于裝鼠標事件發生時鼠標消息的內容,如鼠標左鍵壓下、左鍵抬起等;
● int CursorX和int CursorY中的CursorY和CursorX分別用于裝鼠標事件發生時鼠標在顯示屏上的坐標值;本項目中有關消息的裝入都是用中斷方式來完成的,下面簡要介紹有關中斷處理的具體方法。
在程序中,當鼠標、鍵盤和串口數據事件發生時,本程序把這類事件當做中斷來處理。這樣做的好處在于:CPU不必花大量的時間去查尋外部事件是否產生。因為,中斷事件何時發生是不能預知的,一旦有外部事件請求中斷,則會向CPU的接收中斷信號的引腳發出電信號,這些信號CPU是馬上可以知道的。如鍵盤何時有鍵按下,是隨機的,CPU不用反復查尋鍵盤狀態,而可以去執行其它程序,一旦有按鍵按下,鍵盤馬上產生中斷請求信號,CPU得知這信號后,便立即去執行為鍵盤服務的中斷程序。服務完后,CPU又恢復執行被中斷了的程序邏輯。
中斷方式處理事件的優點在于:執行速度快,可實時處理,不占用CPU過多的時間等優點。注塑機械手控制軟件中,中斷服務程序完成的主要任務就是把中斷事件裝入相應的消息隊列中。如:當有鍵盤敲擊時,就把鍵盤當前被敲擊的鍵值及狀態放入消息隊列,緊接著把尾指針指向下一個消息隊列數據記錄。如果在鍵盤敲擊前,頭指針與尾指針指向同一個消息隊列數據記錄,則鍵盤敲擊后,尾指針指向了下一個消息隊列數據記錄,這樣頭指針與尾指針就指向了不同的消息隊列數據記錄。若此時又有新的消息進入,則把它放入尾指針當前消息隊列數據記錄中,并把尾指針前移,指向下一個消息隊列數據記錄。而主程序則用循環結構不斷檢測消息隊列的頭指針與尾指針是否指向同一地址。若二者指向同一地址,則轉入下一次循環,再次檢測消息隊列的頭指針與尾指針是否指向同一地址,如此反復。若某次檢測發現頭指針與尾指針指向了不同地址則說明有消息裝入消息隊列,此時主程序將讀出消息內容,并將頭指針向前移動,使之指向下一個消息隊列數據記錄的地址,接著對當前消息做相應的處理,處理完成后再次檢測消息隊列的頭指針與尾指針是否指向同一地址,若不同則做相應處理,若相同則進入下一輪循環,如此反復。這樣由外部事件將消息放入消息隊列中,使尾指針前移;而主程序按先進先出的順序讀出消息隊列中的數據記錄,并使頭指針向前移動,然后根據消息內容做相應處理。這種事件處理方式又稱消息處理機制,如圖1。
在上述消息處理的過程中易出現一種特殊情況,即消息密度較高,主程序處理的速度低于消息裝入消息隊列的速度,最后將導致消息隊列裝滿,卻仍有新消息產生。針對這種情況,有兩種處理方法,一種是忽略新來的消息;一種是用新來的消息覆蓋原來的消息。本程序采用的辦法是忽略新來的消息,直到消息隊列空出新的隊列數據記錄,才將空出隊列后產生的消息裝入隊列。如果這種情況發生,就會導致隊列裝滿后和在隊列空出新的數據記錄期間產生的消息不被響應和處理,這種情況在程序實際運行中是不允許發生的。為了避免這種主程序處理的速度低于消息進入的速度的情況,可以采取的措施有三個:
● 中斷服務程序用匯編語言編寫;
● 主程序消息處理部分盡量簡潔;
● 設定消息隊列的數據記錄個數足夠大。
[align=center]

圖1 消息處理機制[/align]
中斷服務程序用匯編語言編寫,這樣可以使得中斷服務程序更簡練,占用CPU時間更少,從而為主程序運行提供了更長的時間。這樣就使得原來可能產生數據阻塞的情況,在用匯編語言編寫中斷程序后,主程序運行分配的時間加長,主程序能夠完成消息處理,及時空出消息隊列數據記錄,為新來的消息提供數據記錄空間。
主程序消息處理部分盡量簡潔,就會加快主程序消息處理的速度,從而使主程序消息處理的速度大于消息裝入的速度。
設定消息隊列的數據記錄個數足夠大,這樣就會使新來的消息有一個足夠大的消息隊列數據記錄空間,就好比一個較大的緩沖區,用以存放新來的消息數據。這樣即使新來的消息比較密集,也有一個比較大的空間存放。從而間接為主程序處理消息提供了時間。但實際上消息隊列數據記錄空間的大小是有上限的,所以在程序設計中這一問題的解決是由三個手段綜合解決的。