emWin 2天速成實例教程018_emWin的工作原理以及GUI_Delay()/GUI_Exec()函數

備註:(1)打開工程目錄下的"Exe\GUISimulationDebug.exe"即可看到效果。(2)看完教程000~005就基本會用emWin做項目,其他章節可以需要時再參考。

  看這篇文章,需要emWin一些基礎,如果你還不會用emWin,請先看完” emWin 2天速成實例教程000_如何快速入門ucGUI_emWin”,然後再看這篇文章。

  emWin整個工作流程,其實就和幾個東西有關:GUI_Init()、GUI_Delay()、GUI_Exec()、回調函數、窗體創建函數、窗體子父關係、PID輸入設備(比如鍵盤、鼠標、觸摸屏等等)。

  GUI_Init()是emWin的初始化,在初始化之前都不能執行任何和emWin相關的操作。在STemWin中,由於ST在emWin中加入了加密校驗,限制STemWin只能用於STM32,因此在GUI_Init()之前必須使能CRC,否則初始化失敗。

  可以說emWin所有動作都靠循環執行GUI_Delay()或GUI_Exec(),兩個函數沒有本質區別,GUI_Exec()包含在GUI_Delay()中,即GUI_Delay()在查詢OS_TimeMS變量(通過一個硬件定時器每1ms中斷加1)實現延時的時候不斷的去調用GUI_Exec(),區別在於GUI_Delay()帶延時1ms功能,而GUI_Exec()不帶延時,但兩個函數作用是一樣的。emWin的應用程序主函數一般是這樣的:

voidMainTask(void)

{

  GUI_Init();

  創建各個窗體;

  while(1) { GUI_Delay(xx);或GUI_Exec();}

}

  即利用不斷的循環執行GUI_Delay(xx);或GUI_Exec();帶動emWin所有的東西,包括:界面有改變時刷新界面,獲取觸摸屏、鼠標、鍵盤等PID輸入設備,執行用戶程序中的回調函數,反正就是帶動了所有的事情,其實就是emWin的動作引擎。

  GUI_Exec()首先會去查詢界面有沒有改變,如果什麼都沒有改變,是不會去刷界面,比如一個靜態界面放在那裏不去動它,這個時候emWin並沒有執行和顯示相關的東西。這就是爲什麼我們要在窗口上顯示一些文字(是單純顯示文字而不是用Text控件)或繪製一條直線,當我們在程序的另一處改變文字內容,但改變永遠不會顯示出來,因爲這些是2D繪圖而不是控件,emWin是不會感知到改變的(控件就會),所以要用WM_InvalidateArea()/ WM_InvalidateRect()/ WM_InvalidateWindow()函數使那個區域無效之後emWin才感知到那個區域有變化而去重繪那個區域。還有就是我們在程序裏改變了界面某個東西,而這個改變是不會馬上顯示出來的,要等emWin下一次執行GUI_Exec()纔會顯示出來。

  在每次執行GUI_Exec()時,都會調用用戶界面程序中的回調函數,包括所有窗體(顯示和隱藏)的回調函數都會進入一遍,看到這裏你應該知道在回調函數裏能做什麼事情了吧?所有事情都能做。

  窗體創建時emWin會在RAM中開闢空間儲存這個窗體所有控件的信息,比如位置、大小、ID、文字信息等待反正一大堆亂七八糟的東西,不過沒有儲存像素信息,因此都是小數據,像素顯示是在界面刷新時才由這些被儲存信息繪製出來的。還有創建窗體之後,會返回一個句柄,其實就是一個ID,有了這個句柄,我們就可以找到這個窗體以及該窗體中任何的東西。

  窗體創建時,其中一個參數是指定回調函數,所以每個窗體都會對應有一個回調函數,其實不單單是窗體纔可以有回調函數,具體到任何的控件(比如按鈕)都可以給它指定一個回調函數,在這個回調函數當中可以做一些和該控件相關的事情,只是一般沒必要而已。

  窗體創建時有些控件佔用RAM控件是固定的,比如按鈕等,但有些控件佔用RAM是動態的,比如ListView列表控件,你每增加一行emWin會另開銷一點RAM空間來儲存這一行的信息,所以在做界面程序的時候實時監控RAM空間是很有必要的,怎麼做?請看源碼。

  到這裏你應該明白一個界面程序控件越多,RAM的需求越大,當你一個界面有幾十個幾百個窗口時怎麼辦?這個時候你就要考慮一個窗口需要時才創建,不需要時把它刪除,經過這樣處理之後,其實任何時刻同時存在的窗體也就只有幾個而已(你不可能某一時刻要同時用到幾十個窗口吧?),只是有三點一定一定要注意:(1)創建窗體前先檢查句柄是否爲0(2) GUI_EndDialog()結束(刪除)窗體後將句柄請0;爲什麼要這樣做?有時候由於某些情況(疏忽或客觀因素)會在窗體沒有刪除的情況下又重複去執行該窗體創建函數。(3)密切關注RAM的佔用情況,一旦發現刪除窗體時沒有完全釋放RAM,或在某種操作下RAM不斷增加,可以及時改正回來。

  在emWin中所有的窗口都是連帶關係的(沒有單獨獨立的窗口),如果一定要說一個窗口是獨立的,那隻能說它的父窗口就是桌面背景(如果兩個窗體的父窗口都是桌面背景,那麼他們是沒有聯繫的)。有了這個連帶關係,我們可以找到任何子窗口或父窗口,還有點擊界面時也不會亂套掉,比如不用擔心點了這個窗口,另一個窗口跑到下面去了,因爲子窗口永遠顯示在父窗口之上,隱藏或刪除父窗口會連同子窗口一起隱藏或刪除的,管理起來是不是很方便?

  PID輸入設備(比如鍵盤、鼠標、觸摸屏等等)是怎樣和emWin關聯起來的呢?其實就是在調用GUI_Exec()時,在某個地方調用了GUI_StoreKeyMsg()、GUI_TOUCH_StoreState()函數把鍵值、觸摸座標儲存給emWin內部就可以了,emWin內部底層怎麼處理是它的事,跟我們沒有關係。還有emWin已經集成了觸摸屏程序和觸摸校準程序,其實就是方便用戶提供了一個觸摸屏程序模板而已,你也可以用它現成的也可以自己寫,用的話就按照它的方法修改下怎樣獲取觸摸AD值,但它的程序跑來跑去最終還是離不開GUI_TOUCH_StoreState()函數。

  說了那麼多,大家看的有點累,在以下實例中,通過程序演示更加深刻的說明emWin引擎函數GUI_Exec()的作用和注意事項:

 

