1. gzyueqian
      13352868059

      MiniGUI-Threads與MiniGUI-Lite下多串口通信的設(shè)計(jì)與實(shí)現(xiàn)

      更新時(shí)間: 2008-07-08 15:16:35來(lái)源: 粵嵌教育瀏覽量:1128

      目前,嵌入式Linux 設(shè)備已廣泛應(yīng)用到計(jì)算機(jī)、通信和工業(yè)控制等領(lǐng)域,負(fù)責(zé)各種數(shù)據(jù)的處理和存儲(chǔ),并進(jìn)行控制決策。MiniGUI 是一種面向嵌入式系統(tǒng)和實(shí)時(shí)系統(tǒng)的圖形用戶界面支持系統(tǒng)。在MiniGUI 環(huán)境下實(shí)現(xiàn)多個(gè)RS-232 串口通信,多串口實(shí)時(shí)協(xié)同工作,可滿足許多工控和通信場(chǎng)合需要。該文實(shí)現(xiàn)平臺(tái)為擴(kuò)展了5 個(gè)串口的Intel PXA255 Sitsang 的嵌入式硬件平臺(tái)以及基于嵌入式Linux2.4.19 的軟件平臺(tái),采用MiniGUI 作為圖形用戶界面,在MiniGUI 的兩個(gè)運(yùn)行模式下設(shè)計(jì)并實(shí)現(xiàn)了多個(gè)串口的實(shí)時(shí)雙工通信。

      1  MiniGUI 及其兩個(gè)運(yùn)行模式
      目前,在Linux 之上進(jìn)行(實(shí)時(shí)) 嵌入式系統(tǒng)開(kāi)發(fā),一般選擇如下幾種GUI 系統(tǒng):緊縮的X Window 系統(tǒng)、MiniGUI、MicroWindows、OpenGUI、QT/Embedded 等。該文采用MiniGUI 1.3.0 版本作為系統(tǒng)GUI ,應(yīng)用程序建立在圖形界面和嵌入式Linux 內(nèi)核之上。
      MiniGUI 可以編譯成兩個(gè)截然不同的運(yùn)行模式:一個(gè)是MiniGUI-Threads ,一個(gè)是MiniGUI-Lite 。MiniGUI-Threads 具有非常高的實(shí)時(shí)性,采用了線程機(jī)制。MiniGUI-Threads 的應(yīng)用程序,可以具有多個(gè)線程,每個(gè)線程有不同的功能和任務(wù),可以在不同的線程中建立多個(gè)窗口,但所有的窗口在一個(gè)進(jìn)程或者地址空間中運(yùn)行。不同的線程之間可通過(guò)MiniGUI 的消息傳遞機(jī)制進(jìn)行事件傳送和同步。但顯然,這種基于線程的結(jié)構(gòu)也導(dǎo)致了系統(tǒng)整體的脆弱,如果某個(gè)線程因?yàn)榉欠ǖ臄?shù)據(jù)訪問(wèn)而終止運(yùn)行,則整個(gè)進(jìn)程都將受到影響。不過(guò),這種體系結(jié)構(gòu)對(duì)實(shí)時(shí)控制系統(tǒng)等時(shí)間關(guān)鍵的系統(tǒng)來(lái)講,還是非常適合的。為了改變Threads 版本體系上的脆弱, MiniGUI-Lite 版本使用進(jìn)程機(jī)制。和MiniGUI-Threads 相反,MiniGUI-Lite 上的每個(gè)程序是單獨(dú)的進(jìn)程,每個(gè)進(jìn)程也可以建立多個(gè)窗口。在Lite 版本中,可以同時(shí)運(yùn)行多個(gè)MiniGUI 應(yīng)用程序。首先需要啟動(dòng)一個(gè)服務(wù)器程序mginit ,然后啟動(dòng)其他作為客戶端運(yùn)行的MiniGUI 應(yīng)用程序。如果因?yàn)槟撤N原因客戶終止,服務(wù)器不受影響,可以繼續(xù)運(yùn)行。

      2  MiniGUI-Threads 下多線程串口通信
        串口通信程序要做到實(shí)時(shí)性高,吞吐量大,程序的輸出與輸入是兩個(gè)需要并發(fā)執(zhí)行的操作,因此在MiniGUI-Threads 下采用多線程技術(shù)。在多串口的MiniGUI 環(huán)境下,通信程序至少應(yīng)具備如下兩個(gè)線程:主線程和串口監(jiān)聽(tīng)線程。主線程是串口通信程序的管理者,用來(lái)進(jìn)行人機(jī)交互的操作、部分串口操作和數(shù)據(jù)處理及協(xié)調(diào)好各線程運(yùn)行;串口監(jiān)聽(tīng)線程的職責(zé)是實(shí)時(shí)監(jiān)聽(tīng)各個(gè)串口的狀態(tài),一旦發(fā)生預(yù)定的事件,需要判斷是哪個(gè)串口有數(shù)據(jù)到來(lái),然后進(jìn)行一定的數(shù)據(jù)處理,并立即向主線程發(fā)送相應(yīng)消息,請(qǐng)求主線程對(duì)其進(jìn)行相應(yīng)處理。主線程在接到串口監(jiān)聽(tīng)線程發(fā)送來(lái)的消息后,立即調(diào)用相應(yīng)的過(guò)程函數(shù)進(jìn)行處理。串口通信程序可以在MiniGUIMain() 中通過(guò)CreateThreadForMainWindow 函數(shù)創(chuàng)建了兩個(gè)并發(fā)的線程,定義線程的入口函數(shù)地址并返回線程標(biāo)識(shí)符。
      在主線程中配置termios 結(jié)構(gòu)完成對(duì)串口的初始化操作,創(chuàng)建主窗口,建立控鍵并進(jìn)入消息循環(huán)。監(jiān)視線程和主線程同時(shí)啟動(dòng),此后串口監(jiān)視線程在后臺(tái)對(duì)各個(gè)串口進(jìn)行實(shí)時(shí)監(jiān)控,在監(jiān)視到預(yù)定事件時(shí),立即通過(guò)SendMessage 向主線程發(fā)送相應(yīng)的消息。與此同時(shí),主線程對(duì)監(jiān)視線程發(fā)送來(lái)的消息作出相應(yīng)的處理,讀取串口數(shù)據(jù)到緩沖區(qū)。主線程在處理完該消息后,串口監(jiān)視線程繼續(xù)執(zhí)行后面的程序代碼,對(duì)串口繼續(xù)監(jiān)聽(tīng)。主線程還可以同時(shí)進(jìn)行其他工作,比如接收或處理鍵盤(pán)、鼠標(biāo)一類的消息,數(shù)據(jù)顯示和響應(yīng)控件事件等。
      MiniGUI-Threads 下的SendMessage 的傳遞機(jī)制,可以用來(lái)發(fā)送“同步消息”。如果發(fā)送消息的線程和接收消息的線程不是同一個(gè)線程,發(fā)送消息的線程將阻塞并等待另一個(gè)線程的處理結(jié)果,然后繼續(xù)運(yùn)行;如果發(fā)送消息的線程和接收消息的線程是同一個(gè)線程,則與MiniGUI-Lite 的SendMessage一樣, 直接調(diào)用接收消息窗口的過(guò)程函數(shù)。
      MiniGUI 定義了MSG_USER 宏,用戶能夠自定義消息,并利用自定義消息傳遞數(shù)據(jù)。應(yīng)用程序可如下定義自己的消息:
      # define MSG_MYMESSAGE1 (MSG_USER + 1) ;
      # define MSG_MYMESSAGE2 (MSG_USER + 2) 。
      這種方式能有效防止數(shù)據(jù)的堵塞,避免線程出現(xiàn)死鎖等情況的出現(xiàn)。MiniGUI-Threads 下串口通信程序過(guò)程如圖1 所示。 
                                                        
                                                                                     圖1  串口通信程序框圖

      3  MiniGUI-Lite 下多進(jìn)程串口通信
        Lite 版本是支持客戶服務(wù)器(C/S) 方式的多進(jìn)程系統(tǒng),在運(yùn)行過(guò)程中有且僅有一個(gè)服務(wù)器程序在運(yùn)行,它的全局變量mgServer 被設(shè)為T(mén)RUE ,其余的MiniGUI 應(yīng)用程序?yàn)榭蛻?mgServer 變量被設(shè)為FALSE。各個(gè)應(yīng)用程序分別運(yùn)行于各自不同的進(jìn)程空間,如圖2 所示。
                  
                            圖2  Lite 版本下的多進(jìn)程系統(tǒng)
      串口通信程序要在MiniGUI-Lite 下做到實(shí)時(shí)性高,吞吐量大,管理協(xié)調(diào),可以參照MiniGUI -Threads 下串口通信程序的設(shè)計(jì),分別建立服務(wù)器程序和客戶監(jiān)聽(tīng)程序。
      根據(jù)MiniGUI-Lite 的特點(diǎn),多串口通信程序首先初始化并啟動(dòng)服務(wù)器程序。MiniGUI-Lite 下服務(wù)器程序名需要命名為mginit ,該程序?yàn)榭蛻魬?yīng)用程序準(zhǔn)備共享資源,并管理客戶應(yīng)用程序。首先,需要初始化OnNewDelClient 和OnChangeLayer 這兩個(gè)服務(wù)器程序特有的全局變量。這兩個(gè)變量是兩個(gè)函數(shù)的指針變量,分別用來(lái)監(jiān)視來(lái)自客戶和層的事件。當(dāng)客戶連接到mginit 或者斷開(kāi)與mginit 之間的套接字連接時(shí),如果程序設(shè)置了OnNewDelClient這個(gè)變量,將調(diào)用這個(gè)變量指向的函數(shù)。同樣,當(dāng)MiniGUI-Lite 的層發(fā)生變化時(shí),比如有新的客戶加入到某個(gè)層,或者層中的活動(dòng)客戶發(fā)生了變化,如果程序中設(shè)置了OnChangeLayer 這個(gè)變量,則會(huì)調(diào)用這個(gè)變量指向的函數(shù)。通過(guò)調(diào)用這些函數(shù),可以得到客戶標(biāo)號(hào)或者層的指針、客戶指針,mginit 程序就可以方便地訪問(wèn)MiniGUI 函數(shù)庫(kù)中的內(nèi)部數(shù)據(jù)結(jié)構(gòu),獲得當(dāng)前的客戶以及當(dāng)前層的所有信息,從而管理客戶程序。接著調(diào)用ServerStart up 函數(shù)啟動(dòng)mginit 的服務(wù)器功能,這個(gè)函數(shù)所做的工作比較簡(jiǎn)單,就是建立監(jiān)聽(tīng)客戶連接的套接字(/ var/ tmp/minigui) 并返回。調(diào)用SetDesktopRect 設(shè)定屏幕上由服務(wù)器獨(dú)占的矩形區(qū)域,客戶程序不能使用這塊矩形區(qū)域。設(shè)定之后,客戶程序就只能在這個(gè)獨(dú)占的區(qū)域以外進(jìn)行繪制。服務(wù)器初始化完畢后,用exec_app () 函數(shù)啟動(dòng)客戶串口監(jiān)聽(tīng)程序,完成服務(wù)器和客戶程序的建立。
      為了實(shí)現(xiàn)客戶和服務(wù)器之間的通訊,MiniGUI-Lite 通過(guò)經(jīng)過(guò)封裝的UNIX Domain Socket 處理函數(shù)在服務(wù)器和監(jiān)聽(tīng)程序之間進(jìn)行數(shù)據(jù)傳遞。服務(wù)器可以使用serv_listen ( ) 函數(shù)建立一個(gè)監(jiān)聽(tīng)套接字,并返回套接字文件描述符:
      # define L ISTEN_SOCKET " / var/ tmp/ mysocket"
      static int listen_fd ;
      BOOL listen_socket ( HWND hwnd)
      {
       if ( (listen_fd = serv_listen (L ISTEN_ SOCKET) )< 0)
      return FALSE;
      eturn RegisterListenFD ( fd , POLL_IN , hwnd ,NULL) ; /*在系統(tǒng)中注冊(cè)監(jiān)聽(tīng)文件描述符,在被監(jiān)聽(tīng)的文件描述符上發(fā)生指定事件時(shí), 向某窗口發(fā)送MSG_ FDEVENT 消息* /
      }
      當(dāng)服務(wù)器接收到來(lái)自客戶的連接請(qǐng)求時(shí),服務(wù)器hwnd 窗口將接收到MSG_ FDEVEN T 消息,這時(shí),服務(wù)器可接受該連接請(qǐng)求:
      int MyWndProc ( HWND hwnd , int message ,WPARAM wParam , L PARAM lParam)
      {
      switch (message) {
      case MSG_FDEVENT :
      if (LOWORD (wParam) = = listen_fd) { / *來(lái)自監(jiān)聽(tīng)套接字*/
      pid_t pid ; uid_t uid ; int conn_fd ;
      conn _ fd = serv _ accept ( listen _ fd , &pid ,&uid) ; /* 服務(wù)器調(diào)用serv_accept () 函數(shù)接受來(lái)自客戶的連接請(qǐng)求*/
      if (conn_fd > = 0) {
      RegisterListenFD ( conn _ fd , POLL _ IN ,hwnd , NULL) ; }
      }
      else {  int fd = LOWORD(wParam) ; /* 來(lái)自已連接套接字*/
      sock_read_t (fd , ...) ; /* 處理來(lái)自客戶的數(shù)據(jù)*/
      sock_write_t (fd , ....) ; }
      break ;
       }
      }
      上面的代碼中,服務(wù)器將連接得到的新文件描述符也注冊(cè)為監(jiān)聽(tīng)描述符, 因此,在MSG_ FDEVEN T 消息的處理中,應(yīng)該判斷導(dǎo)致MSG_ FDEVEN T 消息的文件描述符類型,并適當(dāng)處理。在客戶端,當(dāng)需要連接到服務(wù)器時(shí),可通過(guò)如下代碼:
      int conn_fd ;
      if ( (conn_fd = cli_conn (L ISTEN_SOCKET , 'b') ) >= 0) {
      /* 客戶調(diào)用cli_conn 函數(shù)連接到服務(wù)器*/
      sock_write_t (fd , ....) ; /* 向服務(wù)器發(fā)送請(qǐng)求*/
      sock_read_t (fd , ....) ; } /* 獲取來(lái)自服務(wù)器的處理結(jié)果*/

      4  兩種模式下需要注意的一些差別
      4.1  實(shí)現(xiàn)串口監(jiān)聽(tīng)程序的差別
      串口監(jiān)聽(tīng)程序擔(dān)當(dāng)著實(shí)時(shí)監(jiān)控的任務(wù),監(jiān)視預(yù)定義事件。在MiniGUI-Threads 下監(jiān)聽(tīng)線程需要給主線程發(fā)送預(yù)定義消息;在MiniGUI-Lite 下,監(jiān)聽(tīng)程序要完成和服務(wù)器的數(shù)據(jù)交換和通信。在這里,預(yù)定義事件就是多個(gè)串口中有一個(gè)串口有數(shù)據(jù)到來(lái)時(shí),判斷并鎖定這個(gè)串口進(jìn)行數(shù)據(jù)讀取。使用I/O 多路復(fù)用( I/O multiplexing) 技術(shù)可以很好地解決這個(gè)問(wèn)題。其基本思想是:先構(gòu)造一張有關(guān)描述符的表,然后調(diào)用一個(gè)函數(shù),它要到這些描述符中的一個(gè)已準(zhǔn)備好的進(jìn)行I/O 時(shí)才返回。在返回時(shí),它告訴線程哪一個(gè)描述符已準(zhǔn)備好可以進(jìn)行I/O。使用這種返回值,就可調(diào)用相應(yīng)的I/O 函數(shù)(一般是read 或write ) , 并且確知該函數(shù)不會(huì)阻塞。
      Linux 下的系統(tǒng)調(diào)用select 函數(shù)可以執(zhí)行I/O 多路復(fù)用。Select 函數(shù)原型為:
      Int select ( int maxf dp1 , fd_set *readfds , f d_set *writefds , fd_ set *exceptf ds , Struct timeval *tvpt r) ;
      在MiniGUI-Threads 中,因?yàn)槊總€(gè)線程都有自己相應(yīng)的消息隊(duì)列,而系統(tǒng)消息隊(duì)列是由單獨(dú)運(yùn)行的desktop 線程管理的,所以任何一個(gè)應(yīng)用程序建立的線程都可以長(zhǎng)時(shí)間阻塞,從而可以使用select系統(tǒng)調(diào)用。
      在MiniGUI-Lite 之上運(yùn)行的應(yīng)用程序只有一個(gè)消息隊(duì)列。應(yīng)用程序在初始化之后,會(huì)建立一個(gè)消息循環(huán),然后不停地從這個(gè)消息隊(duì)列當(dāng)中獲得消息并處理,直到接收到MSG_QUIT 消息為止。應(yīng)用程序的窗口過(guò)程在處理消息時(shí),要在處理完消息之后立即返回,以便有機(jī)會(huì)獲得其他的消息并處理。現(xiàn)在,如果應(yīng)用程序在處理某個(gè)消息時(shí)監(jiān)聽(tīng)某個(gè)文件描述符而調(diào)用select 系統(tǒng)調(diào)用,就有可能會(huì)出現(xiàn)問(wèn)題———因?yàn)閟elect 系統(tǒng)調(diào)用可能會(huì)長(zhǎng)時(shí)間阻塞,而由MiniGUI-Lite 服務(wù)器發(fā)送給客戶的事件得不到及時(shí)處理。這樣,消息驅(qū)動(dòng)的方式和select 系統(tǒng)調(diào)用就難于很好地融合。在MiniGUI-Lite 當(dāng)中,可以用以下幾種方法解決這一問(wèn)題:
      ①在調(diào)用select系統(tǒng)調(diào)用時(shí),傳遞超時(shí)值,保證select 系統(tǒng)調(diào)用不會(huì)長(zhǎng)時(shí)間阻塞;
      ② 設(shè)置定時(shí)器,定時(shí)器到期時(shí),利用select 系統(tǒng)調(diào)用查看被監(jiān)聽(tīng)的文件描述符。如果沒(méi)有相應(yīng)的事件發(fā)生,則立即返回,否則進(jìn)行讀寫(xiě)操作;
      ③ 利用MiniGUI-Lite 提供的RegisterListenFD 函數(shù)在系統(tǒng)中注冊(cè)監(jiān)聽(tīng)文件描述符,并在被監(jiān)聽(tīng)的文件描述符上發(fā)生指定的事件時(shí),向某個(gè)窗口發(fā)送MSG_FDEVENT 消息。

      4.2  處理同步問(wèn)題的差別
      在MiniGUI-Threads 下,對(duì)于共享資源的互斥性同步,可以使用“互斥(mutex) ”來(lái)解決,它是一種鎖或者信號(hào)燈,相關(guān)宏定義和函數(shù)包含在<pthread.h>中。當(dāng)一個(gè)線程調(diào)用pthread_mutex_lock () 函數(shù)鎖定某個(gè)Mutex 后,其它也要鎖定Mutex 的線程將被阻塞,直至Mutex 被釋放,從而達(dá)到資源的獨(dú)占。對(duì)于線程按正確順序執(zhí)行的順序同步,正如上面提到的,Sendmessage 的消息傳遞機(jī)制起到了“事件同步”的作用。當(dāng)一個(gè)線程要等待另一個(gè)線程的某個(gè)事件時(shí),會(huì)把自己掛起,直至另一個(gè)線程的相應(yīng)事件發(fā)生后把自己?jiǎn)拘选6贛iniGUI-Lite 中主要是應(yīng)用程序間的互斥性同步,可以使用Linux 下常用的“信號(hào)量機(jī)制”等方式解決多個(gè)進(jìn)程的共享與互斥問(wèn)題。

      5  結(jié)論
      在嵌入式GUI 環(huán)境下,需要結(jié)合操作系統(tǒng)和GUI 系統(tǒng)的特點(diǎn),充分利用兩者的資源來(lái)實(shí)現(xiàn)多串口通信。在Intel PXA255 Sitsang 板上實(shí)踐證明,在MiniGUI-Threads 下采用多線程技術(shù), 在MiniGUI-Lite 下采用服務(wù)器程序結(jié)合監(jiān)聽(tīng)程序,實(shí)現(xiàn)多串口全雙工通信,有效地解決了在串口通信中的實(shí)時(shí)響應(yīng)問(wèn)題,降低了數(shù)據(jù)的丟失率,提高了嵌入式系統(tǒng)的可靠性。

      免費(fèi)預(yù)約試聽(tīng)課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 呦呦婷婷视频在干 | 亚洲一区二区少妇 | 在线观看免费人成视频色 | 尹人香蕉久久99天天拍欧美 | 一级欧美一级日韩中文片 | 亚洲色在线观看 |