摘 要: 介紹了Java實(shí)現(xiàn)串口通信編程的技術(shù)處理。著重就串口通信的連接、數(shù)據(jù)緩沖區(qū)資源的多線程訪問(wèn)控制以及數(shù)據(jù)讀取中的超時(shí)控制等問(wèn)題進(jìn)行了詳細(xì)討論,有效地實(shí)現(xiàn)了主機(jī)與下位單片機(jī)之間的數(shù)據(jù)傳遞。該通信方式已用于基于工控機(jī)的絕緣電阻檢測(cè)應(yīng)用中。
關(guān)鍵詞: Java;串口通信;多線程;comm包
嵌入式系統(tǒng)或傳感器網(wǎng)絡(luò)的很多應(yīng)用都需要通過(guò)PC機(jī)與嵌入式設(shè)備或傳感器節(jié)點(diǎn)進(jìn)行通信。其中,最常用的接口就是RS-232串口。串口通信可以是上位機(jī)與下位機(jī)之間的直接串口通信,也可以是在串口上連接無(wú)線通信模塊,通過(guò)串口進(jìn)行無(wú)線通信。RS-232-C是在1970年由美國(guó)電子工業(yè)協(xié)會(huì)(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計(jì)算機(jī)終端生產(chǎn)廠家共同制定的用于串行通信的標(biāo)準(zhǔn)。RS-232是一個(gè)全雙工的通信協(xié)議,它可以同時(shí)進(jìn)行數(shù)據(jù)接收和發(fā)送的工作。
Java實(shí)現(xiàn)串口通信的編程方式通常采用SUN發(fā)布的串口通信API,它是以獨(dú)立jar包形式提供的一個(gè)標(biāo)準(zhǔn)擴(kuò)展。其中包含3個(gè)文件:comm.jar提供了通信用的java API;win32com.dll提供了串口通信的本地驅(qū)動(dòng)接口;javax.comm.properties是這個(gè)驅(qū)動(dòng)的類(lèi)配置文件。Java讀寫(xiě)串口過(guò)程主要是調(diào)用javax.comm包中的API函數(shù)。在javax.comm包中,串口的讀寫(xiě)操作是數(shù)據(jù)流形式,串口初始化后,通過(guò)CommPort類(lèi)的getInputStream()和getOutputStream()方法即可分別取得端口的輸入流和輸出流。
串口通信應(yīng)用程序有兩種模式:一種是實(shí)現(xiàn)SerialPortEventListener接口,通過(guò)監(jiān)聽(tīng)串口事件并作相應(yīng)處理;另一種就是建立一個(gè)獨(dú)立的接收線程負(fù)責(zé)數(shù)據(jù)的接收。本文采用的是后一種方式。
本文的串口通信是上位工控機(jī)與下位單片機(jī)之間的通信。工控機(jī)通過(guò)觸摸屏方式來(lái)使用系統(tǒng),下位單片機(jī)連接絕緣檢測(cè)筆實(shí)現(xiàn)絕緣電阻的檢測(cè),并將檢測(cè)過(guò)程的結(jié)果發(fā)送給上位機(jī)。本文僅介紹主機(jī)方的Java串口通信編程處理技術(shù),如圖1所示的虛線上方部分,包含消息接收線程、Swing事件驅(qū)動(dòng)應(yīng)用界面、消息緩沖區(qū)、消息分析處理程序。其中:①在圖形應(yīng)用界面中通過(guò)用戶的操作來(lái)觸發(fā)事件實(shí)現(xiàn)與單片機(jī)的通信;②主機(jī)通過(guò)串口向單片機(jī)發(fā)送啟動(dòng)檢測(cè)的消息;③單片機(jī)在收到消息后,將啟動(dòng)檢測(cè),并將傳感器獲取的數(shù)據(jù)通過(guò)串口發(fā)送給主機(jī)作為響應(yīng);④數(shù)據(jù)接收線程將收到的數(shù)據(jù)放到一個(gè)緩存中;⑤消息分析處理程序從緩存中獲取數(shù)據(jù)并進(jìn)行分析處理。主機(jī)和單片機(jī)間每次通信傳送1 B。
1 串口緩沖區(qū)的控制
串口緩沖區(qū)(SerialBuffer類(lèi))實(shí)現(xiàn)從串口接收到的一個(gè)完整消息的封裝,本系統(tǒng)的消息按協(xié)議設(shè)計(jì)為11 B,其中包含消息的起始標(biāo)記、識(shí)別標(biāo)識(shí)和數(shù)據(jù)字節(jié)。消息緩沖區(qū)是消息接收線程和消息分析處理Bean之間的橋梁,只有在接收到一條完整的消息后才可以進(jìn)行消息的分析解析。串口緩沖區(qū)安排有3個(gè)重要屬性:
?。?)Content屬性:存放11 B的消息;
(2)Available屬性:標(biāo)識(shí)消息是否可用;
(3)LengthNeeded屬性:統(tǒng)計(jì)收到的消息字節(jié)長(zhǎng)度。
該類(lèi)還定義了兩個(gè)重要方法:(1)public synchronized byte[] GetMsg():從緩沖區(qū)讀取消息;(2)public synchronized void putbyte(int c):寫(xiě)一個(gè)字節(jié)到緩沖區(qū)。方法定義中均含有synchronized關(guān)鍵詞,也就是要使用這兩個(gè)方法必須取得緩沖區(qū)的對(duì)象鎖,從而實(shí)現(xiàn)對(duì)緩沖區(qū)這個(gè)共享資源的訪問(wèn)互斥操作。
2 消息接收線程
消息接收線程(ReadSerial類(lèi))循環(huán)從串口讀取數(shù)據(jù)并將其存放到消息緩沖區(qū)中,串口無(wú)數(shù)據(jù)或緩沖區(qū)滿時(shí)它將處于資源等待狀態(tài)。以下為線程的run方法代碼:
public void run() {
try {
while (true) {
int c = ComPort.read();
//從串口讀1 B
ComBuffer.putbyte(c);
//將數(shù)據(jù)放入消息緩沖區(qū)
}
} catch (IOException e) { }
}
3 消息分析處理Bean
消息分析處理Bean(SerialBean類(lèi))是Swing界面處理程序?qū)Υ谶M(jìn)行操作訪問(wèn)的調(diào)用接口。其中封裝有3個(gè)方法:Initialize方法實(shí)現(xiàn)串口的初始化;ReadPort()方法從消息緩沖區(qū)讀消息并進(jìn)行分析;WritePort(byte[] Msg)方法寫(xiě)消息到串口。串口初始化只執(zhí)行1次,包括如下工作:
(1)打開(kāi)串口
portId=CommPortIdentifier.getPortIdentifier(串口名);
serialPort=(SerialPort)portId.open(“串口所有者名稱(chēng)”,超時(shí)等待時(shí)間);
(2)獲取串口的輸入/輸出流
in=serialPort.getInputStream();
out=serialPort.getOutputStream();
?。?)設(shè)置串口參數(shù)
serialPort.setSerialPortParams(9 600,SerialPort.
DATABITS_8,
SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
?。?)創(chuàng)建消息緩沖區(qū)對(duì)象
SB=new SerialBuffer();
?。?)創(chuàng)建并啟動(dòng)消息接受線程
RT=new ReadSerial(SB, in);
RT.start();
4 串口通信驅(qū)動(dòng)程序的裝載
串口驅(qū)動(dòng)程序的裝載是實(shí)現(xiàn)串口初始化的前提,如果程序在Spring STS開(kāi)發(fā)環(huán)境中運(yùn)行,會(huì)從JDK的運(yùn)行環(huán)境裝載驅(qū)動(dòng)程序。這種情況下,必須事先將comm.jar拷貝到JDK安裝目錄的jre\lib\ext 目錄下,將win32com.dll 拷貝到JDK安裝目錄的jre\bin目錄下,將javax.comm.properties拷貝到JDK安裝目錄的jre\lib目錄[2]下。
如果將開(kāi)發(fā)的應(yīng)用導(dǎo)出為獨(dú)立的可運(yùn)行的JAR文件,則必須將以上的3個(gè)文件安排到應(yīng)用工程的src所在目錄路徑下,并在程序中用如下程序代碼進(jìn)行裝載。
Import javax.comm.CommDriver;
import javax.swing.JOptionPane;
public class CurrentStatus {
public static SerialBean SB ; //消息分析處理Bean
public static void init(int n){ //n為串口編號(hào)
String driverName=“com.sun.comm.Win32Driver”;
CommDriver driver=null;
try {
System.loadLibrary(“win32com”); //裝載DLL
driver=(CommDriver)Class.forName
(driverName).newInstance();
}
catch(Exception e1){ e1.printStackTrace();}
driver.initialize(); //驅(qū)動(dòng)程序初始化
SB=new SerialBean(n);
if (SB.Initialize()==-1)
JOptionPane.showMessageDialog(null,”串口初始
化錯(cuò)誤”);
}
}
這樣,在程序中可通過(guò)執(zhí)行init方法實(shí)現(xiàn)具體串口的通信初始化。例如:
CurrentStatus.init(1); //初始化串口1
5 在圖形界面中實(shí)現(xiàn)通信調(diào)用
在電氣設(shè)備的絕緣電阻檢測(cè)應(yīng)用中,圖2為應(yīng)用界面。當(dāng)用戶點(diǎn)擊某個(gè)檢測(cè)項(xiàng)對(duì)應(yīng)的按鈕時(shí),通過(guò)注冊(cè)按鈕點(diǎn)擊事件觸發(fā)執(zhí)行代碼,將通過(guò)消息分析處理Bean的WritePort方法給串口發(fā)送檢測(cè)命令,并利用循環(huán)等待讀取來(lái)自單片機(jī)的檢測(cè)結(jié)果。由于單片機(jī)在檢測(cè)過(guò)程中將發(fā)送系列檢測(cè)結(jié)果,因此,要循環(huán)讀取數(shù)據(jù),直到超過(guò)一定時(shí)間無(wú)數(shù)據(jù)可讀或接收到結(jié)束標(biāo)志的消息為止。具體工作過(guò)程如圖3所示。其中,數(shù)據(jù)庫(kù)的訪問(wèn)處理采用Spring的JdbcTemplate類(lèi)提供的功能實(shí)現(xiàn)。
本文介紹了Java實(shí)現(xiàn)串口通信編程的典型編程處理要點(diǎn),可有效地實(shí)現(xiàn)上位工控機(jī)與單片機(jī)之間的數(shù)據(jù)通信。系統(tǒng)通過(guò)多線程及對(duì)消息緩沖區(qū)資源的訪問(wèn)控制、延時(shí)等待控制等措施,保證了上下位機(jī)之間通信的可靠傳遞。實(shí)際應(yīng)用中要根據(jù)具體的消息格式來(lái)組織通信過(guò)程中數(shù)據(jù)的分析處理。
參考文獻(xiàn)
[1] 吳金鋒,劉偉平,黃紅斌.Java串口通信數(shù)據(jù)采控系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[J].微計(jì)算機(jī)信息,2010(10).
[2] 唐未香.Java程序與ZigBee串口通訊的實(shí)現(xiàn)[J].福建電腦,2010(5).