總共做了4種方式去更新列表控件(爲什麼用列表控件做例子?因爲直觀),在程序中還用了user_code_delay()函數去模擬有時候用戶程序遇到大延時的情況(比如讀FLASH數據):

(1)mode_0:是規範的做法,在回調函數中模擬大延時的情況,然後增加一行列表,如此重複100次完畢;在實際應用中,這種方式顯示體驗較差,因爲等待時沒有調用GUI_Exec()函數及時刷新界面,按下的按鍵沒有及時彈起(看下圖),不知道的人還以爲是死機了;還有一個致命的問題是,如果觸摸屏是用中斷而不是查詢的方式處理的,在等待的時候不斷去點擊其他東西,這些點擊是有效的,只是界面還沒反應過來而已。


(2)mode_1:加入了GUI_Exec()函數,列表每增加一行用GUI_Exec()去更新(刷新)一次界面,這樣在等待的過程中界面是實時更新的,但由於在回調函數中調用了GUI_Exec(),而在GUI_Exec()中又去重複執行該回調函數,什麼風險?搞不好會無限嵌套!那怎麼辦?想辦法不讓程序又跑到該消息中去(回調函數是重複執行了,但沒重複進入該消息),即讓程序不會重複再跑到該點;怎麼實現?調用GUI_Exec()之前禁止掉該窗口的所有控件。再者,還會出現mode_1中程序等待時亂點屏幕而點擊有效嗎?不會,因爲不斷調用了GUI_Exec()函數去處理(消除)這些觸摸事件,而處理(消除)時所有東西都是禁止無效的。

(3)mode_2:和mode_1基本一樣,唯一區別是每添加完8行列表之後才GUI_Exec()刷新屏幕,筆者覺得這種方式是最好的,爲什麼?記住有這個思想就行,以後碰到一行刷一次反應慢時就會想起。


(4)mode_3:和mode_1有點相似,做個實驗而已。在調用GUI_Exec()之前沒有禁止掉所有控件,這樣在執行等待的時候不斷的去點擊Start按鈕,程序很快就會因爲嵌套而死掉。


 

總結:當你發現由於某些阻塞事件造成了界面沒有實時更新(壞處是顯示體驗差,界面會記住你的亂點擊),這個時候就在阻塞事件裏面(是裏面)插入GUI_Exec()以保證界面實時刷新,以及及時處理(消除)掉觸摸事件,甚至還可以在程序等待時做個提示窗口(因爲有GUI_Exec(),該提示是可以實時顯示的),只不過在GUI_Exec()之前記得禁止掉該畫面所有能點擊的東西。


源碼/軟件下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章