最近一門(mén)課要求編寫(xiě)一個(gè)上位機(jī)串口通訊工具,我根據(jù)Java編寫(xiě)了一個(gè)帶有圖形界面的簡(jiǎn)略串口通訊工具,下面詳述一下過(guò)程,供我們參考^_^
一:
首要,你需要下載一個(gè)額外的支撐Java串口通訊操作的jar包,由于java.comm比較老了,并且不支撐64位體系,這兒引薦Rxtx這個(gè)jar包(32位/64位均支撐)。
官方下載地址:http://fizzed.com/oss/rxtx-for-java(注:可能需要FQ才干下載)
不能FQ的童鞋,能夠在這兒下載:
二:
下載解壓jar包并在JavaBuildPath下引入:
捕獲
注:如果運(yùn)轉(zhuǎn)過(guò)程中拋出java.lang.UnsatisfiedLinkError過(guò)錯(cuò)或gnu.io下的類(lèi)找不到,請(qǐng)將rxtx解壓包中的rxtxParallel.dll,rxtxSerial.dll這兩個(gè)文件仿制到C:\Windows\System32目錄下即可處理該過(guò)錯(cuò)。
三:
關(guān)于該jar包的運(yùn)用,我寫(xiě)了一個(gè)SerialTool.java類(lèi),該類(lèi)供給關(guān)于串口通訊的各簡(jiǎn)略服務(wù),代碼如下(留意該類(lèi)位于serialPort包里):
packageserialPort;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.util.ArrayList;importjava.util.Enumeration;importjava.util.TooManyListenersException;importgnu.io.CommPort;importgnu.io.CommPortIdentifier;importgnu.io.NoSuchPortException;importgnu.io.PortInUseException;importgnu.io.SerialPort;importgnu.io.SerialPortEventListener;importgnu.io.UnsupportedCommOperationException;importserialException.*;/***串口服務(wù)類(lèi),供給翻開(kāi)、封閉串口,讀取、發(fā)送串口數(shù)據(jù)等服務(wù)(采用單例規(guī)劃方式)
*@authorzhong
**/publicclassSerialTool{privatestaticSerialToolserialTool=null;static{//在該類(lèi)被ClassLoader加載時(shí)就初始化一個(gè)SerialTool目標(biāo)if(serialTool==null){
serialTool=newSerialTool();
}
}//私有化SerialTool類(lèi)的結(jié)構(gòu)辦法,不允許其他類(lèi)生成SerialTool目標(biāo)privateSerialTool(){}/***獲取供給服務(wù)的SerialTool目標(biāo)
*@returnserialTool*/publicstaticSerialToolgetSerialTool(){if(serialTool==null){
serialTool=newSerialTool();
}returnserialTool;
}/***查找所有可用端口
*@return可用端口稱(chēng)號(hào)列表*/publicstaticfinalArrayListfindPort(){//獲得當(dāng)時(shí)所有可用串口EnumerationportList=CommPortIdentifier.getPortIdentifiers();
ArrayListportNameList=newArrayList<>();//將可用串口名增加到List并回來(lái)該Listwhile(portList.hasMoreElements()){
StringportName=portList.nextElement().getName();
portNameList.add(portName);
}returnportNameList;
}/***翻開(kāi)串口
*@paramportName端口稱(chēng)號(hào)
*@parambaudrate波特率
*@return串口目標(biāo)
*@throwsSerialPortParameterFailure設(shè)置串口參數(shù)失敗
*@throwsNotASerialPort端口指向設(shè)備不是串口類(lèi)型
*@throwsNoSuchPort沒(méi)有該端口對(duì)應(yīng)的串口設(shè)備
*@throwsPortInUse端口已被占用*/publicstaticfinalSerialPortopenPort(StringportName,intbaudrate)throwsSerialPortParameterFailure,NotASerialPort,NoSuchPort,PortInUse{try{//通過(guò)端口名識(shí)別端口CommPortIdentifierportIdentifier=CommPortIdentifier.getPortIdentifier(portName);//翻開(kāi)端口,并給端口名字和一個(gè)timeout(翻開(kāi)操作的超時(shí)時(shí)刻)CommPortcommPort=portIdentifier.open(portName,2000);//判斷是不是串口if(commPortinstanceofSerialPort){
SerialPortserialPort=(SerialPort)commPort;try{//設(shè)置一下串口的波特率等參數(shù)serialPort.setSerialPortParams(baudrate,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
}catch(UnsupportedCommOperationExceptione){thrownewSerialPortParameterFailure();
}//System.out.println(“Open”+portName+”sucessfully!”);returnserialPort;
}else{//不是串口thrownewNotASerialPort();
}
}catch(NoSuchPortExceptione1){thrownewNoSuchPort();
}catch(PortInUseExceptione2){thrownewPortInUse();
}
}/***封閉串口
*@paramserialport待封閉的串口目標(biāo)*/publicstaticvoidclosePort(SerialPortserialPort){if(serialPort!=null){
serialPort.close();
serialPort=null;
}
}/***往串口發(fā)送數(shù)據(jù)
*@paramserialPort串口目標(biāo)
*@paramorder待發(fā)送數(shù)據(jù)
*@throwsSendDataToSerialPortFailure向串口發(fā)送數(shù)據(jù)失敗
*@throwsSerialPortOutputStreamCloseFailure封閉串口目標(biāo)的輸出流犯錯(cuò)*/publicstaticvoidsendToPort(SerialPortserialPort,byte[]order)throwsSendDataToSerialPortFailure,SerialPortOutputStreamCloseFailure{
OutputStreamout=null;try{
out=serialPort.getOutputStream();
out.write(order);
out.flush();
}catch(IOExceptione){thrownewSendDataToSerialPortFailure();
}finally{try{if(out!=null){
out.close();
out=null;
}
}catch(IOExceptione){thrownewSerialPortOutputStreamCloseFailure();
}
}
}/***從串口讀取數(shù)據(jù)
*@paramserialPort當(dāng)時(shí)已樹(shù)立銜接的SerialPort目標(biāo)
*@return讀取到的數(shù)據(jù)
*@throwsReadDataFromSerialPortFailure從串口讀取數(shù)據(jù)時(shí)犯錯(cuò)
*@throwsSerialPortInputStreamCloseFailure封閉串口目標(biāo)輸入流犯錯(cuò)*/publicstaticbyte[]readFromPort(SerialPortserialPort)throwsReadDataFromSerialPortFailure,SerialPortInputStreamCloseFailure{
InputStreamin=null;byte[]bytes=null;try{
in=serialPort.getInputStream();intbufflenth=in.available();//獲取buffer里的數(shù)據(jù)長(zhǎng)度while(bufflenth!=0){
bytes=newbyte[bufflenth];//初始化byte數(shù)組為buffer中數(shù)據(jù)的長(zhǎng)度in.read(bytes);
bufflenth=in.available();
}
}catch(IOExceptione){thrownewReadDataFromSerialPortFailure();
}finally{try{if(in!=null){
in.close();
in=null;
}
}catch(IOExceptione){thrownewSerialPortInputStreamCloseFailure();
}
}returnbytes;
}/***增加監(jiān)聽(tīng)器
*@paramport串口目標(biāo)
*@paramlistener串口監(jiān)聽(tīng)器
*@throwsTooManyListeners監(jiān)聽(tīng)類(lèi)目標(biāo)過(guò)多*/publicstaticvoidaddListener(SerialPortport,SerialPortEventListenerlistener)throwsTooManyListeners{try{//給串口增加監(jiān)聽(tīng)器port.addEventListener(listener);//設(shè)置當(dāng)有數(shù)據(jù)到達(dá)時(shí)喚醒監(jiān)聽(tīng)接納線(xiàn)程port.notifyOnDataAvailable(true);//設(shè)置當(dāng)通訊中斷時(shí)喚醒中斷線(xiàn)程port.notifyOnBreakInterrupt(true);
}catch(TooManyListenersExceptione){thrownewTooManyListeners();
}
}
注:該類(lèi)辦法中throw的Exception都是我自定義的Exception,之所以這么做是為了方便在主程序中進(jìn)行相應(yīng)處理,下面貼其中一個(gè)Exception出來(lái)給我們做下說(shuō)明:
(留意我所有自定義的Exception都放在serialException包里)
packageserialException;publicclassSerialPortParameterFailureextendsException{/****/privatestaticfinallongserialVersionUID=1L;publicSerialPortParameterFailure(){}
@OverridepublicStringtoString(){return”設(shè)置串口參數(shù)失敗!翻開(kāi)串口操作未完成!”;
}
}
每個(gè)自定義的Exception類(lèi)我都重寫(xiě)了它的toString()辦法,便于主程序捕捉到該Exception后打印對(duì)應(yīng)的過(guò)錯(cuò)信息
其中在serialException包里還有一個(gè)專(zhuān)門(mén)將接納到的Exception目標(biāo)內(nèi)的過(guò)錯(cuò)信息提取出來(lái)轉(zhuǎn)換成字符串并回來(lái)的類(lèi),代碼如下:
packageserialException;importjava.io.IOException;importjava.io.PrintWriter;importjava.io.StringWriter;/***負(fù)責(zé)將傳入的Exception中的過(guò)錯(cuò)信息提取出來(lái)并轉(zhuǎn)換成字符串;
*@authorzhong
**/publicclassExceptionWriter{/***將Exception中的過(guò)錯(cuò)信息封裝到字符串中并回來(lái)該字符串
*@parame包括過(guò)錯(cuò)的Exception
*@return過(guò)錯(cuò)信息字符串*/publicstaticStringgetErrorInfoFromException(Exceptione){
StringWritersw=null;
PrintWriterpw=null;try{
sw=newStringWriter();
pw=newPrintWriter(sw);
e.printStackTrace(pw);return”\r\n”+sw.toString()+”\r\n”;
}catch(Exceptione2){return”犯錯(cuò)啦!未獲取到過(guò)錯(cuò)信息,請(qǐng)查看后重試!”;
}finally{try{if(pw!=null){
pw.close();
}if(sw!=null){
sw.close();
}
}catch(IOExceptione1){
e1.printStackTrace();
}
}
}
}
四:
主程序類(lèi)的運(yùn)用,Client.java里含有程序的入口地址(main辦法),它的作用是顯現(xiàn)一個(gè)歡迎界面并調(diào)用DataView.java這個(gè)類(lèi)進(jìn)行實(shí)踐的串口數(shù)據(jù)顯現(xiàn)。
Client.java代碼如下:
packageserialPort;importjava.awt.Color;importjava.awt.FlowLayout;importjava.awt.Font;importjava.awt.Frame;importjava.awt.Graphics;importjava.awt.GridLayout;importjava.awt.Image;importjava.awt.Label;importjava.awt.Panel;importjava.awt.Toolkit;importjava.awt.event.KeyAdapter;importjava.awt.event.KeyEvent;importjava.awt.event.WindowAdapter;importjava.awt.event.WindowEvent;importjavax.swing.JOptionPane;importserialException.ExceptionWriter;/***主程序
*@authorzhong
**/publicclassClientextendsFrame{/****/privatestaticfinallongserialVersionUID=1L;/***程序界面寬度*/publicstaticfinalintWIDTH=800;/***程序界面高度*/publicstaticfinalintHEIGHT=620;/***程序界面呈現(xiàn)方位(橫坐標(biāo))*/publicstaticfinalintLOC_X=200;/***程序界面呈現(xiàn)方位(縱坐標(biāo))*/publicstaticfinalintLOC_Y=70;
Colorcolor=Color.WHITE;
ImageoffScreen=null;//用于雙緩沖//設(shè)置window的icon(這兒我自定義了一下Windows窗口的icon圖標(biāo),由于真實(shí)覺(jué)得哪個(gè)小咖啡圖標(biāo)不好看==)ToolkittoolKit=getToolkit();
Imageicon=toolKit.getImage(Client.class.getResource(“computer.png”));//持有其他類(lèi)DataViewdataview=newDataView(this);//主界面類(lèi)(顯現(xiàn)監(jiān)控?cái)?shù)據(jù)主面板)/***主辦法
*@paramargs//*/publicstaticvoidmain(String[]args){newClient().launchFrame();
}/***顯現(xiàn)主界面*/publicvoidlaunchFrame(){this.setBounds(LOC_X,LOC_Y,WIDTH,HEIGHT);//設(shè)定程序在桌面呈現(xiàn)的方位this.setTitle(“CDIO工程項(xiàng)目”);//設(shè)置程序標(biāo)題this.setIconImage(icon);this.setBackground(Color.white);//設(shè)置布景色this.addWindowListener(newWindowAdapter(){//增加對(duì)窗口狀態(tài)的監(jiān)聽(tīng)publicvoidwindowClosing(WindowEventarg0){//當(dāng)窗口封閉時(shí)System.exit(0);//退出程序}
});this.addKeyListener(newKeyMonitor());//增加鍵盤(pán)監(jiān)聽(tīng)器this.setResizable(false);//窗口大小不可更改this.setVisible(true);//顯現(xiàn)窗口newThread(newRepaintThread()).start();//開(kāi)啟重畫(huà)線(xiàn)程}/***畫(huà)出程序界面各組件元素*/publicvoidpaint(Graphicsg){
Colorc=g.getColor();
g.setFont(newFont(“微軟雅黑”,Font.BOLD,40));
g.setColor(Color.black);
g.drawString(“歡迎運(yùn)用上位機(jī)實(shí)時(shí)監(jiān)控體系”,45,190);
g.setFont(newFont(“微軟雅黑”,Font.ITALIC,26));
g.setColor(Color.BLACK);
g.drawString(“Version:1.0PoweredBy:ZhongLei”,280,260);
g.setFont(newFont(“微軟雅黑”,Font.BOLD,30));
g.setColor(color);
g.drawString(“————點(diǎn)擊Enter鍵進(jìn)入主界面————”,100,480);//使文字”————點(diǎn)擊Enter鍵進(jìn)入主界面————”是非閃爍if(color==Color.WHITE)color=Color.black;elseif(color==color.BLACK)color=Color.white;
}/***雙緩沖方法重畫(huà)界面各元素組件*/publicvoidupdate(Graphicsg){if(offScreen==null)offScreen=this.createImage(WIDTH,HEIGHT);
GraphicsgOffScreen=offScreen.getGraphics();
Colorc=gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0,0,WIDTH,HEIGHT);//重畫(huà)布景畫(huà)布this.paint(gOffScreen);//重畫(huà)界面元素gOffScreen.setColor(c);
g.drawImage(offScreen,0,0,null);//將新畫(huà)好的畫(huà)布“貼”在原畫(huà)布上}/**內(nèi)部類(lèi)方式完成對(duì)鍵盤(pán)事件的監(jiān)聽(tīng)*/privateclassKeyMonitorextendsKeyAdapter{publicvoidkeyReleased(KeyEvente){intkeyCode=e.getKeyCode();if(keyCode==KeyEvent.VK_ENTER){//當(dāng)監(jiān)聽(tīng)到用戶(hù)敲擊鍵盤(pán)enter鍵后履行下面的操作setVisible(false);//隱去歡迎界面dataview.setVisible(true);//顯現(xiàn)監(jiān)測(cè)界面dataview.dataFrame();//初始化監(jiān)測(cè)界面}
}
}/**重畫(huà)線(xiàn)程(每隔250毫秒重畫(huà)一次)*/privateclassRepaintThreadimplementsRunnable{publicvoidrun(){while(true){
repaint();try{
Thread.sleep(250);
}catch(InterruptedExceptione){//重畫(huà)線(xiàn)程犯錯(cuò)拋出反常時(shí)創(chuàng)建一個(gè)Dialog并顯現(xiàn)反常詳細(xì)信息Stringerr=ExceptionWriter.getErrorInfoFromException(e);
JOptionPane.showMessageDialog(null,err,”過(guò)錯(cuò)”,JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
}
}
運(yùn)轉(zhuǎn)截圖:
注:實(shí)踐運(yùn)轉(zhuǎn)過(guò)程中最下面的“點(diǎn)擊Enter鍵進(jìn)入主界面”有一個(gè)一閃一閃的作用(是通過(guò)每隔一段時(shí)刻重畫(huà)一次界面,讓這句話(huà)以白黑兩色反復(fù)替換呈現(xiàn)完成的),雙緩沖方法利于處理重畫(huà)時(shí)界面閃爍的問(wèn)題(如果不運(yùn)用雙緩沖方法的話(huà)相當(dāng)于每次重畫(huà)時(shí)是在舊界面上一點(diǎn)一點(diǎn)畫(huà)上新東西,而雙緩沖實(shí)質(zhì)上是通過(guò)先在內(nèi)存中直接畫(huà)好一張新界面圖,然后一次性直接用新界面覆蓋掉舊界面)
1.串口通訊指串口按位(bit)發(fā)送和接納字節(jié)。盡管比按字節(jié)(byte)的并行通訊慢,可是串口能夠在運(yùn)用一根線(xiàn)發(fā)送數(shù)據(jù)的一起用另一根線(xiàn)接納數(shù)據(jù)。
2.串口是計(jì)算機(jī)上一種非常通用的設(shè)備通訊協(xié)議(不要與通用串行總線(xiàn)UniversalSerialBus或者USB混雜)
3.地,串口用于ASCII碼字符的傳輸。通訊運(yùn)用3根線(xiàn)完成:(1)地線(xiàn),(2)發(fā)送,(3)接納。因?yàn)榇谕ㄓ嵤钱惒降?,端口能夠在一根線(xiàn)上發(fā)送數(shù)據(jù)一起在另一根線(xiàn)上接納數(shù)據(jù)。其他線(xiàn)用于握手,可是不是有必要的。串口通訊最重要的參數(shù)是比特率、數(shù)據(jù)位、中止位和奇偶校驗(yàn)。關(guān)于兩個(gè)進(jìn)行通訊的端口,這些參數(shù)有必要匹配
4.-232(ANSI/EIA-232規(guī)范)是IBM-PC及其兼容機(jī)上的串行銜接規(guī)范、RS-422(EIARS-422-AStandard)是Apple的Macintosh計(jì)算機(jī)的串口銜接規(guī)范。RS-485(EIA-485規(guī)范)是RS-422的改進(jìn)。
說(shuō)到開(kāi)源,恐怕很少有人不挑大指稱(chēng)譽(yù)。學(xué)生經(jīng)過(guò)開(kāi)源代碼學(xué)到了常識(shí),程序員經(jīng)過(guò)開(kāi)源類(lèi)庫(kù)獲得了別人的成功經(jīng)驗(yàn)及能夠按時(shí)完成手頭的工程,商家經(jīng)過(guò)開(kāi)源軟件賺到了錢(qián)……,總歸是大快人心。但是開(kāi)源軟件或類(lèi)庫(kù)的首要缺陷便是大多缺乏具體的闡明文檔和運(yùn)用的比如,或者便是軟件代碼隨意你用,便是文檔,比如和后期服務(wù)收錢(qián)。這也難怪,究竟就像某個(gè)著名NBA球員說(shuō)的那樣:“我還要養(yǎng)家,所以千萬(wàn)美元以下的合同別找我談,不然我寧可失業(yè)”。是啊,支撐開(kāi)源的人也要養(yǎng)家,收點(diǎn)錢(qián)也不過(guò)火。要想既不花錢(qián)又學(xué)到常識(shí)就只能借助網(wǎng)絡(luò)和了,我僅僅想拋磚引玉,為開(kāi)源事業(yè)做出點(diǎn)菲薄共獻(xiàn),能為你的工程處理哪怕一個(gè)小問(wèn)題,也就足夠了。
盡管我的這個(gè)系列介紹的東西不是什么Web結(jié)構(gòu),也不是什么開(kāi)源服務(wù)器,可是我信任,作為一個(gè)程序員,什么樣的問(wèn)題都會(huì)遇到。有時(shí)分越是簡(jiǎn)單的問(wèn)題反而越棘手;越是小的地方就越是找不到稱(chēng)手的家伙。只要你不是整天只與“架構(gòu)”、“構(gòu)件”、“結(jié)構(gòu)”打交道的話(huà),信任我所說(shuō)的東西你一定會(huì)用到。
1串口通訊簡(jiǎn)介
1.1常見(jiàn)的Java串口包
1.2串口包的裝置(Windows下)
2串口API概覽
2.1javax.comm.CommPort
2.2javax.comm.CommPortIdentifier
2.3javax.comm.SerialPort
2.4串口API實(shí)例
2.4.1列舉出本機(jī)一切可用串口
2.4.2串口參數(shù)的裝備
2.4.3串口的讀寫(xiě)
3串口通訊的通用形式及其問(wèn)題
3.1事情監(jiān)聽(tīng)模型
3.2串口讀數(shù)據(jù)的線(xiàn)程模型
3.3第三種辦法
4結(jié)束語(yǔ)
1串口通訊簡(jiǎn)介
嵌入式體系或傳感器網(wǎng)絡(luò)的很多運(yùn)用和測(cè)試都需求經(jīng)過(guò)PC機(jī)與嵌入式設(shè)備或傳感器節(jié)點(diǎn)進(jìn)行通訊。其間,最常用的接口便是RS-232串口和并口(鑒于USB接口的復(fù)雜性以及不需求很大的數(shù)據(jù)傳輸量,USB接口用在這兒仍是顯得過(guò)于奢侈,何況現(xiàn)在除了SUN有一個(gè)支撐USB的包之外,我還沒(méi)有看到其他直接支撐USB的Java類(lèi)庫(kù))。SUN的CommAPI分別提供了對(duì)常用的RS232串行端口和IEEE1284并行端口通訊的支撐。RS-232-C(又稱(chēng)EIARS-232-C,以下簡(jiǎn)稱(chēng)RS232)是在1970年由美國(guó)電子工業(yè)協(xié)會(huì)(EIA)聯(lián)合貝爾體系、調(diào)制解調(diào)器廠(chǎng)家及計(jì)算機(jī)終端生產(chǎn)廠(chǎng)家共同擬定的用于串行通訊的規(guī)范。RS232是一個(gè)全雙工的通訊協(xié)議,它能夠一起進(jìn)行數(shù)據(jù)接納和發(fā)送的作業(yè)。
1.1常見(jiàn)的Java串口包
現(xiàn)在,常見(jiàn)的Java串口包有SUN在1998年發(fā)布的串口通訊API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通訊API以及一個(gè)開(kāi)源的完成。鑒于在Windows下SUN的API比較常用以及IBM的完成和SUN的在API層面都是一樣的,那個(gè)開(kāi)源的完成又不像兩家大廠(chǎng)的產(chǎn)品那樣讓人定心,這兒就只介紹SUN的串口通訊API在Windows平臺(tái)下的運(yùn)用。
1.2串口包的裝置(Windows下)
到SUN的網(wǎng)站下載javacomm20-win32.zip,包括的東西如下所示:
java串口通訊編程實(shí)例
按照其運(yùn)用闡明(Readme.html)的說(shuō)法,要想運(yùn)用串口包進(jìn)行串口通訊,除了設(shè)置好環(huán)境變量之外,還要將win32com.dll仿制到《JDK》/bin目錄下;將comm.jar仿制到《JDK》/lib;把javax.comm.properties也相同拷貝到《JDK》/lib目錄下。但是在真實(shí)運(yùn)轉(zhuǎn)運(yùn)用串口包的時(shí)分,僅作這些是不行的。因?yàn)橐话惝?dāng)運(yùn)轉(zhuǎn)“javaMyApp”的時(shí)分,是由JRE下的虛擬機(jī)發(fā)動(dòng)MyApp的。而咱們只仿制上述文件到JDK相應(yīng)目錄下,所以運(yùn)用程序?qū)?huì)提示找不到串口。處理這個(gè)問(wèn)題的辦法很簡(jiǎn)單,咱們只須將上面說(shuō)到的文件放到JRE相應(yīng)的目錄下就能夠了。
值得留意的是,在網(wǎng)絡(luò)運(yùn)用程序中運(yùn)用串口API的時(shí)分,還會(huì)遇到其他更復(fù)雜問(wèn)題。有興趣的話(huà),你能夠檢查CSDN社區(qū)中“關(guān)于網(wǎng)頁(yè)上Applet用javacomm20讀取客戶(hù)端串口的問(wèn)題”的帖子。
2串口API概覽
2.1javax.comm.CommPort
這是用于描繪一個(gè)被底層體系支撐的端口的抽象類(lèi)。它包括一些高層的IO操控辦法,這些辦法關(guān)于一切不同的通訊端口來(lái)說(shuō)是通用的。SerialPort和ParallelPort都是它的子類(lèi),前者用于操控串行端口而后者用于控這并口,二者關(guān)于各自底層的物理端口都有不同的操控辦法。這兒咱們只關(guān)心SerialPort。
2.2javax.comm.CommPortIdentifier
這個(gè)類(lèi)首要用于對(duì)串口進(jìn)行辦理和設(shè)置,是對(duì)串口進(jìn)行訪(fǎng)問(wèn)操控的中心類(lèi)。首要包括以下辦法
l確認(rèn)是否有可用的通訊端口
l為IO操作翻開(kāi)通訊端口
l決議端口的一切權(quán)
l處理端口一切權(quán)的爭(zhēng)用
l辦理端口一切權(quán)變化引發(fā)的事情(Event)
2.3javax.comm.SerialPort
這個(gè)類(lèi)用于描繪一個(gè)RS-232串行通訊端口的底層接口,它界說(shuō)了串口通訊所需的最小功用集。經(jīng)過(guò)它,用戶(hù)能夠直接對(duì)串口進(jìn)行讀、寫(xiě)及設(shè)置作業(yè)。
2.4串口API實(shí)例
大段的文字怎樣也不如一個(gè)小比如來(lái)的明晰,下面咱們就一起看一下串口包自帶的比如—SerialDemo中的一小段代碼來(lái)加深對(duì)串口API中心類(lèi)的運(yùn)用辦法的認(rèn)識(shí)。
2.4.1列舉出本機(jī)一切可用串口
voidlistPortChoices(){
CommPortIdentifierportId;
Enumerationen=CommPortIdentifier.getPortIdentifiers();
//iteratethroughtheports.
while(en.hasMoreElements()){
portId=(CommPortIdentifier)en.nextElement();
if(portId.getPortType()==CommPortIdentifier.PORT_SERIAL){
System.out.println(portId.getName());
}
}
portChoice.select(parameters.getPortName());
}
以上代碼能夠列舉出當(dāng)前體系一切可用的串口稱(chēng)號(hào),我的機(jī)器上輸出的成果是COM1和COM3。
2.4.2串口參數(shù)的裝備
串口一般有如下參數(shù)能夠在該串口翻開(kāi)以前裝備進(jìn)行裝備:
java串口通訊編程實(shí)例
包括波特率,輸入/輸出流操控,數(shù)據(jù)位數(shù),中止位和齊偶校驗(yàn)。
SerialPortsPort;
try{
sPort.setSerialPortParams(BaudRate,Databits,Stopbits,Parity);
//設(shè)置輸入/輸出操控流
sPort.setFlowControlMode(FlowControlIn|FlowControlOut);
}catch(UnsupportedCommOperationExceptione){}
2.4.3串口的讀寫(xiě)
對(duì)串口讀寫(xiě)之前需求先翻開(kāi)一個(gè)串口:
CommPortIdentifierportId=CommPortIdentifier.getPortIdentifier(PortName);
try{
SerialPortsPort=(SerialPort)portId.open(“串口一切者稱(chēng)號(hào)”,超時(shí)等待時(shí)刻);
}catch(PortInUseExceptione){//假如端口被占用就拋出這個(gè)異常
thrownewSerialConnectionException(e.getMessage());
}
//用于對(duì)串口寫(xiě)數(shù)據(jù)
OutputStreamos=newBufferedOutputStream(sPort.getOutputStream());
os.write(intdata);
//用于從串口讀數(shù)據(jù)
InputStreamis=newBufferedInputStream(sPort.getInputStream());
intreceivedData=is.read();
讀出來(lái)的是int型,你能夠把它轉(zhuǎn)換成需求的其他類(lèi)型。
這兒要留意的是,因?yàn)镴ava言語(yǔ)沒(méi)有無(wú)符號(hào)類(lèi)型,即一切的類(lèi)型都是帶符號(hào)的,在由byte到int的時(shí)分應(yīng)該尤其留意。因?yàn)榧偃鏱yte的最高位是1,則轉(zhuǎn)成int類(lèi)型時(shí)將用1來(lái)占位。這樣,原本是10000000的byte類(lèi)型的數(shù)變成int型就成了1111111110000000,這是很?chē)?yán)峻的問(wèn)題,應(yīng)該留意防止。
3串口通訊的通用形式及其問(wèn)題
終于啰嗦完我最討厭的基礎(chǔ)常識(shí)了,下面開(kāi)端咱們本次的重點(diǎn)–串口運(yùn)用的研討。因?yàn)橄虼趯?xiě)數(shù)據(jù)很簡(jiǎn)單,所以這兒咱們只重視于從串口讀數(shù)據(jù)的狀況。一般,串口通訊運(yùn)用程序有兩種形式,一種是完成SerialPortEventListener接口,監(jiān)聽(tīng)各種串口事情并作相應(yīng)處理;另一種便是建立一個(gè)獨(dú)立的接納線(xiàn)程專(zhuān)門(mén)負(fù)責(zé)數(shù)據(jù)的接納。因?yàn)檫@兩種辦法在某些狀況下存在很?chē)?yán)峻的問(wèn)題(至于什么問(wèn)題這兒先賣(mài)個(gè)關(guān)子J),所以我的完成是選用第三種辦法來(lái)處理這個(gè)問(wèn)題。
3.1事情監(jiān)聽(tīng)模型
現(xiàn)在咱們來(lái)看看事情監(jiān)聽(tīng)模型是怎么運(yùn)作的
:
l首先需求在你的端口操控類(lèi)(例如SManager)加上“implementsSerialPortEventListener”
l在初始化時(shí)參加如下代碼:
try{
SerialPortsPort.addEventListener(SManager);
}catch(TooManyListenersExceptione){
sPort.close();
thrownewSerialConnectionException(“toomanylistenersadded”);
}
sPort.notifyOnDataAvailable(true);
l覆寫(xiě)publicvoidserialEvent(SerialPortEvente)辦法,在其間對(duì)如下事情進(jìn)行判別:
BI-通訊中斷。
CD-載波檢測(cè)。
CTS-鏟除發(fā)送。
DATA_AVAILABLE-有數(shù)據(jù)抵達(dá)。
DSR-數(shù)據(jù)設(shè)備準(zhǔn)備好。
FE-幀過(guò)錯(cuò)。
OE-溢位過(guò)錯(cuò)。
OUTPUT_BUFFER_EMPTY-輸出緩沖區(qū)已清空。
PE-奇偶校驗(yàn)錯(cuò)。
RI-振鈴指示。
一般最常用的便是DATA_AVAILABLE–串口有數(shù)據(jù)抵達(dá)事情。也便是說(shuō)當(dāng)串口有數(shù)據(jù)抵達(dá)時(shí),你能夠在serialEvent中接納并處理所收到的數(shù)據(jù)。但是在我的實(shí)踐中,遇到了一個(gè)非常嚴(yán)峻的問(wèn)題。
首先描繪一下我的試驗(yàn):我的運(yùn)用程序需求接納傳感器節(jié)點(diǎn)從串口發(fā)回的查詢(xún)數(shù)據(jù),并將成果以圖標(biāo)的方式顯現(xiàn)出來(lái)。串口設(shè)定的波特率是115200,川口每隔128毫秒回來(lái)一組數(shù)據(jù)(大約是30字節(jié)左右),周期(即持續(xù)時(shí)刻)為31秒。實(shí)測(cè)的時(shí)分在一個(gè)周期內(nèi)應(yīng)該回來(lái)4900多個(gè)字節(jié),而用事情監(jiān)聽(tīng)模型我最多只能收到不到1500字節(jié),不知道這些字節(jié)都跑哪里去了,也不清楚到底丟掉的是那部分?jǐn)?shù)據(jù)。值得留意的是,這是我將serialEvent()中一切處理代碼都注掉,只剩下打印代碼所得的成果。數(shù)據(jù)丟掉的如此嚴(yán)峻是我所不能忍受的,所以我決議選用其他辦法。
3.2串口讀數(shù)據(jù)的線(xiàn)程模型
這個(gè)模型顧名思義,便是將接納數(shù)據(jù)的操作寫(xiě)成一個(gè)線(xiàn)程的方式:
publicvoidstartReadingDataThread(){
ThreadreadDataProcess=newThread(newRunnable(){
publicvoidrun(){
while(newData!=-1){
try{
newData=is.read();
System.out.println(newData);
//其他的處理過(guò)程
………。
}catch(IOExceptionex){
System.err.println(ex);
return;
}
}
readDataProcess.start();
}
在我的運(yùn)用程序中,我將收到的數(shù)據(jù)打包放到一個(gè)緩存中,然后發(fā)動(dòng)另一個(gè)線(xiàn)程從緩存中獲取并處理數(shù)據(jù)。兩個(gè)線(xiàn)程以生產(chǎn)者—顧客形式協(xié)同作業(yè),數(shù)據(jù)的流向如下圖所示:
java串口通訊編程實(shí)例
這樣,我就圓滿(mǎn)處理了丟數(shù)據(jù)問(wèn)題。但是,沒(méi)快樂(lè)多久我就又發(fā)現(xiàn)了一個(gè)相同嚴(yán)峻的問(wèn)題:盡管這回不再丟數(shù)據(jù)了,可是原本一個(gè)周期(31秒)之后,傳感器節(jié)電現(xiàn)已中止傳送數(shù)據(jù)了,但我的串口線(xiàn)程仍然在盡力的執(zhí)行讀串口操作,在操控臺(tái)也能夠看見(jiàn)收到的數(shù)據(jù)仍在不斷的打印。原來(lái),因?yàn)閭鞲衅鞴?jié)點(diǎn)發(fā)送的數(shù)據(jù)過(guò)快,而我的接納線(xiàn)程處理不過(guò)來(lái),所以InputStream就先把已抵達(dá)卻還沒(méi)處理的字節(jié)緩存起來(lái),所以就導(dǎo)致了明明傳感器節(jié)點(diǎn)現(xiàn)已不再發(fā)數(shù)據(jù)了,而操控臺(tái)卻還能看見(jiàn)數(shù)據(jù)不斷打印這一古怪的現(xiàn)象。僅有值得慶幸的是最后收到數(shù)據(jù)確實(shí)是4900左右字節(jié),沒(méi)呈現(xiàn)丟掉現(xiàn)象。但是當(dāng)處理完最后一個(gè)數(shù)據(jù)的時(shí)分現(xiàn)已快1分半鐘了,這個(gè)時(shí)刻遠(yuǎn)遠(yuǎn)大于節(jié)點(diǎn)運(yùn)轉(zhuǎn)周期。這一推遲關(guān)于一個(gè)實(shí)時(shí)的顯現(xiàn)體系來(lái)說(shuō)簡(jiǎn)直是災(zāi)難!
后來(lái)我想,是不是因?yàn)閮蓚€(gè)線(xiàn)程之間的同步和通訊導(dǎo)致了數(shù)據(jù)接納緩慢呢?所以我在接納線(xiàn)程的代碼中去掉了一切處理代碼,僅保存打印收到數(shù)據(jù)的句子,成果仍然如故??磥?lái)并不是線(xiàn)程間的通訊阻止了數(shù)據(jù)的接納速度,而是用線(xiàn)程模型導(dǎo)致了關(guān)于發(fā)送端數(shù)據(jù)發(fā)送速率過(guò)快的狀況下的數(shù)據(jù)接納推遲。這兒申明一點(diǎn),便是關(guān)于數(shù)據(jù)發(fā)送速率不是如此快的狀況下前面者兩種模型應(yīng)該仍是好用的,僅僅特別狀況仍是應(yīng)該特別處理。
3.3第三種辦法
痛苦了許久(Boss天天催我L)之后,偶然的時(shí)機(jī),我聽(tīng)說(shuō)TinyOS中(又是開(kāi)源的)有一部分是和我的運(yùn)用程序類(lèi)似的串口通訊部分,所以我下載了它的1.x版的Java代碼部分,參考了它的處理辦法。處理問(wèn)題的辦法說(shuō)穿了其實(shí)很簡(jiǎn)單,便是從本源入手。本源不便是接納線(xiàn)程導(dǎo)致的嗎,那好,我就干脆撤銷(xiāo)接納線(xiàn)程和作為中介的同享緩存,而直接在處理線(xiàn)程中調(diào)用串口讀數(shù)據(jù)的辦法來(lái)處理問(wèn)題(什么,為什么不把處理線(xiàn)程也同時(shí)撤銷(xiāo)?—-都撤銷(xiāo)運(yùn)用程序界面不就鎖死了嗎?所以有必要保存)所以程序變成了這樣:
publicbyte[]getPack(){
while(true){
//PacketLength為數(shù)據(jù)包長(zhǎng)度
byte[]msgPack=newbyte[PacketLength];
for(inti=0;i《PacketLength;i++){
if((newData=is.read())!=-1){
msgPack[i]=(byte)newData;
System.out.println(msgPack[i]);
}
}
returnmsgPack;
}
}
在處理線(xiàn)程中調(diào)用這個(gè)辦法回來(lái)所需求的數(shù)據(jù)序列并處理之,這樣不光沒(méi)有丟掉數(shù)據(jù)的現(xiàn)象行呈現(xiàn),也沒(méi)有數(shù)據(jù)接納推遲了。這兒僅有需求留意的便是當(dāng)串口中止發(fā)送數(shù)據(jù)或沒(méi)有數(shù)據(jù)的時(shí)分is.read()一直都回來(lái)-1,假如一旦在開(kāi)端接納數(shù)據(jù)的時(shí)分發(fā)現(xiàn)-1就不要理它,繼續(xù)接納,直到收到真實(shí)的數(shù)據(jù)為止。
4結(jié)束語(yǔ)
本文介紹了串口通訊的基本常識(shí),以及常用的幾種形式。經(jīng)過(guò)實(shí)踐,提出了一些問(wèn)題,并在最后加以處理。值得留意的是關(guān)于榜首種辦法,我曾將傳感器發(fā)送的時(shí)刻由128毫秒增加到512毫秒,仍然有很?chē)?yán)峻的數(shù)據(jù)丟掉現(xiàn)象發(fā)生,所以假如你的運(yùn)用程序需求很精密的成果,傳輸數(shù)據(jù)的速率又很快的話(huà),就最好不要用榜首種辦法。關(guān)于第二種辦法,因?yàn)槭蔷€(xiàn)程導(dǎo)致的問(wèn)題,所以關(guān)于不同的機(jī)器應(yīng)該會(huì)有不同的體現(xiàn),關(guān)于那些處理多線(xiàn)程比較好的機(jī)器來(lái)說(shuō),應(yīng)該會(huì)好一些??墒俏业臋C(jī)器是Inter奔四3.0雙核CPU+512DDR內(nèi)存,這樣都推遲這么兇猛,還得多強(qiáng)的CPU才行?。克躁P(guān)于數(shù)據(jù)量比較大的傳輸來(lái)說(shuō),仍是用第三種辦法吧。不過(guò)這個(gè)國(guó)際問(wèn)題是很多的,并且未知的問(wèn)題比已知的問(wèn)題多的多,說(shuō)不定還有什么其他問(wèn)題存在,歡迎你經(jīng)過(guò)下面的聯(lián)系方式和我一起研討。
廣州天河區(qū)珠江新城富力盈力大廈北塔2706
020-38013166(網(wǎng)站咨詢(xún)專(zhuān)線(xiàn))
400-001-5281 (售后服務(wù)熱線(xiàn))
深圳市坂田十二橡樹(shù)莊園F1-7棟
Site/ http://www.szciya.com
E-mail/ itciya@vip.163.com
品牌服務(wù)專(zhuān)線(xiàn):400-001-5281
長(zhǎng)沙市天心區(qū)芙蓉中路三段398號(hào)新時(shí)空大廈5樓
聯(lián)系電話(huà)/ (+86 0731)88282200
品牌服務(wù)專(zhuān)線(xiàn)/ 400-966-8830
旗下運(yùn)營(yíng)網(wǎng)站:
Copyright ? 2016 廣州思洋文化傳播有限公司,保留所有權(quán)利。 粵ICP備09033321號(hào)