了解串行通訊 串行通訊協議有很多種,像RS232,RS485,RS422,甚至現今流行的USB等都是串行通訊協議。而串行通訊技術的應用無處不在。可能大家見的最多就是電腦的串口與Modem的通訊。記得在PC機剛開始在中國流行起來時(大約是在90年代前五年),那時甚至有人用一條串行線進行兩台電腦之間的數據共享。除了這些,手機,PDA,USB鼠標、鍵盤等等都是以串行通訊的方式與電腦連接。而筆者工作性質的關系,所接觸到的就更多了,像多串口卡,各種種類的具有串口通訊接口的檢測與測量儀器,串口通訊的網絡設備等。
雖然串行通訊有很多種,但筆者所知的在整個電子通訊產品方面,以RS232的通訊方式最為多見。雖然USB接口的電子產品也是層出不窮,但了解一下Java在串行通訊方面的技術還有有必要的,說不定有哪位讀者還想用此技術寫一個PDA與電腦之間數據共享的程序呢。
本文主要以RS232為主來講解JAVA的串行通訊技術。
RS232通訊基礎 RS-232-C(又稱 EIA RS-232-C,以下簡稱RS232)是在1970年由美國電子工業協會(EIA)聯合貝爾系統、調制解調器廠家及計算機終端生產廠家共同制定的用於串行通訊的標准。RS232是一個全雙工的通訊協議,它可以同時進行數據接收和發送的工作。RS232的端口通常有兩種:9針(DB9)和25針(DB25)。
DB9和DB25的常用針腳定義
常見的邊線方式
常見的通訊方式是三線式,這種方式是將兩個RS232設備的發送端(TXD)和接收端(RXD)及接地端(GND)互相連接,也是許多讀者所知道的連接方式:
(9針)
2(RXD) ---------
3(TXD
3(TXD) ---------
2(TXD)
5(GND) ---------
5(GND)
(25針)
2(RXD) ---------
3(TXD
3(TXD) ---------
2(RXD)
7(GND) ---------
7(GND)
這種方式分別將兩端的RS232接口的2--3,3---2,5(7)---5(7)針腳連接起來。其中2是數據接收線(RXD),3是數據發送線(TXD),5(7)是接地(RND)。假如有一台式PC,和一部NoteBook電腦,就可以用這種方式連線了。用三線式可以將大多數的RS232設備連接起來。但假如你認死了2--3,3--2,5(7)--5(7)對接這個理,會發現在連某些RS232設備時並不奏效。這是因為有些設備在電路內部已將2和3線調換過來了,你只要2,3,5(7)針一一對應就行了。
安裝Java Communications API Sun的J2SE中並沒有直接提供以上提到的任何一種串行通訊協議的開發包,而是以獨立的jar包形式發布在java.sun.com網站上(從這裡下載)----即comm.jar,稱之為Javatm Communications API,它是J2SE的標准擴展。comm.jar並不是最近才有,早在1998年時,sun就已經發布了這個開發包。comm.jar分別提供了對常用的RS232串行端口和IEEE1284並行端口通訊的支持。目前sun發布的comm.jar只有Windows和Solaris平台兩個版本,假如你需要Linux平台下的,可以在http://www.geeksville.com/~kevinh/linuxcomm.Html找到。
在使用comm.jar之前,必須知道如何安裝它。這也是困擾許多初學java RS232通訊者的一個難題。假如我們電腦上安裝了JDK, 它將同時為我們安裝一份JRE(Java Runtime Entironment),通常我們運行程序時都是以JRE來運行的。所以以下的安裝適用於JRE。假如你是用JDK來運行程序的,請將相應的<JRE_HOME>改成<JDK_HOME>。
下載了comm.jar開發包後,與之一起的還有兩個重要的文件,win32com.dll和javax.comm.properties。 comm.jar提供了通訊用的java API,而win32com.dll提供了供comm.jar調用的本地驅動接口。而javax.comm.properties是這個驅動的類配置文件。首先將comm.jar復制到<JRE_HOME>libext目錄。再將win21com.dll復制到你的RS232應用程序運行的目錄,即user.dir。然後將javax.comm.properties復制到<JRE_HOME>lib目錄。
通訊前的預備 假如你手頭上沒有現成的提供了標准RS232串口的設備,你可以將自己的電腦模擬成兩台不同的串口設備。通常電腦主機後面的面板提供了兩個9針的串口,請將這兩個串口的2,3,5腳按前面介紹的方法連接。電子市場都有現成的連接頭賣,請不要買那種封裝的嚴嚴實實的接頭,而要買用螺絲封裝可以拆開的連接頭,這樣可以方便自己根據需要連接各個針腳。
Comm API基礎 我無意於在此具體描述Comm API每個類和接口的用法,但我會介紹Comm API的類結構和幾個重要的API用法。
所有的comm API位於javax.comm包下面。從Comm API的javadoc來看,它介紹給我們的只有區區以下13個類或接口:
javax.comm.CommDriver
javax.comm.CommPort
javax.comm.ParallelPort
javax.comm.SerialPort
javax.comm.CommPortIdentifier
javax.comm.CommPortOwnershipListener
javax.comm.ParallelPortEvent
javax.comm.SerialPortEvent
javax.comm.ParallelPortEventListener (extends java.util.EventListener)
javax.comm.SerialPortEventListener (extends java.util.EventListener)
javax.comm.NoSUChPortException
javax.comm.PortInUseException
javax.comm.UnsupportedCommOperationException
下面講解一下幾個主要類或接口。
1.枚舉出系統所有的RS232端口
在開始使用RS232端口通訊之前,我們想知道系統有哪些端口是可用的,以下代碼列出系統中所有可用的RS232端口:
Enumeration en = CommPortIdentifier.getPortIdentifiers();
CommPortIdentifier portId;
while (en.hasMoreElements())
{
portId = (CommPortIdentifier) en.nextElement();
/*假如端口類型是串口,則打印出其端口信息*/
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
{
System.out.println(portId.getName());
}
}
在我的電腦上以上程序輸出以下結果:
COM1
COM2
CommPortIdentifier類的getPortIdentifiers方法可以找到系統所有的串口,每個串口對應一個CommPortIdentifier類的實例。
2.打開端口
假如你使用端口,必須先打開它。
try{
CommPort serialPort = portId.open("My App", 60);
/* 從端口中讀取數據*/
InputStream input = serialPort.getInputStream();
input.read(...);
/* 往端口中寫數據*/
OutputStream output = serialPort.getOutputStream();
output.write(...)
...
}catch(PortInUseException ex)
{ ... }
通過CommPortIdentifier的open方法可以返回一個CommPort對象。open方法有兩個參數,第一個是String,通常設置為你的應用程序的名字。第二個參數是時間,即開啟端口超時的毫秒數。當端口被另外的應用程序占用時,將拋出PortInUseException異常。
在這裡CommPortIdentifier類和CommPort類有什麼區別呢?其實它們兩者是一一對應的關系。CommPortIdentifier主要負責端口的初始化和開啟,以及治理它們的占有權。而CommPort則是跟實際的輸入和輸出功能有關的。通過CommPort的getInputStream()可以取得端口的輸入流,它是java.io.InputStream接口的一個實例。我們可以用標准的InputStream的操作接口來讀取流中的數據,就像通過FileInputSteam讀取文件的內容一樣。相應的,CommPort的getOutputStream可以獲得端口的輸出流,這樣就可以往串口輸出數據了。
3. 關閉端口
使用完的端口,必須記得將其關閉,這樣可以讓其它的程序有機會使用它,不然其它程序使用該端口時可能會拋出端口正在使用中的錯誤。很希奇的是,CommPortIdentifier類只提供了開啟端口的方法,而要關閉端口,則要調用CommPort類的close()方法。
通訊方式 CommPort的輸入流的讀取方式與文件的輸入流有些不一樣,那就是你可能永遠不知這個InputStream何時結束,除非對方的OutputStream向你發送了一個特定數據表示發送結束,你收到這個特定字符後,再行關閉你的InputStream。而comm.jar提供了兩種靈活的方式讓你讀取數據。
1. 輪詢方式(Polling)
舉個例子,你同GF相約一起出門去看電影,但你的GF好妝扮,這一妝扮可能就是半小時甚至一小時以上。這時你就耐不住了,每兩分鐘就催問一次“好了沒?”,如此這樣,直到你的GF說OK了才算完。這個就叫輪詢(Polling)。
在程序中,輪詢通常設計成一個封閉的循環,當滿足某個條件時即結束循環。剛才那個例子中,你的GF說“OK了!”,這個就是結束你輪詢的條件。在單線程的程序中,當循環一直執行某項任務而又無法預知它何時結束時,此時你的程序看起來可能就像死機一樣。在VB程序中,這個問題可以用在循環結構中插入一個doEvent語句來解決。而Java中,最好的方式是使用線程,就像以下代碼片斷一樣。
public TestPort extend Thread
{
...
InputStream input = serialPort.getInputStream();
StringBuffer buf = new StringBuffer();
boolean stopped = false;
...