




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
Java串口通信詳解序言說到開源,恐怕很少有人不挑大指稱贊。學生通過開源代碼學到了知識,程序員通過開源類庫獲得了別人的成功經驗及能夠按時完成手頭的工程,商家通過開源軟件賺到了錢……,總之是皆大歡喜。然而開源軟件或類庫的首要缺點就是大多缺乏詳細的說明文檔和使用的例子,或者就是軟件代碼隨便你用,就是文檔,例子和后期服務收錢。這也難怪,畢竟就像某個著名NBA球員說的那樣:''我還要養家,所以千萬美元以下的合同別找我談,否則我寧可待業”。是啊,支持開源的人也要養家,收點錢也不過分。要想既不花錢又學到知識就只能借助網絡和了,我只是想拋磚引玉,為開源事業做出點微薄共獻,能為你的工程解決哪怕一個小問題,也就足夠了。雖然我的這個系列介紹的東西不是什么Web框架,也不是什么開源服務器,但是我相信,作為一個程序員,什么樣的問題都會遇到。有時候越是簡單的問題反而越棘手;越是小的地方就越是找不到稱手的家伙。只要你不是整天只與''架構”、''構件”、''框架”打交道的話,相信我所說的東西你一定會用到。串口通信簡介常見的Java串口包串口包的安裝(Windows下)串口API概覽m.CommPm.CommPortIdem.SerialPort串口API實例列舉出本機所有可用串口串口參數的配置串口的讀寫串口通信的通用模式及其問題事件監聽模型串口讀數據的線程模型第三種方法結束語1串口通信簡介嵌入式系統或傳感器網絡的很多應用和測試都需要通過PC機與嵌入式設備或傳感器節點進行通信。其中,最常用的接口就是RS-232串口和并口(鑒于USB接口的復雜性以及不需要很大的數據傳輸量,USB接口用在這里還是顯得過于奢侈,況且目前除了SUN有一個支持USB的包之外,我還沒有看到其他直接支持USB的Java類庫)。SUN的CommAPI分別提供了對常用的RS232串行端口和IEEE1284并行端口通訊的支持。RS-232-C(又稱EIARS-232-C,以下簡稱RS232)是在1970年由美國電子工業協會(EIA)聯合貝爾系統、調制解調器廠家及計算機終端生產廠家共同制定的用于串行通訊的標準。RS232是一個全雙工的通訊目前,常見的Java串口包有SUN在1998年發布的串口通信API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一個開源的實現。鑒于在Windows下SUN的API比較常用以及IBM的實現和SUN的在API層面都是一樣的,那個開源的實現又不像兩家大廠的產品那樣讓人放心,這里就只介紹SUN的串口通信API在Windows1.2串口包的安裝(Windows下)到SUN的網站下載javacomm20-win32.zip,包含的東西如下所示:按照其使用說明(Readme.html)的說法,要想使用串口包進行串口通信,除了設置好環境變量之外,還要將win32com.dll復制到vJDK>\bin目錄下;將comm.jar復制到vJDK>\lib;把perties也同樣拷貝到<JDK>\lib目錄下。然而在真正運行使用串口包的時候,僅作這些是不夠的。因為通常當運行“javaMyApp"的時候,是由JRE下的虛擬機啟動MyApp的。而我們只復制上述文件到JDK相應目錄下,所以應用程序將會提示找不到串口。解決這個問題的方法很簡單,我們只須將上面提到的文件放到JRE相應的目錄下就可以了。值得注意的是,在網絡應用程序中使用串口API的時候,還會遇到其他更復雜問題。有興趣的話,你可以查看CSDN社區中''關于網頁上Applet用javacomm20讀取客戶端串口的問題"的帖子。2串口API概覽m.CommPort這是用于描述一個被底層系統支持的端口的抽象類。它包含一些高層的io控制方法,這些方法對于所有不同的通訊端口來說是通用的。SerialPort和ParallelPort都是它的子類,前者用于控制串行端口而后者用于控這并口,二者對于各自底層的物理端口都有不同的控制方法。這里我們只關心SerialPort。javax?comm?CommPortIdentifier這個類主要用于對串口進行管理和設置,是對串口進行訪問控制的核心類。主要包括以下方法l確定是否有可用的通信端口l為io操作打開通信端口l決定端口的所有權l處理端口所有權的爭用l管理端口所有權變化引發的事件(Event)m.SerialPort這個類用于描述一個RS-232串行通信端口的底層接口,它定義了串口通信所需的最小功能集。通過它,用戶可以直接對串口進行讀、寫及設置工作。2.4串口API實例大段的文字怎么也不如一個小例子來的清晰,下面我們就一起看一下串口包自帶的例子---SerialDemo中的一小段代碼來加深對串口API核心類的使用方法的認識。2?4?1列舉出本機所有可用串口voidlistPortChoices(){CommPortldentifierportId;Enumerationen=CommPortldentifier.getPortIdentifiers();//iteratethroughtheports.while(en.hasMoreElements()){portId=(CommPortIdentifier)en.nextElement();if(portId.getPortType()==CommPortIdentifier.PORT_SERIAL){System.out.println(portId.getName());}}portChoice.select(parameters.getPortName());}以上代碼可以列舉出當前系統所有可用的串口名稱,我的機器上輸出的結果是COM1和COM3。2?4?2串口參數的配置串口一般有如下參數可以在該串口打開以前配置進行配置:包括波特率,輸入/輸出流控制,數據位數,停止位和齊偶校驗。SerialPortsPort;try{sPort.setSerialPortParams(BaudRate,Databits,Stopbits,Parity);〃設置輸入/輸出控制流sPort.setFlowControlMode(FlowControlln|FlowControlOut);}catch(UnsupportedCommOperationExceptione){}2?4?3串口的讀寫對串口讀寫之前需要先打開一個串口:CommPortIdentifierportId=CommPortIdentifier.getPortIdentifier(PortName);try{SerialPortsPort=(SerialPort)portId.open("串口所有者名稱",超時等待時間);}catch(PortInUseExceptione){//如果端口被占用就拋出這個異常
thrownewSerialConnectionException(e.getMessage());}〃用于對串口寫數據OutputStreamos=newBufferedOutputStream(sPort.getOutputStream());os.write(intdata);〃用于從串口讀數據InputStreamis=newBufferedInputStream(sPort.getInputStream());intreceivedData=is.read();讀出來的是int型,你可以把它轉換成需要的其他類型。這里要注意的是,由于Java語言沒有無符號類型,即所有的類型都是帶符號的,在由byte到int的時候應該尤其注意。因為如果byte的最高位是1,則轉成int類型時將用1來占位。這樣,原本是10000000的byte類型的數變成int型就成了1111111110000000,這是很嚴重的問題,應該注意避免。3串口通信的通用模式及其問題終于嘮叨完我最討厭的基礎知識了,下面開始我們本次的重點--串口應用的研究。由于向串口寫數據很簡單,所以這里我們只關注于從串口讀數據的情況。通常,串口通信應用程序有兩種模式,一種是實現SerialPortEventListener接口,監聽各種串口事件并作相應處理;另一種就是建立一個獨立的接收線程專門負責數據的接收。由于這兩種方法在某些情況下存在很嚴重的問題(事于什監問題這型先賣個關子J),所以我的實現是采用第三種方法來解決這個問題。現在我們來看看事件監聽模型是如何運作的l 首先需要在你的端口控制類(例如SManager)加上“implementsSerialPortEventListener”l 在初始化時加入如下代碼:try{SerialPortsPort.addEventListener(SManager);}catch(TooManyListenersExceptione){sPort.close();thrownewSerialConnectionException("toomanylistenersadded");}sPort.notifyOnDataAvailable(true);l覆寫publicvoidserialEvent(SerialPortEvente)方法,在其中對如下事件進行判斷:BI-通訊中斷.CD-載波檢測.CTS-清除發送.DATA_AVAILABLE-有數據到達.DSR^數據設備準備好.FE-幀錯誤.OE-溢位錯誤.OUTPUT_BUFFER_EMPTY-輸出緩沖區已清空.PE-奇偶校驗錯.RI-振鈴指示.一般最常用的就是DATA_AVAILABLE--串口有數據到達事件。也就是說當串口有數據到達時,你可以在serialEvent中接收并處理所收到的數據。然而在我的實踐中,遇到了一個十分嚴重的問題。首先描述一下我的實驗:我的應用程序需要接收傳感器節點從串口發回的查詢數據,并將結果以圖標的形式顯示出來。串口設定的波特率是115200,川口每隔128毫秒返回一組數據(大約是30字節左右),周期(即持續時間)為31秒。實測的時候在一個周期內應該返回4900多個字節,而用事件監聽模型我最多只能收到不到1500字節,不知道這些字節都跑哪里去了,也不清楚到底丟失的是那部分數據。值得注意的是,這是我將serialEvent()中所有處理代碼都注掉,只剩下打印代碼所得的結果。數據丟失的如此嚴重是我所不能忍受的,于是我決定采用其他方法3?2串口讀數據的線程模型這個模型顧名思義,就是將接收數據的操作寫成一個線程的形式:publicvoidstartReadingDataThread(){ThreadreadDataProcess=newThread(newRunnable(){publicvoidrun(){while(newData!=-1){try{newData=is.read();System.out.println(newData);〃其他的處理過程}catch(IOExceptionex){System.err.println(ex);return;雖然這回不再丟數據了,可是原本一個周期(31秒)之后,傳感器節電已經停止傳送數據了,但我的串口線程依然在努力的執行讀串口操作,在控制臺也可以看見收到的數據仍在不斷的打印。原來,由于傳感器節點發送的數據過快,而我的接收線程處理不過來,所以InputStream就先把已到達卻還沒處理的字節緩存起來,于是就導致了明明傳感器節點已經不再發數據了,而控制臺卻還能看見數據不斷打印這一奇怪的現象。唯一值得慶幸的是最后收到數據確實是4900左右字節,沒出現丟失現象。然而當處理完最后一個數據的時候已經快1分半鐘了,這個時間遠遠大于節點運行周期。這一延遲對于一個實時的顯示系統來說簡直是災難!后來我想,是不是由于兩個線程之間的同步和通信導致了數據接收緩慢呢?于是我在接收線程的代碼中去掉了所有處理代碼,僅保留打印收到數據的語句,結果依然如故。看來并不是線程間的通信阻礙了數據的接收速度,而是用線程模型導致了對于發送端數據發送速率過快的情況下的數據接收延遲。這里申明一點,就是對于數據發送速率不是如此快的情況下前面者兩種模型應該還第三的方只法是特殊情況還是應該特殊處理。痛苦了許久(Boss天天催我L)之后,偶然的機會,我聽說TinyOS中(又是開源的)有一部分是和我的應用程序類似的串口通信部分,于是我下載了它的1.x版的Java代碼部分,參考了它的處理方法。解決問題的方法說穿了其實很簡單,就是從根源入手。根源不就是接收線程導致的嗎,那好,我就干脆取消接收線程和作為中介的共享緩存,而直接在處理線程中調用串口讀數據的方法來解決問題(什么,為什么不把處理線程也一并取消?----都取消應用程序界面不就鎖死了嗎?所以必須保留)于是程序變成了這樣:publicbyte[]getPack(){while(true){//PacketLength為數據包長度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;}}在處理線程中調用這個方法返回所需要的數據序列并處理之,這樣不但沒有丟失數據的現象行出現,也沒有數據接收延遲了。這里唯一需要注意的就是當串口停止發送數據或沒有數據的時候is.read()—直都返回-1,如果一旦在開始接收數據的時候發現-1就不要理它,繼續接收,直到收到真正的數據為止。4結束語本文介紹了串口通信的基本知識,以及常用的幾種模式。通過實踐,提出了一些問題,并在最后加以解決。值得注意的是對于第一種方法,我曾將傳感器發送的時間由128毫秒增加到512毫秒,仍然有很嚴重的數據丟失現象發生,所以如果你的應用程序需要很精密的結果,傳輸數據的速率又很快的話,就最好不要用第一種方法。對于第二種方法,由于是線程導致的問題,所以對于不同的機器應該會有不同的表現,對于那些處理多線程比較好的機器來說,應該會好一些。但是我的機器是Inter奔四3.0雙核CPU+512DDR內存,這樣都延遲這么厲害,還得多強的CPU才行啊?所以對于數據量比較大的傳輸來說,還是用第三種方法吧。不過這個世界問題是很多的,而且未知的問題比已知的問題多的多,說不定還有什么其他問題存在,歡迎你通過下面的聯系方式和我一起研究。關于java使用javacomm20-win32實踐總結由于這幾天要通過java調用通過串口或并口連接的硬件資源,所以我就要用到和底層的硬件進行通訊。通過RS-232的通訊協議,了解電腦和外設是怎樣進行通訊的。在應用中我們也可以通過JNI來實現(詳情請見/blog/31508),這樣的話,就必須知道更多的知識。由于java已經提供我們一個javacomm20-win32通用的API我們還是實行"拿來主義”吧。我就把整個應用的過程詳細的說一下,希望給需要的人一點幫助。我們經過串口和外設通訊,下面我就以串口為例進行解說。1)我們要準備相應的設備。電腦,外設,通過數據線把他們連接起來。2)檢驗外設到底是用的那個COM口和電腦通訊的.也就是說,他們有沒有真確的連接上。我們可以通過下載串口通訊口測試軟件,我用的是"SuperCommTool.exe"的綠色軟件,進行測試的。這軟件很適應,如果選中的某個COM已經被使用了,它會給你一個相應的提示(端口以被占用)。如果你不知道到底是使用的那個端口,那么你可以通過superCommTool軟件一個一個的試,如果正常的話,那么你可以看到有數據顯示在數據接收窗口。也許,有些主板的串口壞了,那么你就要買一個轉接卡,通過PCI插口轉接。3) 察看外設使用說明書知道外設的相關參數.比如,波特率,數據位,停止位,校驗位,等等。只有正確參數,才能顯示正確的數據。當然,你可以在通訊測試軟件上調試這些參數的。比如:波特率-2400,數據位-8,停止位=2,校驗位=1。4) 準備開發環境。最基本的JDK了,你可以使用自己鐘愛的IDE,幫助你開發。IDE可能自帶了JDK,那么你要把相應的javaComm20-win32放到運行時使用的JDK中。下載JAVAcomm20-win32。5) 了解javaComm20-win32。你必須把win32com.dll復制至Ujava.home/bin下;把perties復制到java.home/lib下;把comm.jar添加到你classPath下。前面兩個都是非常重要的。下面說明用到的幾個類:m.CommPortldentifier通訊端口管理器,CommPortIdentifier是控制訪問到通訊端口的中心類。它包括的方法有:通過驅動決定通訊端口是可用的。打開通訊端口為了I/O操作。決定端口的擁有者。解析端口擁有者的爭奪。管理事件顯示在端口擁有者的中的狀態改變。一個應用程序首先使用CommPortIdentifier中的方法,通過相關的驅動去獲取那些通訊端口是可用的并且選擇一個端口便于開始。然后它使用方法在其它類中想CommPort,ParallelPort和SerialPort通過這個端口進行通訊。m.SerialPort一個RS-232串口通訊端口。SerialPort描述底層的接口到一個串口通訊端口變得有效的通過底層的系統。SerialPort定義最小的必需的功能便于串口通訊端口。m.SerialPortEventListener串行端口事件傳播。m.CommDriver6)代碼的編寫。a.獲取SerialPortsPort對象的兩種方法。1)2)java代碼System.IoadLibrary("win32com");m.CommDriverdriver=null;StringdriverName="m.Win32Driver";SerialPortsPort=(SerialPort)driver.getCommPort("COM4",ommPortIdentifier.PORT_SERIAL);java代碼CommPortIdentifierportId=CommPortIdentifier.getPortldentifier("COM4");SerialPortsPort=(SerialPort)portId.open("shipment",1OOO);以上兩種方法都可以。不過一般都會采用第二種。方法說明我們獲取了對串行端口(COM4),可以和它進行通訊了。設置串行端口通訊參數。java代碼sPort.setSerialPortParams(2400,SerialPort.DATABITS_8,SerialPort.STOPBITS_2,SerialPort.PARITY_NONE);獲取輸入(出)流。java代碼InputStreamis=sPort.getInputStream();〃從夕卜設獲取數據Outputstreamos=sPort.getOutputStream();〃發送命令到外設通過監聽器就可以得到數據了。java代碼//SetnotifyOnDataAvailabletotruetoalloweventdriveninput.sPort.notifyOnDataAvailable(true);3.//SetnotifyOnBreakInterruptoalloweventdrivenbreakhandling.sPort.notifyOnBreakInterrupt(true);6.//Setreceivetimeouttoallowbreakingoutofpollingloopduringinputhandling.sPort.enableReceiveTimeout(30);StringBufferlinkWgt=newStringBuffer();//存放獲取的數據sPort.addEventListener(newSerialPortEventListener(){publicvoidserialEvent(SerialPortEvente){intnewData=0;//Determinetypeofevent.switch(e.getEventType()){//Readdatauntil-1isreturned.If\risreceivedsubstitute
17.//\nforcorrectnewlinehandling.18.caseSerialPortEvent.DATA_AVAILABLE:19.while(newData!=-1){20.try{21.newData=is.read();22.if(newData==-1){23.break;24.}25.if('\r'==(char)newData){26.}else{27.linkWgt.append((char)newData);28.}29.}catch(IOExceptionex){30.System.err.println(ex);31.return;32.}33.}34.35.//AppendreceiveddatatomessageAreaIn.36.37.try{38.System.out.println("linkWgt lllll"+Double.valueOf(linkWgt.toString()));39.40.}catch(Exceptionew){41.ew.printStackTrace();42.}finally{43.try{44.//用完了,記得關閉端口。45.is.close();46.sPort.close();47.}catch(Exceptionc){}}break;//Ifbreakeventappend BREAKRECEIVEDmessage.caseSerialPortEvent.BI:System.out.println("\n---BREAKRECEIVED---\n");TOC\o"1-5"\h\z}}57.});7)常見的異常a.m.NoSuchPortException這個說明你的perties沒有放到正確的位置。如果有什么不正確的地方,歡迎批評指正,謝謝!Java串行端口通訊技術了解串行通訊串行通訊協議有很多種,像RS232,RS485,RS422,甚至現今流行的USB等都是串行通訊協議。而串行通訊技術的應用無處不在。可能大家見的最多就是電腦的串口與Modem的通訊。記得在PC機剛開始在中國流行起來時(大約是在90年代前五年),那時甚至有人用一條串行線進行兩臺電腦之間的數據共享。除了這些,手機,PDA,USB鼠標、鍵盤等等都是以串行通訊的方式與電腦連接。而筆者工作性質的關系,所接觸到的就更多了,像多串口卡,各種種類的具有串口通訊接口的檢測與測量儀器,串口通訊的網絡設備等。雖然串行通訊有很多種,但筆者所知的在整個電子通訊產品方面,以RS232的通訊方式最為多見。雖然USB接口的電子產品也是層出不窮,但了解一下Java在串行通訊方面的技術還有有必要的,說不定有哪位讀者還想用此技術寫一個PDA與電腦之間數據共享的程序呢。本文主要以RS232為主來講解JAVA的串行通訊技術。RS232通訊基礎RS-232-C(又稱EIARS-232-C,以下簡稱RS232)是在1970年由美國電子工業協會
(EIA)聯合貝爾系統、調制解調器廠家及計算機終端生產廠家共同制定的用于串行通訊的標準。RS232是一個全雙工的通訊協議,它可以同時進行數據接收和發送的工作。RS232的端口通常有兩種:9針(DB9)和25針(DB25)。DB9和DB25的常用針腳定義9針串口(DB9)25針串口(DB25)針號功能說明縮寫針號功能說明縮寫1數據載波檢測DCD8數據載波檢測DCD2RXD3RXD接收數據接收數據3發送數據TXD2發送數據TXD4數據終端準備DTR20數據終端準備DTR5信號地GND7信號地GND6數據設備準備好DSR6數據準備好DSR7請求發送RTS4請求發送RTS8清除發送CTS5清除發送CTS9振鈴指示RI22振鈴指示RI常見的邊線方式常見的通訊方式是三線式,這種方式是將兩個RS232設備的發送端(TXD)和接收端(RXD)及接地端(GND)互相連接,也是許多讀者所知道的連接方式:(9針)3(TXD2(TXD)5(GND)3(TXD2(RXD)2(RXD)3(TXD2(TXD)5(GND)3(TXD2(RXD)3(TXD)5(GND)(25針)2(RXD)3(TXD)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)針一一對應就行了。小技巧:如何辨別TXD和RXD端口?搞電子的人手邊應該常備一個電表,用來測測電壓,電阻什么的會很有用。你只要分別測一下RS232端口的2--5或3--5針腳之間的電壓,通常TXD針腳與GND之間會有3~15V左右的負電壓,表示它是TXD針腳。安裝JavaCommunicationsAPISun的J2SE中并沒有直接提供以上提到的任何一種串行通訊協議的開發包,而是以獨立的jar包形式發布在網站上(從這里下載)——即comm.jar,稱之為JavatmCommunicationsAPI,它是J2SE的標準擴展。comm.jar并不是最近才有,早在1998年時,sun就已經發布了這個開發包。comm.jar分別提供了對常用的RS232串行端口和IEEE1284并行端口通訊的支持。目前sun發布的comm.jar只有Windows和Solaris平臺兩個版本,如果你需要Linux平臺下的,可以在/~kevinh/linuxcomm.html找到。在使用comm.jar之前,必須知道如何安裝它。這也是困擾許多初學javaRS232通訊者的一個難題。如果我們電腦上安裝了JDK,它將同時為我們安裝一份JRE(JavaRuntimeEntironment),通常我們運行程序時都是以JRE來運行的。所以以下的安裝適用于JRE。如果你是用JDK來運行程序的,請將相應的改成。下載了comm.jar開發包后,與之一起的還有兩個重要的文件,win32com.dll和m.jar提供了通訊用的javaAPI,而win32com.dll提供了供comm.jar調用的本地驅動接口。而perties是這個驅動的類配置文件。首先將comm.jar復制到\lib\ext目錄。再將win21com.dll復制到你的RS232應用程序運行的目錄,即user.dir。然后將perties復制到\lib目錄。通訊前的準備如果你手頭上沒有現成的提供了標準RS232串口的設備,你可以將自己的電腦模擬成兩臺不同的串口設備。通常電腦主機后面的面板提供了兩個9針的串口,請將這兩個串口的2,3,5腳按前面介紹的方法連接。電子市場都有現成的連接頭賣,請不要買那種封裝的嚴嚴實實的接頭,而要買用螺絲封裝可以拆開的連接頭,這樣可以方便自己根據需要連接各個針腳。CommAPI基礎我無意于在此詳細描述CommAPI每個類和接口的用法,但我會介紹CommAPI的類結構和幾個重要的API用法。所有的commAPI位于m包下面。從CommAPI的javadoc來看,它介紹給我們的只有區區以下13個類或接口:m.CommDm.CommPm.ParallelPm.SerialPm.CommPortIm.CommPortOwnershipListem.ParallelPortEm.SerialPortEventm.ParallelPortEventListener(extendsjava.util.EventListener)m.SerialPortEventListener(extendsjava.util.EventListener)m.NoSuchPortEm.PortlnUseExceptionm.UnsupportedCommOperationException下面講解一下幾個主要類或接口。枚舉出系統所有的RS232端口在開始使用RS232端口通訊之前,我們想知道系統有哪些端口是可用的,以下代碼列出系統中所有可用的RS232端口:Enumerationen=CommPortldentifier.getPortldentifiers();CommPortldentifierportId;while(en.hasMoreElements()){portId=(CommPortIdentifier)en.nextElement();/*如果端口類型是串口,貝y打印出其端口信馭if(portId.getPortType()==CommPortldentifier.PORT_SERIAL){System.out.println(portId.getName());}}在我的電腦上以上程序輸出以下結果:COM1COM2CommPortldentifier類的getPortldentifiers方法可以找到系統所有的串口,每個串口對應一個CommPortldentifier類的實例。打開端口如果你使用端口,必須先打開它。try{CommPortserialPort=portId.open("MyApp",60);/*從端口中讀取數據*/InputStreaminput=serialPort.getInputStream();input.read(...);/*往端口中寫數據*/Outputstreamoutput=serialPort.getOutputStream();output.write(...)}catch(PortInUseExceptionex){■■-}通過CommPortIdentifier的open方法可以返回一個CommPort對象。open方法有兩個參數,第一個是String,通常設置為你的應用程序的名字。第二個參數是時間,即開啟端口超時的毫秒數。當端口被另外的應用程序占用時,將拋出PortlnUseException異常。在這里CommPortIdentifier類和CommPort類有什么區別呢?其實它們兩者是一一對應的關系。CommPortIdentifier主要負責端口的初始化和開啟,以及管理它們的占有權。而CommPort則是跟實際的輸入和輸出功能有關的。通過CommPort的getlnputStream()可以取得端口的輸入流,它是java.io.InputStream接口的一個實例。我們可以用標準的InputStream的操作接口來讀取流中的數據,就像通過FilelnputSteam讀取文件的內容一樣。相應的,CommPort的getOutputStream可以獲得端口的輸出流,這樣就可以往串口輸出數據了。3.關閉端口使用完的端口,必須記得將其關閉,這樣可以讓其它的程序有機會使用它,不然其它程序使用該端口時可能會拋出端口正在使用中的錯誤。很奇怪的是,CommPortIdentifier類只提供了開啟端口的方法,而要關閉端口,則要調用CommPort類的close()方法。通訊方式CommPort的輸入流的讀取方式與文件的輸入流有些不一樣,那就是你可能永遠不知這個InputStream何時結束,除非對方的Outputstream向你發送了一個特定數據表示發送結束,你收到這個特定字符后,再行關閉你的InputStreamo而comm.jar提供了兩種靈活的方式讓你讀取數據。1.輪詢方式(Polling)舉個例子,你同GF相約一起出門去看電影,但你的GF好打扮,這一打扮可能就是半小時甚至一小時以上。這時你就耐不住了,每兩分鐘就催問一次''好了沒?”,如此這樣,直到你的GF說OK了才算完。這個就叫輪詢(Polling)°在程序中,輪詢通常設計成一個封閉的循環,當滿足某個條件時即結束循環。剛才那個例子中,你的GF說“0K了!”,這個就是結束你輪詢的條件。在單線程的程序中,當循環一直執行某項任務而又無法預知它何時結束時,此時你的程序看起來可能就像死機一樣。在VB程序中,這個問題可以用在循環結構中插入一個doEvent語句來解決。而Java中,最好的方式是使用線程,就像以下代碼片斷一樣。publicTestPortextendThread{InputStreaminput=serialPort.getInputStream();StringBufferbuf=newStringBuffer();booleanstopped=false;publicvoidrun(){try{while(!stopped)intch=input.read();if(ch=='q'||ch=='Q'){/*結束讀取,關閉端口…*/stopped=true;}else{buf.append((char)ch);}}catch(InterruptedExceptione){}}}監聽方式(listening)CommAPI支持標準的JavaBean型的事件模型。也就是說,你可以使用類似AddXXXListener這樣的方法為一個串口注冊自己的監聽器,以監聽方式進行數據讀取。如要對端口監聽,你必須先取得CommPortIdentifier類的一個實例,CommPortserialPort=portId.open("MyApp",60);從而取得SerialPort,再調用它的addEventListener方法為它添加監聽器,serialPort.addEventListener(newMyPortListener());SerialPort的監聽器必須繼承于SerialPortEventListener接口。當有任何SerialPort的事件發生時,將自動調用監聽器中的serialEvent方法。SerialEvent有以下幾種類型:BI-通訊中斷CD-載波檢測CTS-清除發送DATA_AVAILABLE-有數據到達DSR-數據設備準備好FE-幀錯誤OE-溢位錯誤OUTPUT_BUFFER_EMPTY-輸出緩沖區已清空PE-奇偶校驗錯RI-振鈴指示下面是一個監聽器的示例,publicvoidMyPortListenerimplementsSerialPortEventListener{這個監聽器只是簡單打印每個發生的事件名稱。而對于大多數應用程序來說,通常關心是DATA_AVAILABLE事件,當數據從外部設備傳送到端口上來時將觸發此事件。此時就可以使用前面提到過的方法,serialPort.getInputStream()來從InputStream中讀取數據了。完整的程序為節省篇幅,本文只提供了一些代碼片斷來幫助讀者來理解CommAPI的用法。你可以從CommAPI的開發包中取得完整的可運行的演示程序。請先下載了commAPI的開發包,解壓之后有一個名為Sample的目錄,里面有幾個演示程序,分別是:BlackBox:ASerialPortBlackBoxapplication.ParallelBlackBox:AParallelPortBlackBoxapplicationSerialDemo:AsimplerSerialPortsampleapplicationSimple:AverysimplecommapplicationNullDriver:Atemplatefordriverwriters.CanbeusedasthestartingpointtowriteadriverfortheCommAPI.porting:AtemplateCommPortIdentifierjavafileforpeopleinterestedinportingtheCommAPItoanewplatform.其中,第1),3),4)是關于rs232通訊的演示程序。而其它的,2)是并行端口的演示程序。5)和6)是開發自己的端口驅動程序的模板程序,有興趣的讀者可以自行研究。publicvoidserialEvent(SerialPortEventevt){switch(evt.getEventType()){caseSerialPortEvent.CTS:System.out.println("CTSeventoccured.");break;caseSerialPortEvent.CD:System.out.println("CDeventoccured.");break;caseSerialPortEvent.BI:System.out.println("BIeventoccured.");break;caseSerialPortEvent.DSR:System.out.println("DSReventoccured.");break;caseSerialPortEvent.FE:System.out.println("FEeventoccured.");break;caseSerialPortEvent.OE:System.out.println("OEeventoccured.");break;caseSerialPortEvent.PE:System.out.println("PEeventoccured.");break;caseSerialPortEvent.RI:System.out.println("rieventoccured.");break;caseSerialPortEvent.OUTPUT_BUFFER_EMPTY:System.out.println("output_buffEr_Emptyeventoccured.");break;caseSerialPortEvent.DATA_AVAILABLE:System.out.println("DATA_AvAILABLEeventoccured.");intch;StringBufferbuf=newStringBuffer();InputStreaminput=serialPort.getInputStreamtry{while((ch=input.read())>0){buf.append((char)ch);}System.out.print(buf);}catch(IOExceptione){}break;}}java串口編程1.SerialBeanSerialBean是本類庫與其他應用程序的接口。該類庫中定義了SerialBean的構造方法以及初始化串口,從串口讀取數據,往串口寫入數據以及關閉串口的函數。具體介紹如下:publicSerialBean(intPortID)本函數構造一個指向特定串口的SerialBean,該串口由參數PortID所指定。PortID=1表示C0M1,PortID=2表示COM2,由此類推。publicintInitialize()本函數初始化所指定的串口并返回初始化結果。如果初始化成功返回1,否則返回-1。初始化的結果是該串口被SerialBean獨占性使用,其參數被設置為9600,N,8,1。如果串口被成功初始化,則打開一個進程讀取從串口傳入的數據并將其保存在緩沖區中。publicStringReadPort(intLength)本函數從串口(緩沖區)中讀取指定長度的一個字符串。參數Length指定所返回字符串的長度。publicvoidWritePort(StringMsg)本函數向串口發送一個字符串。參數Msg是需要發送的字符串。publicvoidClosePort()本函數停止串口檢測進程并關閉串口。packageserial;importjava.io.*;importjava.util.*;importm.*;/***Thisbeanprovidessomebasicfunctionstoimplementfulldulplexinformationexchangethroughthesrialport.**/publicclassSerialBean{staticStringPortName;CommPortIdentifierportId;SerialPortserialPort;staticOutputStreamout;staticInputStreamin;SerialBufferSB;ReadSerialRT;/****Constructor**@paramPortIDtheIDoftheserialtobeused.1forCOM1,*2forCOM2,etc.**/publicSerialBean(intPortID){PortName="COM"+PortID;}/****Thisfunctioninitializetheserialportforcommunication.Itstartssa*threadwhichconsistentlymonitorstheserialport.Anysignalcapturred*fromtheserialportisstoredintoabufferarea.**/publicintInitialize(){intInitSuccess=1;intInitFail=-1;tryportId=CommPortIdentifier.getPortIdentifier(PortName);try{serialPort=(SerialPort)portId.open("Serial_Communication",2000);}catch(PortInUseExceptione){returnInitFail;}//UseInputStreamintoreadfromtheserialport,andOutputStream//outtowritetotheserialport.try{in=serialPort.getInputStream();out=serialPort.getOutputStream();}catch(IOExceptione){returnInitFail;}//Initializethecommunicationparametersto9600,8,1,none.try{serialPort.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);}catch(UnsupportedCommOperationExceptione){returnInitFail;}}catch(NoSuchPortExceptione)returnInitFail;}//whensuccessfullyopentheserialport,createanewserialbuffer,//thencreateathreadthatconsistentlyacceptsincomingsignalsfrom//theserialport.Incomingsignalsarestoredintheserialbuffer.SB=newSerialBuffer();RT=newReadSerial(SB,in);RT.start();//returnsuccessinformationreturnInitSuccess;}/****Thisfunctionreturnsastringwithacertainlengthfromtheincomin*messages.**@paramLengthThelengthofthestringtobereturned.*/publicStringReadPort(intLength){StringMsg;Msg=SB.GetMsg(Length);returnMsg;}/****Thisfunctionsendsamessagethroughtheserialport.*@paramMsgThestringtobesent.**/publicvoidWritePort(StringMsg){intc;try{for(inti=0;i<Msg.length();i++)out.write(Msg.charAt(i));}catch(IOExceptione){}}/****Thisfunctionclosestheserialportinuse.**/publicvoidClosePort(){RT.stop();serialPort.close();}}SerialBufferSerialBuffer是本類庫中所定義的串口緩沖區,它定義了往該緩沖區中寫入數據和從該緩沖區中讀取數據所需要的函數。publicsynchronizedStringGetMsg(intLength)本函數從串口(緩沖區)中讀取指定長度的一個字符串。參數Length指定所返回字符串的長度。publicsynchronizedvoidPutChar(intc)本函數望串口緩沖區中寫入一個字符,參數c是需要寫入的字符。在往緩沖區寫入數據或者是從緩沖區讀取數據的時候,必須保證數據的同步,因此GetMsg和PutChar函數均被聲明為synchronized并在具體實現中采取措施實現的數據的同步。packageserial;/***Thisclassimplementsthebufferareatostoreincomingdatafromtheserialport.**/publicclassSerialBuffer{privateStringContent="";privateStringCurrentMsg,TempContent;privatebooleanavailable=false;privateintLengthNeeded=1;/***Thisfunctionreturnsastringwithacertainlengthfromtheincominmessages.*@paramLengthThelengthofthestringtobereturned.**/publicsynchronizedStringGetMsg(intLength)LengthNeeded=Length;notifyAll();if(LengthNeeded>Content.length()){available=false;while(available==false){try{wait();}catch(InterruptedExceptione){}}}CurrentMsg=Content.substring(0,LengthNeeded);TempContent=Content.substring(LengthNeeded);Content=TempContent;LengthNeeded=1;notifyAll();returnCurrentMsg;}/****Thisfunctionstoresacharactercapturedfromtheserialporttothe*bufferarea.**@paramtThecharvalueofthecharactertobestored.**/publicsynchronizedvoidPutChar(intc)Characterd=newCharacter((char)c);Content=Content.concat(d.toString());if(LengthNeeded<Content.length()){available=true;}notifyAll();}}ReadSerialReadSerial是一個進程,它不斷的從指定的串口讀取數據并將其存放到緩沖區中。publicReadSerial(SerialBufferSB,InputStreamPort)本函數構造一個ReadSerial進程,參數SB指定存放傳入數據的緩沖區,參數Port指定從串口所接收的數據流。publicvoidrun()ReadSerial進程的主函數,它不斷的從指定的串口讀取數據并將其存放到緩沖區中。packageserial;importjava.io.*;/***Thisclassreadsmessagefromthespecificserialportandsavethemessagetotheserialbuffer.**/publicclassReadSerialextendsThread{privateSerialBufferComBuffer;privateInputStreamComPort;*Constructor**@paramSBThebuffertosavetheincomingmessages.*@paramPortTheInputStreamfromthespecificserialport.**/publicReadSerial(SerialBufferSB,InputStreamPort){ComBuffer=SB;ComPort=Port;}publicvoidrun(){intc;try{while(true){c=ComPort.read();ComBuffer.PutChar(c);}}catch(IOExceptione){}}}SerialExampleSerialExample是本類庫所提供的一個例程。它所實現的功能是打開串口C0M1,對其進行初始化,從串口讀取信息對其進行處理后將處理結果發送到串口。importserial.*;importjava.io.*;/***ThisisanexampleofhowtousetheSerialBean.ItopensCOM1andreads*sixmessageswithdifferentlengthformtheserialport.**/classSerialExample{publicstaticvoidmain(String[]args){//TODO:AddyourJAVAcodeshereSerialBeanSB=newSerialBean(1);StringMsg;SB.Initialize();for(inti=5;i<=10;i++){Msg=SB.ReadPort(i);SB.WritePort("Reply:"+Msg);}SB.ClosePort();}}本類庫中使用了JavaCommunicationAPI(m)。這是一個Java擴展類庫,并不包括在標準的JavaSDK當中。如果你尚未安裝這個擴展類庫的話,你應該從Sun公司的Java站點下載這個類庫并將其安裝在你的系統上。在所下載的包里面包括一個安裝說明,如果你沒有正確安裝這個類庫及其運行環境的話,運行這個程序的時候你會找不到串口。正確安裝JavaCommunicationAPI并將上述程序編譯通過以后,你可以按如下方法測試這個程序。如果你只有一臺機器,你可以利用一條RS-232電纜將COM1和COM2連接起來,在COM1上運行SerialExample,在COM2上運行Windows提供的超級終端程序。如果你有兩臺機器的話,你可以利用一條RS-232電纜將兩臺機器的C0M1(或者是COM2)連接起來,在一端運行例程,另外一端運行Windows提供的超級終端程序。如果有必要的話,可以對SerialExample中所聲明的串口進行相應改動。本程序在Windows2000+JavaSDK1.3環境下編譯通過并成功運行。java串口通信2007-05-1111:50/*******************************************程序文件名稱:SendComm.java*功能:從串行口COM1中發送數據******************************************/importjava.awt.*;importjava.awt.event.*;importjava.io.*;importjava.util.*;importm.*;classS_FrameextendsFrameimplementsRunnable,ActionListener{/*檢測系統中可用的通訊端口類*/staticCommPortIdentifierportId;/*Enumeration為枚舉型類,在util中*/staticEnumerationportList;OutputStreamoutputStream;/*RS-232的串行口*/SerialPortserialPort;ThreadreadThread;Panelp=newPanel();TextFieldin_message二newTextField("打開COM1,波特率9600,數據位&停止位1.");TextAreaout_message=newTextArea();ButtonbtnOpen二newButton(“打開串口,發送數據");ButtonbtnClose二newButton(“關閉串口,停止發送數據");bytedata[]=newbyte[10240];/*設置判斷要是否關閉串口的標志*/booleanmark;/*安排窗體*/S_Frame(){super("串口發送數據");setSize(200,200);setVisible(true);add(out_message,"Center");add(p,"North");p.add(btnOpen);p.add(btnClose);add(in_message,"South");btnOpen.addActionListener(this);btnClose.addActionListener(this);}//R_Frame()end/*點擊按扭打開串口.*/publicvoidactionPerformed(ActionEventevent){if(event.getSource()==btnClose){serialPort.close();//關閉串口mark二true;//用于中止線程的run()方法in_message.setText("串口COM1已經關閉,停止發送數據.");}else{mark=false;/*從文本區按字節讀取數據*/data=out_message.getText().getBytes();/*打開串口*/start();in_message.setText(“串口COM1已經打開,正在每2秒鐘發送一次數據 ");}}//actionPerformed()end/*打開串口,并調用線程發送數據*/publicvoidstart(){/*獲取系統中所有的通訊端口*/portList=CommPortIdentifier.getPortIdentifiers();/*用循環結構找出串口*/while(portList.hasMoreElements()){/*強制轉換為通訊端口類型*/portId=(CommPortIdentifier)portList.nextElement();if(portId.getPortType()==CommPortIdentifier.PORT_SERIAL){if(portId.getName().equals("COM1")){/*打開串口*/try{serialPort=(SerialPort)portId.open("ReadComm",2000);}catch(PortInUseExceptione){}/*設置串口輸出流*/try{outputStream=serialPort.getOutputStream();}catch(IOExceptione){}}//ifend}//ifend}//whileend/*調用線程發送數據*/try{readThread=newThread(this);//線程負責每發送一次數據,休眠2秒鐘readThread.start();}catch(Exceptione){}}//start()end/*發送數據,休眠2秒鐘后重發*/publicvoidrun(){/*設置串口通訊參數*/try{serialPort.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);}catch(UnsupportedCommOperationExceptione){}/*發送數據流(將數組data[]中的數據發送出去)*/try{outputStream.write(data);}catch(IOExceptione){}/*發送數據后休眠2秒鐘,然后再重發*/try{Thread.sleep(2000);if(mark){return;//結束run方法,導致線程死亡}start();}catch(InterruptedExce
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 通過案例學習網絡規劃設計師考試試題及答案
- 高空拋物考試題及答案
- 四川省達州市本年度(2025)小學一年級數學部編版能力評測(上學期)試卷及答案
- Aildenafil-Methisosildenafil-生命科學試劑-MCE
- 四川省綿陽市本年度(2025)小學一年級數學統編版專題練習((上下)學期)試卷及答案
- 營養咨詢的基本原則試題及答案
- 江西省萍鄉市本年度(2025)小學一年級數學統編版期末考試(上學期)試卷及答案
- 食堂從業人員試題及答案
- 西醫臨床學習策略試題及答案
- 綜合管廊建設項目可行性分析報告
- 藥事管理法律法規相關知識培訓
- 地毯織造技藝(北京宮毯織造技藝)
- 第4章-選區激光熔化工藝及材料課件
- GB/T 3785.1-2023電聲學聲級計第1部分:規范
- 2023屆高考寫作指導:“尋找溫暖”與“成為燈火”課件
- 2022年上海市工業技術學校招聘考試真題
- 長期護理保險技能比賽理論試題庫300題(含各題型)
- 二重積分的概念與性質演示文稿
- 醫院雙重預防機制建設工作完成情況
- 大學生勞動教育通論知到章節答案智慧樹2023年大連海洋大學
- 2003高教社杯全國大學生數學建模競賽B題競賽參考答案
評論
0/150
提交評論