Java串口編程

最近因爲項目的須要,須要用到java串口和windows端java程序的通信,筆者也是剛剛接觸串口這一模塊,在網上搜索了不少的串口編程實例之類的,幾乎前篇一概吧,可是串口通信以前的配置是很是重要的,若是配置沒有成功,編程也顯得沒有意義。串口編程主要有兩種接口,第一種是利用sun提供的comm.jar包,這種方式比較古老了,這個包也沒有更新。第二種就是RXTX模式,這種模式其實和comm.jar包的模式幾乎是同樣的。下面就記下我學習和使用此模塊的記錄。RXTX資源包,網上有不少,但要注意的是看你的電腦配置,若是是win64位系統的,則要下載RXTX 64位的資源包,不然會出錯(這裏最主要的區別就是rxtxSerial.dll文件不同)。前端

(1)編程環境的搭建和配置java

本文是基於RXTX(提供串口和並口通訊)開源類庫對串口進行操做的。使用準備(這裏的JAVA_HOME是個人jdk的安裝路徑)編程

1.將RXTXcomm.jar放到%JAVA_HOME%\jre\lib\ext\下,如:E:\jdk\Java1.8\jdk1.8.0_45\jre\lib\ext,或者項目1.右鍵->2.Preperties(首選項)->3.Java Build Path->4.Libraries->5.Add External JARs引入(建議使用後面一種,前面一種有時候在項目中讀取不出來,在項目打包的過程當中若是沒有將jre打包進去的話,項目移植到別的設備上時候是不會成功的)windows

2.把 rxtxSerial.dll放入到%JAVA_HOME%\jre\bin中,如:E:\jdk\Java1.8\jdk1.8.0_45\jre\bin,或C:\windows\system32ide

(2)RXTX介紹學習

RXTX是一個提供串口和並口通訊的開源java類庫,由該項目發佈的文件均遵循LGPL協議。測試

RXTX項目提供了Windows,Linux,Mac os X,Solaris操做系統下的兼容javax.comm串口通信包API的實現,爲其餘開發人員在此類系統下開發串口應用提供了至關的方便。ui

RXTX的使用上與sun提供的comm.jar基本相同,編程時最明顯的不一樣是要包含的包名由javax.comm.*改爲了gnu.io.*this

RxtxAPI 的核心是抽象的CommPort類(用於描述一個被底層系統支持的端口的抽象類,它包含一些高層的IO控制方法,這些方法對於全部不一樣的通信端口來講是通用的)及其兩個子類:SerialPort類和ParallePort類。其中,SerialPort類是用於串口通訊的類,ParallePort類是用於並行口通訊的類。CommPort類還提供了常規的通訊模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,專用於與端口上的設備進行通訊。操作系統

然而,這些類的構造方法都被有意的設置爲非公有的(non-public)。因此,不能直接構造對象,而是先經過靜態的CommPortIdentifer.getPortIdentifiers()得到端口列表,再從這個端口列表中選擇所須要的端口,並調用CommPortIdentifer對象的Open( )方法,這樣,就能獲得一個CommPort對象。固然,還要將這個CommPort對象的類型轉換爲某個非抽象的子類,代表是特定的通信設備,該子類能夠是SerialPort類和ParallePort類中的一個。下面將分別對CommPortIdentifier類,串口類SerialPort進行詳細的介紹。

接口

CommDriver可負載設備(the loadable device)驅動程序接口的一部分

CommPortOwnershipListener傳遞各類通信端口的全部權事件

ParallelPortEventListener傳遞並行端口事件

SerialPortEventListener傳遞串行端口事件

CommPort通信端口

CommPortIdentifier通信端口管理

ParallelPort並行通信端口

ParallelPortEvent並行端口事件

SerialPortRS-232串行通信端口

SerialPortEvent 串行端口事件

異常類

NoSuchPortException當驅動程序不能找到指定端口時拋出

PortInUseException當碰到指定端口正在使用中時拋出

UnsupportedCommOperationException驅動程序不容許指定操做時拋出

CommPortIdentifier類

這個類主要用於對通訊端口進行管理和設置,是對端口進行訪問控制的核心類,主要包括如下方法:

addPortName(String,int, CommDriver) 添加端口名到端口列表裏

addPortOwnershipListener(CommPortOwnershipListener)添加端口擁有的監聽器

removePortOwnershipListener(CommPortOwnershipListener)移除端口擁有的監聽器

getCurrentOwner()獲取當前佔有端口的對象或應用程序

getName()獲取端口名稱

getPortIdentifier(CommPort)獲取指定打開的端口的CommPortIdentifier類型對象

getPortIdentifier(String)獲取以參數命名的端口的CommPortIdentifier類型對象

getPortIdentifiers()獲取系統中的端口列表

getPortType()獲取端口的類型

isCurrentlyOwned()判斷當前端口是否被佔用

open(FileDescriptor)用文件描述的類型打開端口

open(String,int) 打開端口,兩個參數:程序名稱,延遲時間(毫秒數)

SerialPort類

這個類用於描述一個RS-232串行通訊端口的底層接口,它定義了串口通訊所需的最小功能集。經過它,用戶能夠直接對串口進行讀、寫及設置工做。

SerialPort類中關於串口參數的靜態成員變量說明:

DATABITS_5 數據位爲5

DATABITS_6 數據位爲6

DATABITS_7 數據位爲7

DATABITS_8 數據位爲8

PARITY_NONE 空格檢驗

PARITY_ODD 奇檢驗

PARITY_EVEN 偶檢驗

PARITY_MARK 標記檢驗

PARITY_SPACE 無檢驗

STOPBITS_1 中止位爲1

STOPBITS_2 中止位爲2

STOPBITS_1_5 中止位爲1.5

 

SerialPort類中關於串口參數的方法說明:

getBaudRate()獲得波特率

getParity()獲得檢驗類型

getDataBits()獲得數據位數

getStopBits()獲得中止位數

setSerialPortParams(int,int, int, int) 設置串口參數依次爲(波特率,數據位,中止位,奇偶檢驗)

 

SerialPort類中關於事件的靜態成員變量說明:

BI Break interrupt 通信中斷

FE Framing error 幀錯誤

CD Carrier detect 載波偵聽

OE Overrun error 溢位錯誤

CTS Clear to send 清除發送

PE Parity error 奇偶檢驗錯誤

DSR Data set ready 數據設備準備好

RI Ring indicator 響鈴偵測

DATA_AVAILABLE 串口中的可用數據

OUTPUT_BUFFER_EMPTY 輸出緩衝區已清空

 

SerialPort類中關於事件的方法說明:

isCD()是否有載波

isCTS()是否清除以傳送

isDSR()數據是否備妥

isDTR()是否數據端備妥

isRI()是否響鈴偵測

isRTS()是否要求傳送

addEventListener(SerialPortEventListener)向SerialPort對象中添加串口事件監聽器

removeEventListener()移除SerialPort對象中的串口事件監聽器

notifyOnBreakInterrupt(boolean)設置中斷事件true有效,false無效

notifyOnCarrierDetect(boolean)設置載波監聽事件true有效,false無效

notifyOnCTS(boolean)設置清除發送事件true有效,false無效

notifyOnDataAvailable(boolean)設置串口有數據的事件true有效,false無效

notifyOnDSR(boolean)設置數據備妥事件true有效,false無效

notifyOnFramingError(boolean)設置發生錯誤事件true有效,false無效

notifyOnOutputEmpty(boolean)設置發送緩衝區爲空事件true有效,false無效

notifyOnParityError(boolean)設置發生奇偶檢驗錯誤事件true有效,false無效

notifyOnRingIndicator(boolean)設置響鈴偵測事件true有效,false無效

getEventType()獲得發生的事件類型返回值爲int型

sendBreak(int)設置中斷過程的時間,參數爲毫秒值

setRTS(boolean)設置或清除RTS位

setDTR(boolean)設置或清除DTR位

 

SerialPort中的其餘經常使用方法說明:

close()關閉串口

getOutputStream()獲得OutputStream類型的輸出流

getInputStream()獲得InputStream類型的輸入流

(3)列出串口以及其它通訊端口類以及方法,下面貼出demo

import java.util.Enumeration;
import java.util.HashSet;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;

public class ListPort {
	/**
	 * @Description:列出全部可用串口
	 * @author:dengchaoqun
	 * @date:2015-8-29 上午11:34:04
	 */
	public static void listPorts() {
		HashSet<CommPortIdentifier> portSet = getAvailableSerialPorts();
		for (CommPortIdentifier comm : portSet) {
			System.out.println(comm.getName() + " - " + getPortTypeName(comm.getPortType()));
		}
	}

	/**
	 * @Description:列出全部通訊端口
	 * @author:dengchaqun
	 * @date:2015-8-29 下午2:06:17
	 */
	@SuppressWarnings("unchecked")
	public static void listCommPorts() {
		CommPortIdentifier.getPortIdentifiers();
		/*
		 * 不帶參數的getPortIdentifiers方法能夠得到一個枚舉對象,該對象包含了
		 * 系統中每一個端口的CommPortIdentifier對象。注意這裏的端口不只僅是指串口,也包括並口。
		 * 這個方法還能夠帶參數,getPortIdentifiers(CommPort)得到已經被應用程序打開的端口
		 * 相對應的CommPortIdentifier對象。getPortIdentifier(String portName)
		 * 獲取指定端口名(好比「COM1」)的CommPortIdentifier對象。
		 */
		java.util.Enumeration<CommPortIdentifier> portEnum = CommPortIdentifier.getPortIdentifiers();
		while (portEnum.hasMoreElements()) {
			CommPortIdentifier portIdentifier = portEnum.nextElement();
			System.out.println(portIdentifier.getName() + " - " + getPortTypeName(portIdentifier.getPortType()));
		}
	}

	/**
	 * @Description:獲取通訊端口類型名稱
	 * @author:Lu
	 * @date:2015-8-29 上午11:35:32
	 */
	public static String getPortTypeName(int portType) {
		switch (portType) {
		case CommPortIdentifier.PORT_I2C:
			return "I2C";
		case CommPortIdentifier.PORT_PARALLEL: // 並口
			return "Parallel";
		case CommPortIdentifier.PORT_RAW:
			return "Raw";
		case CommPortIdentifier.PORT_RS485: // RS485端口
			return "RS485";
		case CommPortIdentifier.PORT_SERIAL: // 串口
			return "Serial";
		default:
			return "unknown type";
		}
	}

	/**
	 * @Description:獲取全部可用的串口集合
	 * @author:dengchaoqun
	 * @date:2015-8-29 上午11:37:54
	 */
	@SuppressWarnings("unchecked")
	public static HashSet<CommPortIdentifier> getAvailableSerialPorts() {
		HashSet<CommPortIdentifier> h = new HashSet<CommPortIdentifier>();
		Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
		while (portList.hasMoreElements()) {
			CommPortIdentifier com = (CommPortIdentifier) portList.nextElement();
			switch (com.getPortType()) {
			case CommPortIdentifier.PORT_SERIAL:
				try {
					// open:(應用程序名【隨意命名】,阻塞時等待的毫秒數)
					/*
					 * open方法打開通信端口,得到一個CommPort對象,它使程序獨佔端口。
					 * 若是端口正被其餘應用程序佔用,將使用CommPortOwnershipListener事件機制
					 * 傳遞一個PORT_OWNERSHIP_REQUESTED事件。
					 * 每一個端口都關聯一個InputStream和一個OutputStream,若是端口是用
					 * open方法打開的,那麼任何的getInputStream都將返回相同的數據流對象,除非 有close被調用。
					 */
					CommPort thePort = com.open(Object.class.getSimpleName(), 50);
					thePort.close();
					h.add(com);
				} catch (PortInUseException e) {
					// 不可用串口
					System.out.println("Port, " + com.getName() + ", is in use.");
				} catch (Exception e) {
					System.err.println("Failed to open port " + com.getName());
					e.printStackTrace();
				}
			}
		}
		return h;
	}

	public static void main(String[] args) {
		/**
		 * 可列出當前系統全部可用的串口名稱,本機輸出COM1 - Serial, COM2 - Serial.....COM6 - Serial
		 */
		listPorts();
		listCommPorts();
	}
}

  

(4)調試串口輸入輸出demo

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.TooManyListenersException;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

public class SerialPortTest1 implements Runnable, SerialPortEventListener {
	// 檢測系統中可用的通信端口類
	private CommPortIdentifier portId;
	// 枚舉類型
	private Enumeration<CommPortIdentifier> portList;

	// RS232串口
	private SerialPort serialPort;

	// 輸入輸出流
	private InputStream inputStream;
	private OutputStream outputStream;

	// 保存串口返回信息
	private String test = "";

	// 單例建立
	private static SerialPortTest1 uniqueInstance = new SerialPortTest1();

	// 初始化串口
	@SuppressWarnings("unchecked")
	public void init() {
		// 獲取系統中全部的通信端口
		portList = CommPortIdentifier.getPortIdentifiers();
		// 循環通信端口
		while (portList.hasMoreElements()) {
			portId = portList.nextElement();
			// 判斷是不是串口
			if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				// 比較串口名稱是不是"COM3"
				if ("COM2".equals(portId.getName())) {
					System.out.println("找到串口COM2");
					// 打開串口
					try {
						// open:(應用程序名【隨意命名】,阻塞時等待的毫秒數)
						serialPort = (SerialPort) portId.open(Object.class.getSimpleName(), 2000);
						System.out.println("獲取到串口對象,COM2");
						//實例化輸入流
						inputStream = serialPort.getInputStream();
						// 設置串口監聽
						serialPort.addEventListener(this);
						// 設置串口數據時間有效(可監聽)
						serialPort.notifyOnDataAvailable(true);
						// 設置串口通信參數
						// 波特率,數據位,中止位和校驗方式
						// 波特率2400,偶校驗
						serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, //
								SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

					} catch (PortInUseException e) {
						e.printStackTrace();
					} catch (TooManyListenersException e) {
						e.printStackTrace();
					} catch (UnsupportedCommOperationException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}

				}
			}
		}
	}

	// 實現接口SerialPortEventListener中的方法 讀取從串口中接收的數據
	@Override
	public void serialEvent(SerialPortEvent event) {
		switch (event.getEventType()) {
		case SerialPortEvent.BI: // 通信中斷
		case SerialPortEvent.OE: // 溢位錯誤
		case SerialPortEvent.FE: // 幀錯誤
		case SerialPortEvent.PE: // 奇偶校驗錯誤
		case SerialPortEvent.CD: // 載波檢測
		case SerialPortEvent.CTS: // 清除發送
		case SerialPortEvent.DSR: // 數據設備準備好
		case SerialPortEvent.RI: // 響鈴偵測
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 輸出緩衝區已清空
			break;
		case SerialPortEvent.DATA_AVAILABLE: // 有數據到達
			readComm();
			break;
		default:
			break;
		}
	}

	// 讀取串口返回信息
	public void readComm() {
		byte[] readBuffer = new byte[1024];
		try {
			inputStream = serialPort.getInputStream();
			// 從線路上讀取數據流
			int len = 0;
			while ((len = inputStream.read(readBuffer)) != -1) {
				System.out.println("實時反饋:" + new String(readBuffer, 0, len).trim() + new Date());
				test += new String(readBuffer, 0, len).trim();
				break;
			}
			System.out.println(test + " ");
			//closeSerialPort();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void closeSerialPort() {
		uniqueInstance.serialPort.close();
	}
	
	//向串口發送數據
	public void sendMsg(){
		String information = "AT\r";
		try {
			//實例化輸出流
			outputStream = serialPort.getOutputStream();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		try {
			outputStream.write(information.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		init();
		sendMsg();
		
	}
}

(5)測試上面輸入輸出demo

public class TestDemo {

	public static void main(String[] args) {

		Thread thread=new Thread(new SerialPortTest1());
		thread.start();
	}

}

串口測試的效果如圖所示

(6)後面的就是將此demo應用到項目中去,實際狀況,根據本身的需求來定

相關文章
相關標籤/搜索