史禮婷,張 騫,皮本杰,高佳雋,李春權
(1.航天行云科技有限公司,武漢 430040;2.湖北航天科工集團總體設計所,武漢 430040)
具有自動、章動、進動的三自由度轉臺,是飛行器進行動態性能測試的關鍵設備,廣泛應用于各類飛行器模擬試驗,特別是研究彈丸或引信在空中的飛行試驗,經常需要利用轉臺進行地面飛行狀況的模擬。
在轉臺的模擬試驗中,需要通過計算機良好的人機界面和強大的數據處理功能,對其進行控制。而其他設備與計算機進行數據通信的方法中,串口通信是經常使用的方法之一,它具有實現簡單、使用靈活、數據傳輸可靠等有點,因而應用廣泛[1]。
在具體工程實踐中,遇到兩個較大問題:一是三自由度轉臺既要實現實時控制,又要周期返回大量狀態信息,I/O數據量較大,導致程序處理速度降低。二是由于工程設計需要,轉臺程序需要在多個系統中復用。
為解決上述問題,本文設計了多線程的處理程序,并將串口和轉臺之間的通信協議進行了DLL封裝,具有以下優點:一是提高內存利用率。采用多線程技術,大大提高程序I/O處理能力,提高工作效率。二是簡化主程序開發。將協議解析和使用分開,簡化主控制程序的開發工作量。將串口訪問、協議解析、組幀等進行封裝,主控制程序的調用相對簡單,不必理解協議具體內容,簡化了主程序的開發。三是采用模塊化開發設計,便于升級和維護。將通信協議封裝在DLL里,協議有修改時,動態鏈接庫可獨立升級調試,不影響主控制軟件,便于維護。四是提高系統的適應性和安全性。DLL可以用不同的編程語言進行開發和調用,只要接口確定,內部可以獨立設計,相對安全、適應性高。
串口通信的實現通常有三個辦法:一是利用mscomm通信控件;二是采用微軟提供的內置的串口操作類System.IO.SerialPort;三是通過API函數。前兩種方法編程實現起來簡單,但是靈活性有所欠缺;第三種方法雖然編程難度較大,但是高效、自由、靈活,且能避免高速緩存中緩存溢出的缺點,因此,本文采用第三種方法。

圖1 串口通信操作步驟
如圖1所示,本文對串口使用采用以下幾個步驟:
(1)打開轉臺串口。由于串口是獨占性資源,因此,在應用程序設計中,需要避免因打開已占用的串口引發的錯誤。
(2)設置波特率等串口通信參數。利用GetCommState函數獲取串口當前配置,根據需要更改BuildCommDCB結構中的參數,利用SetCommState函數設置串口通信參數。
(3)啟動串口監控,即啟動串口相關線程(具體見2.2節)。
(4)數據收發。向轉臺發送指令進行控制;對串口數據接收,將接收到轉臺的數據解析
(5)關閉串口。在不需要使用此串口時,需關閉串口,釋放資源。
現今主流操作系統大多是多進程系統,即系統內可同時運行多個進程,而每個進程也可以同時執行多個線程。每個進程都有一個主線程,同時,可以建立另外的多個線程,進程中的各個線程都是并行執行的[1]。對于本系統,有大量的I/O測試數據,要想獲得較高的用戶體驗,創建多線程是最好的選擇。
在轉臺模擬試驗中,數據接收和發送是兩個相對獨立的過程,其中接收的部分數據是按設置的時間周期定時返回,接收頻率有可能會比較高,發送控制指令相對來說比較隨機,需要根據試驗設計的操作流程來確定。根據這些特點,可以在程序中將讀線程和寫線程分開,創建兩個專門的線程來分別讀取和發送串口的數據,如圖2所示,線程設計解釋如下:

