Java串口編程


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

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

本文是基於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打包進去的話,項目移植到別的設備上時候是不會成功的)

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

(2)RXTX介紹

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

RXTX項目提供了Windows,Linux,Mac os X,Solaris操作系統下的兼容javax.comm串口通訊包API的實現,為其他開發人員在此類系統下開發串口應用提供了相當的方便。

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

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應用到項目中去,實際情況,根據自己的需求來定


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM