羅尹奇+劉力銀
摘要:為解決在不同平臺下Java串口通信問題,設(shè)計了一種通用的Java串口通信系統(tǒng)。該系統(tǒng)采用三層體系架構(gòu),包含了Java接口調(diào)用層、本地接口調(diào)用層和本地實現(xiàn)層;Java接口調(diào)用層負責(zé)定義串口通信的功能函數(shù),本地接口調(diào)用層則由JNI(Java Native Interface)技術(shù)生成,定義本地代碼的函數(shù)調(diào)用接口,本地實現(xiàn)層利用操作系統(tǒng)API實現(xiàn)串口通信功能。根據(jù)該系統(tǒng)設(shè)計,以Windows平臺為例,實現(xiàn)了在Windows平臺下的Java串口通信。通過功能測試實驗表明該系統(tǒng)設(shè)計能正確地完成串口通信功能。
關(guān)鍵詞:JNI(Java Native Interface);本地方法;串口通信;Windows
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2017)34-0051-06
Abstract: In order to solve the problem of Java serial communication in different platforms, a general Java serial communication system is designed in this paper. The system uses three layer architecture, including Java interface layer, native interface layer and a local implementation layer; Java interface layer is responsible for the function definition of serial communication, native interface layer is formed by JNI (Java Native Interface) technique and the function interface to define the local code, local implementation layer using operation API realize the serial communication function. According to the design of the system, this paper takes the Windows platform as an example to realize the Java serial communication under the Windows platform. The function test shows that the design of the system can correctly complete the serial communication function.
Key words: JNI(Java Native Interface); native method;serial communication; Windows
隨著物聯(lián)網(wǎng)技術(shù)的不斷發(fā)展,串口通信技術(shù)作為一項十分重要的數(shù)據(jù)傳輸手段正得到越來越廣泛的應(yīng)用[1]。在傳統(tǒng)的應(yīng)用領(lǐng)域里,串口設(shè)備的訪問均是基于C/C++本地代碼實現(xiàn)的,雖然具備較高的訪問性能,但其跨平臺性則相對受限;而Java程序在跨平臺方面具備得天獨厚的優(yōu)勢,其體系結(jié)構(gòu)無關(guān)性正受到越來越多的企業(yè)級服務(wù)的青睞[2-3]。然而Java的跨平臺特性也為其帶來了一定的局限性,部分與平臺相關(guān)的功能無法得到良好支持[4],在對本地硬件設(shè)備訪問方面(諸如串口設(shè)備等)就是受限情況之一。
JNI技術(shù)(Java Native Interface)作為Java訪問C/C++本地代碼的接口,可以實現(xiàn)對本地動態(tài)庫的調(diào)用,既彌補了Java的不足,同時也兼具了跨平臺的優(yōu)勢[3]。通過該技術(shù)可以將與平臺相關(guān)的串口通信同跨平臺的Java應(yīng)用結(jié)合起來,實現(xiàn)Java程序?qū)Υ谠O(shè)備的訪問。
1 關(guān)鍵技術(shù)簡介
1.1 JNI(Java Native Interface)
JNI(Java Native Interface)是Java本地程序接口,屬于JDK的一部分[3]。JNI允許運行在Java 虛擬機(JavaVirtual Machine,JVM)上的Java代碼操作其他語言(例如C/C++)編寫的應(yīng)用程序和庫[5]。同時本地應(yīng)用程序和庫也可以通過JNI來操作JVM內(nèi)存中的Java對象,實現(xiàn)與Java應(yīng)用程序共享這些對象[5]。
本地代碼在平臺屬性使用和高性能計算方面具備明顯的優(yōu)勢,而JNI技術(shù)則將這種優(yōu)勢集成到了Java之中,極大擴展了Java的功能范疇,特別是在處理Java本身不具備的平臺屬性和提升Java應(yīng)用程序性能方面??梢哉fJNI技術(shù)充當(dāng)了跨平臺的Java代碼和平臺相關(guān)的本地代碼的通信“橋梁”,實現(xiàn)了兩者之間的互操作性。
1.2 串口通信
串口是計算機與外圍設(shè)備之間的數(shù)據(jù)傳輸通道[6],數(shù)據(jù)通過串口以一位一位按順序的方式進行傳輸,其優(yōu)點是只需一對傳輸線,大大降低了數(shù)據(jù)傳輸?shù)某杀?,特別適合遠距離通信[7]。當(dāng)前主流的串口標準包含了RS-232、RS-485、RS-422等,這些標準只對接口的電氣特性做出規(guī)定,而不涉及接插件、電纜或協(xié)議,在此基礎(chǔ)上用戶可以建立自己的高層通信協(xié)議。
在當(dāng)前主流操作系統(tǒng)平臺上,串口設(shè)備是作為一種設(shè)備資源存在的[6],不同的操作系統(tǒng)對設(shè)備資源的管理方式各不相同,對串口設(shè)備的編程訪問也需要依平臺而定。在Windows平臺下,實現(xiàn)串口通信的方式主要包括平臺API函數(shù)、MScomm通信組件以及VS2008專門提供的串口通信類SerialPort[6]。
2 系統(tǒng)設(shè)計
2.1 整體架構(gòu)
為了實現(xiàn)系統(tǒng)模塊化設(shè)計,保證各個模塊之間邏輯相對獨立以方便后續(xù)改進,該系統(tǒng)采用了三層體系架構(gòu),即Java接口調(diào)用層、本地接口調(diào)用層和本地實現(xiàn)層。各層的關(guān)系如下:
1) Java接口調(diào)用層:定義串口通信的Java接口,該接口一方面提供給應(yīng)用層代碼調(diào)用,實現(xiàn)Java程序與串口設(shè)備通信;另一方面該接口通過JNI技術(shù)產(chǎn)生本地調(diào)用接口,實現(xiàn)Java調(diào)用接口與本地調(diào)用接口一一對應(yīng)關(guān)系。
2) 本地接口調(diào)用層:由JNI技術(shù)產(chǎn)生與Java接口對應(yīng)的本地代碼(C/C++)接口,該接口規(guī)定了本地實現(xiàn)串口通信功能時所必須遵照的調(diào)用協(xié)議[2]。
3) 本地實現(xiàn)層:利用操作系統(tǒng)API實現(xiàn)串口號獲取、打開串口、配置參數(shù)、讀寫串口、關(guān)閉串口等通信功能,并對本地調(diào)用接口進行封裝。
根據(jù)各層的關(guān)系,該系統(tǒng)的整體架構(gòu)設(shè)計如下:
2.2 Java接口調(diào)用層
Java接口調(diào)用層實現(xiàn)對串口對象的抽象,將實際物理串口設(shè)備抽象成邏輯串口對象,并提供給應(yīng)用層代碼調(diào)用;同時為了和本地代碼通信,該層定義了調(diào)用接口,描述了本地接口應(yīng)該遵照的調(diào)用協(xié)議形式。由于Java語言本身是面向?qū)ο蟮模镜胤椒ǎɡ鏑語言)是面向過程的,所以調(diào)用接口是按照基本數(shù)據(jù)傳遞方式進行設(shè)計。Java接口調(diào)用層設(shè)計如圖2所示。
1) SerialPort接口描述了串口對象的基本功能,屏蔽了底層串口通信細節(jié),應(yīng)用層可以調(diào)用該接口實現(xiàn)與串口通信通信;在該接口中定義了串口的打開和關(guān)閉、配置串口以及讀寫串口功能;
2) SerialPortImpl類為SerialPort接口的具體實現(xiàn),該類中包含了串口對象的波特率、數(shù)據(jù)位、停止位和校驗位屬性,同時由于串口設(shè)備只能采用獨占式打開,因此該類采用了單例設(shè)計模式,保證邏輯串口對象和物理串口設(shè)備的統(tǒng)一;
3) PortHelper類為調(diào)用接口,可以通過JNI技術(shù)生成本地調(diào)用接口,該接口中的方法均為靜態(tài)native方法,以便和本地代碼(例如C語言)匹配。
2.3 本地接口調(diào)用層
本地接口調(diào)用層由JNI技術(shù)自動生成,該層的函數(shù)與上述PortHelper類中的方法形成一一對應(yīng)關(guān)系,從而實現(xiàn)Java虛擬機通過裝載動態(tài)連接庫[4],經(jīng)JNI間接完成本地代碼調(diào)用的過程。由于該層接口是對Java調(diào)用接口的本地描述,因此當(dāng)Java調(diào)用接口發(fā)生改變時,該層需重新生成以保證調(diào)用邏輯正確對應(yīng)。
本地調(diào)用層在實現(xiàn)與Java接口調(diào)用層通信的同時,還需要實現(xiàn)同本地實現(xiàn)代碼之間的調(diào)用。為了保持模塊的相對獨立性,本地調(diào)用層不包含具體的串口通信代碼,其主要功能是實現(xiàn)基本的數(shù)據(jù)提取和安全檢查,并將處理后的數(shù)據(jù)傳遞給本地實現(xiàn)層函數(shù)。本地接口調(diào)用層設(shè)計如下:
1) 本地接口依賴于JNI技術(shù)自動生成,是一組本地代碼的函數(shù)聲明,不包含具體的邏輯功能,與PortHelper類中的方法對應(yīng)。由于采用了基本數(shù)據(jù)類型傳遞數(shù)據(jù),因此生成的本地接口中的數(shù)據(jù)類型可以和C語言中的基本數(shù)據(jù)類型進行關(guān)聯(lián),并可以通過相應(yīng)的方法完成數(shù)據(jù)轉(zhuǎn)換;
2) Java中的數(shù)據(jù)類型與C語言中的數(shù)據(jù)類型存在差異,無法在本地代碼中直接使用Java數(shù)據(jù)類型,因此需要對JNI傳遞而來的數(shù)據(jù)做適當(dāng)?shù)奶崛∨c格式轉(zhuǎn)化之后才能在本地代碼中使用;
3) 在JVM內(nèi)存模型中,JVM能夠?qū)崿F(xiàn)對堆內(nèi)存進行自動垃圾回收,但在本地內(nèi)存上則無法實現(xiàn),因此在處理數(shù)據(jù)的過程中,還需對數(shù)據(jù)的內(nèi)存進行管理,以保證不會發(fā)生JVM崩潰問題。
2.4 本地實現(xiàn)層
本地實現(xiàn)層負責(zé)處理具體的串口邏輯功能。由于串口設(shè)備的驅(qū)動方式和通信方式與具體的操作系統(tǒng)平臺相關(guān),因此本地實現(xiàn)層需要借助具體的操作系統(tǒng)平臺API來實現(xiàn)對串口設(shè)備的操作。串口設(shè)備作為一種獨占式訪問設(shè)備,要求用戶程序有且僅有一個實例來獲取串口句柄,因此在本地實現(xiàn)層里提供了一個全局串口變量來抽象該設(shè)備,通過該變量的使用狀態(tài)來達到訪問互斥的效果。本地實現(xiàn)層設(shè)計如下:
在現(xiàn)代操作系統(tǒng)中,物理設(shè)備被抽象成各種文件,因此對設(shè)備的訪問也相應(yīng)的抽象成了文件訪問。而在不同操作系統(tǒng)中,文件模型以及讀寫文件的IO模型各不一樣,故本地實現(xiàn)層的代碼需視具體的操作系統(tǒng)平臺而定。全局串口變量存在于本地內(nèi)存中,為了保證其內(nèi)存運行正確,不會出現(xiàn)溢出崩潰現(xiàn)象,故設(shè)計了一組內(nèi)存管理函數(shù)New_Port和Free_Port,用于對全局串口變量的內(nèi)存空間開辟和釋放的管理。
3 代碼實現(xiàn)
3.1 Java接口實現(xiàn)
在Java接口調(diào)用層中,PortHelper類為調(diào)用接口,也是通過JNI技術(shù)生成本地調(diào)用接口的模板,因此對該接口的實現(xiàn)成為了系統(tǒng)調(diào)用的關(guān)鍵。根據(jù)Java調(diào)用接口層設(shè)計,接口中的方法均為靜態(tài)native方法,并且傳遞的參數(shù)均為基本數(shù)據(jù)類型,不涉及復(fù)雜對象的傳遞。同時Java語言本身具備良好的跨平臺性,該接口的定義在任意平臺下均適用。調(diào)用接口的代碼如下:
各方法的功能及參數(shù)的說明如表2所示。
3.2 本地接口生成
本地接口是基于JNI技術(shù)自動生成的,在生成時需要使用不同平臺下的JNI[3]。本文選擇了Windows平臺作為實驗平臺,因此需使用該平臺下的JNI(%JAVA_HOME%\bin\javah.exe)。通過調(diào)用javah命令將上述PortHelper類生成對應(yīng)的本地接口文件test_tools_PortHelper.h[2]。該頭文件中定義的本地接口與PortHelper類中的靜態(tài)方法形成了一一對應(yīng)關(guān)系,具體代碼如下(省略條件編譯代碼):
其中jni.h頭文件(以及內(nèi)部包含的jni_md.h)由本地JDK提供,定義了一系列Java與本地代碼通信的庫函數(shù)和數(shù)據(jù)結(jié)構(gòu),通過引入該頭文件可以實現(xiàn)Java數(shù)據(jù)類型與本地數(shù)據(jù)類型之間相互轉(zhuǎn)化,以及JVM的本地內(nèi)存管理。
3.3 本地接口調(diào)用實現(xiàn)
生成的本地接口不包含任何邏輯,為了實現(xiàn)本地接口對本地實現(xiàn)層的代碼調(diào)用,還需對本地接口的調(diào)用過程進行實現(xiàn)。為了保證調(diào)用過程的相對獨立,降低各個接口之間的耦合度,本地接口與本地實現(xiàn)層的調(diào)用關(guān)系也為一一對應(yīng)的,具體對應(yīng)關(guān)系如下:
在調(diào)用過程中,數(shù)據(jù)的傳遞是雙向的,一方面Java代碼中的數(shù)據(jù)通過JNI傳遞到本地代碼中,這需要保證在本地代碼中能夠正確的提取出所傳遞的數(shù)據(jù);另一方面本地代碼產(chǎn)生的結(jié)果也需要通過JNI傳遞到Java代碼中,這需要保證JVM本地內(nèi)存和本地內(nèi)存正確,避免潛在的內(nèi)存泄漏引發(fā)程序崩潰。因此在本地調(diào)用時,需要做以下兩點處理:
1) 利用虛擬機內(nèi)存指針對傳遞的數(shù)據(jù)進行處理,將其轉(zhuǎn)化為本地代碼能夠識別的數(shù)據(jù)類型;
2) 利用虛擬機內(nèi)存指針和本地釋放函數(shù)對數(shù)據(jù)進行回收,避免內(nèi)存泄漏。
由于在調(diào)用過程中所有的模塊均需要遵循上述處理要求,因此現(xiàn)以讀取串口為例,展示從JNI中利用虛擬機內(nèi)存指針獲取數(shù)據(jù)、處理數(shù)據(jù)、調(diào)用本地代碼和回收數(shù)據(jù)整個過程(其他模塊處理過程類似,在這里不再復(fù)述)。
根據(jù)Java調(diào)用接口定義,讀取串口方法中包含了緩沖區(qū)參數(shù)以及返回實際讀取的字節(jié)數(shù),由于Java數(shù)據(jù)類型與本地代碼數(shù)據(jù)類型存在差異,因此首先需要在本地代碼中利用虛擬機內(nèi)存指針獲取JNI層傳遞的數(shù)組對象;其次通過調(diào)用本地實現(xiàn)層將數(shù)據(jù)從串口設(shè)備中讀取出來;最后對讀取結(jié)果進行封裝并回收內(nèi)存。具體的調(diào)用代碼如下:
其中需要注意以下關(guān)鍵步驟:
1) 在步驟①中,由于Java數(shù)組對象與本地代碼數(shù)組存在著差異,無法直接對該數(shù)組對象直接操作,因此通過虛擬機內(nèi)存指針以拷貝的形式將JNI傳遞的數(shù)組對象獲取出來。需要注意的是該獲取過程采用了復(fù)制數(shù)組的形式,因此必須要有對應(yīng)的回收數(shù)據(jù)的過程,避免虛擬機內(nèi)存溢出;
2) 在步驟②中,通過直接調(diào)用本地實現(xiàn)層代碼,完成了從串口設(shè)備中讀取數(shù)據(jù)的功能,其讀取的字節(jié)保存在緩沖區(qū)中;
3) 在步驟③中,將緩沖區(qū)中的字節(jié)數(shù)據(jù)復(fù)制到數(shù)組對象中,但需要注意的是,因為該數(shù)組對象是復(fù)制產(chǎn)生的(步驟①),并未對實際數(shù)組對象發(fā)生任何修改,所以還需通過虛擬機內(nèi)存指針對原數(shù)組對象進行覆蓋;
4) 步驟④實現(xiàn)了虛擬機內(nèi)存指針對原數(shù)組對象進行覆蓋的功能,從而將本地數(shù)據(jù)通過JNI傳遞到了Java代碼中;
5) 步驟⑤與步驟①對應(yīng),將復(fù)制的數(shù)組對象進行釋放,回收內(nèi)存空間,防止虛擬機內(nèi)存溢出。
3.4 本地實現(xiàn)層
3.4.1 基本數(shù)據(jù)結(jié)構(gòu)
本地實現(xiàn)層負責(zé)利用平臺API完成實際的串口通信。在Windows平臺下串口設(shè)備被抽象成了文件,因此對串口設(shè)備的訪問轉(zhuǎn)化為對文件的訪問,而文件的訪問均需要通過對應(yīng)的句柄才能完成,故為了方便數(shù)據(jù)管理,進一步提高抽象層次,基本數(shù)據(jù)結(jié)構(gòu)中封裝了串口句柄和相應(yīng)參數(shù),并定義了一組常量和內(nèi)存管理函數(shù)?;緮?shù)據(jù)結(jié)構(gòu)核心代碼如下:
其中Port結(jié)構(gòu)體封裝了與串口有關(guān)的數(shù)據(jù),包括串口句柄、波特率、數(shù)據(jù)位、停止位和校驗位。本地實現(xiàn)層定義了全局變量g_port,能夠保存串口對象以便在后續(xù)訪問中實現(xiàn)對串口設(shè)備的讀寫功能。New_Port函數(shù)負責(zé)從內(nèi)存中創(chuàng)建全局變量并完成初始化,free_Port函數(shù)則負責(zé)關(guān)閉串口句柄并回收內(nèi)存,確保本地內(nèi)存不會泄漏。
3.4.2 串口號獲取
在Windows平臺下,為了獲取主機安裝的所有串口設(shè)備,需要借助Windows API對注冊表進行訪問。在獲取所有串口過程中沒有打開串口的操作,故不需要使用全局變量g_port。串口號獲取核心代碼如下:
其中核心步驟主要有以下四步:
1) 步驟①利用了Windows平臺提供的操作注冊表函數(shù)實現(xiàn)打開注冊表,將注冊表句柄賦值給hKey變量,并從參數(shù)中返回;
2) 在步驟②中,循環(huán)的從注冊表句柄里不斷的獲取串口名稱,當(dāng)沒有串口名時才從循環(huán)中退出。獲取到的串口名被封裝進commName變量里;
3) 步驟③則將本輪循環(huán)得到的commName變量(即串口名)賦值給結(jié)果變量;
4) 步驟④則與步驟①相對,將打開的注冊表關(guān)閉并釋放句柄,保證操作注冊表的邏輯正確。
3.4.3 打開串口
在Windows平臺下串口設(shè)備被抽象成了文件,可以借助文件操作函數(shù)CreateFile實現(xiàn)串口打開。但需要注意的是,在Windows平臺上文件的訪問有同步訪問和重疊訪問,而串口設(shè)備不允許采用重疊訪問的方式打開,因此在串口設(shè)備打開時需要選擇同步模式。打開串口的核心代碼如下:
由于串口設(shè)備必須是以獨占的形式訪問,因此全局變量g_port必須是在為NULL的時候才能進行串口打開操作,而一旦打開成功時全局變量g_port非空,這樣防止了重復(fù)打開串口引發(fā)錯誤,同時也提高了代碼訪問效率,降低了對系統(tǒng)函數(shù)訪問的頻率。
3.4.4 配置串口
在打開串口成功之后,此時的串口運行參數(shù)是系統(tǒng)默認值,需進一步對串口的通信參數(shù)進行配置。串口的通信參數(shù)主要包括了串口的波特率、數(shù)據(jù)位、停止位和校驗位。在一般情況下,配置串口過程是通過從系統(tǒng)中獲取默認的串口參數(shù)信息,修改該參數(shù)信息并重新設(shè)置來實現(xiàn)的。配置串口的核心代碼如下:
步驟①實現(xiàn)了獲取系統(tǒng)默認的參數(shù)信息,而通過步驟②對默認的參數(shù)信息進行修改,使用實際參數(shù)值進行覆蓋,在步驟③中則用修改后的參數(shù)信息對系統(tǒng)信息重新設(shè)置,并刷新串口緩沖區(qū)。
3.4.5 串口讀寫
串口讀寫的過程與一般文件讀寫過程類似。在成功獲取了串口句柄和配置串口的基礎(chǔ)上,通過平臺提供的文件操作函數(shù)ReadFile和WriteFile實現(xiàn)對串口的讀寫。在讀取的過程中,從串口中獲取的字節(jié)信息將會保存到緩沖區(qū)中,并返回給本地調(diào)用層;而向串口寫入數(shù)據(jù)時則直接將緩沖區(qū)中的字節(jié)數(shù)據(jù)寫出。串口讀寫核心代碼如下:
3.4.6 關(guān)閉串口
在操作結(jié)束時需要將串口設(shè)備關(guān)閉。關(guān)閉的過程一方面需要使用平臺提供的句柄操作函數(shù)CloseHandle函數(shù)來關(guān)閉串口句柄,另一方面需要回收全局變量g_port,并將其恢復(fù)成初始化狀態(tài),以便下一次訪問串口設(shè)備。關(guān)閉串口核心代碼如下:
4 實驗與結(jié)果
為了測試Java代碼能否通過JNI技術(shù)實現(xiàn)本地串口通信,本文設(shè)計了一組功能實驗,具體的運行環(huán)境如下:
基于上述測試環(huán)境,實驗得到的結(jié)果如圖5所示。
根據(jù)上圖實驗結(jié)果,圖(a)實驗軟件顯示了獲取平臺的全部串口信息,同時對串口COM1實現(xiàn)了打開并配置。在配置成功的基礎(chǔ)上,實驗軟件向串口COM1寫入了測試數(shù)據(jù),并開啟了循環(huán)讀取串口數(shù)據(jù)功能,結(jié)果顯示實驗軟件能夠持續(xù)的從串口中讀出數(shù)據(jù)。圖(b)驗證軟件打開了串口COM2,由于串口COM1與串口COM2實現(xiàn)了關(guān)聯(lián),因此可以將串口COM1的數(shù)據(jù)從串口COM2中讀出,或向串口COM2寫入數(shù)據(jù)由串口COM1讀出。驗證軟件運行結(jié)果顯示,其能夠正確接收到實驗軟件發(fā)送的測試數(shù)據(jù),并且其周期性寫入的數(shù)據(jù)在實驗軟件中也實現(xiàn)了正確讀出。
5 結(jié)束語
實驗結(jié)果表明,在Windows平臺下Java代碼能夠通過JNI技術(shù)實現(xiàn)對本地串口設(shè)備的訪問,運行結(jié)果符合預(yù)期,顯示了該串口通信系統(tǒng)的設(shè)計方案是較為合理的,可以基于該設(shè)計方案在其他操作系統(tǒng)平臺下做進一步驗證。
然而該系統(tǒng)設(shè)計并未考慮性能因素,未將其與同類型軟件在性能上做出橫向?qū)Ρ?,在后續(xù)的研究工作中還需深入對該設(shè)計方案的性能做出評估。
參考文獻:
[1] 陳傳波, 杜娟, 張智杰. WIN32下基于RS232C協(xié)議的串口通信方法及應(yīng)用研究[J]. 南昌大學(xué)學(xué)報:工科版, 2005(3):71-75.
[2] 李亞東, 夏雨佳, 席裕庚. 基于JNI的跨平臺軟件設(shè)計[J]. 計算機工程, 2000(9):87-88,154.
[3] 任俊偉, 林東岱. JNI技術(shù)實現(xiàn)跨平臺開發(fā)的研究[J]. 計算機應(yīng)用研究, 2005(7):180-184.
[4] 張華平, 玄光哲, 于貴平, 等. 基于JNI技術(shù)應(yīng)用框架的分析和實現(xiàn)[J].吉林大學(xué)學(xué)報:信息科版, 2003(2):188-191.
[5] 沙嘉祥, 寧書年, 林捷. 利用JNI實現(xiàn)企業(yè)Java程序與傳統(tǒng)應(yīng)用程序的集成[J]. 計算機與現(xiàn)代化, 2004(2):20-25.
[6] 龔新文. 串口通信在VS2008中的實現(xiàn)與應(yīng)用[J]. 電腦與電信, 2011(3):47-48,51.
[7] 黃暉, 柴劍勇, 嚴興, 等. 串口通信技術(shù)[J]. 科技創(chuàng)新導(dǎo)報, 2010(27):20-21.