圖2 通信線程設計
(1)主線程:用來響應用戶的各種操作,處理其他線程的各種響應。
(2)讀線程:監聽串口,響應各種串口消息事件,如標準事件EX_RXCHAR,響應接收到字符事件;事件EX_RXFLAG,響應接收到事件字符、置入輸入緩存區事件;事件EV_CTS,響應準許發送數據事件等。
(3)寫線程:向串口寫數據,采用API函數WriteFile實現,當有寫入錯誤需要返回錯誤代碼。
(4)中斷線程:中斷線程的優先級最高,用戶或轉臺隨時可以中斷讀和寫操作信號,提高程序的安全性和健壯性。
由于同一個進程中的所有的線程共享同一虛擬地址空間,且線程的中斷是匯編語言級的,所以有可能會出現線程沖突、阻塞或錯誤,因此需要采取線程同步措施來避免這些問題[1]。
(1)重疊I/O方式。線程的執行采用重疊I/O方式,避免了程序因等待函數返回而阻塞線程的問題,本文在CreateFile時傳遞OVERLAPPED結構參數,如果進行I/O操作的API函數返回后并沒有完成需要的操作,則調用GetOverLappedResult函數,來等待知道I/O操作完成后返回。
(2)臨界區對象。臨界區是一段代碼,在任意給定的時刻只能被一個線程使用,如果有兩個以上的線程同時訪問臨界區,只允許一個線程使用,其他線程保持等待或被阻塞。在初始化串口、讀串口、寫串口這些重要操作時,可以利用臨界區,避免重要操作沖突或阻塞。
在實現時利用全局變量來統一控制臨界區的使用,本文定義了全局變量CRITICAL_SECTION m_csComnuSync,用Enter-CriticalSection函數進入臨界區,LeaveCriticalSection釋放對臨界區的使用權。
(3)事件對象。事件對象時指用戶在程序中使用內核對象的有無信號狀態實現線程間的同步。通過API函數CreateEvent用于創建事件對象;SetEvent設置其為有信號狀態;ResetEvent設置無信號狀態。
(4)等待函數。等待函數是用來暫時掛起線程,待到監控對象產生一定信號則繼續執行該線程,避免對CPU的過多占用,提高程序的執行效率。利用WaitforSingleObject監控單個同步對象,用WaitForMultipleObject同時監控多個同步對象。
本文將采用動態鏈接庫(DLL)對通信協議進行封裝,主測試程序負責提供給測試人員操作界面相關功能,如串口打開、指令發送、指令解析、日志記錄等操作;DLL內實現具體的協議組幀、協議解析、串口初始化、線程啟動、串口讀、串口寫、關閉串口等功能,程序流程設計如圖3所示。

圖3 程序設計流程圖
具體設計如下:
(1)對于協議的實現,本文從協議發送和接收兩個方面出發進行設計。
(2)設計了協議處理類CFrameData和指令參數結構體strOrderParam,strOrderParam定義如下:

(3)在主程序對指令進行發送時,只需設置外環、中環、內環的指令參數,統一調用協議處理類里面的SetOrder函數即可。
(4)協議的解析,采用解析函數MatchAnswerString來處理,如果協議解析成功,函數將返回CFrameData對象指針。
(5)CFrameData類定義了一系列協議解析的返回的參數,如利用GetMsg來返回幀解析字符串,GetAngel來獲取外環、中環、內環的具體數值等。
本文的DLL采用MFC規則的動態鏈接庫,在DLL中有一些必須導出的函數,可以采用以下關鍵字將函數或類進行導出:可以使用導出關鍵字__declspec(dllexport)來聲明導出;關鍵字AFX_EXT_CLASS來聲明導出類;關鍵字extern “C”可以使編寫的函數供其他編程語言使用。
由程序設計分析得出,需要導出的接口如下:
(1)串口控制類。包含串口控制對象,以及串口打開、配置串口、數據發送和接收、關閉串口等串口控制函數,便于主程序調用控制實現同時操作多個串口。
(2)串口中斷函數。主程序可采用發送消息的方式,向DLL發送中斷消息,從而觸發DLL中的中斷函數,停止串口收發數據。
(3)協議發送控制和接收解析函數。協議調用的接口函數主要包括以下兩個部分:一是提供統一的指令發送接口函數,供主程序調用后,在DLL內完成組幀發送操作;二是提供統一的解析接口函數,將轉臺的狀態數據返回,顯示在主程序界面中。
程序采用Microsoft Visual Studio 2010開發平臺,開發語言為VC++,主程序開發語言為同一語言。程序界面如圖4所示。在工程實踐中,用于飛行試驗中對轉臺的通信控制試驗,試驗未出現信號中斷或異常,控制效果良好。

圖4 主程序界面
本文將協議和串口通信封裝在DLL中,并基于DLL實現了上位機與三自由度轉臺之間的串口通信。這種處理方法通信速度快、節省資源、簡化主程序的開發過程、遵循模塊化程序設計思想,具有較強的可拓展性和實用性,在轉臺試驗中得到了較好的應用。