天天看點

Java中基于Rxtx的序列槽操作

歡迎大家通路我的部落格:位址

感謝:java序列槽通信

一個嵌入式系統通常需要通過序列槽與其主要系統進行全雙工通訊,譬如一個流水線控制系統需要不斷的接受從主要系統發送來的查詢和控制資訊,并将執行結果或查詢結果發送回主要系統。

本文是基于RXTX(提供序列槽和并口通信)開源類庫對序列槽進行操作的。

使用準備(windows,rxtx-2.2)

1.将RXTXcomm.jar放到%jre_home%\lib\ext\下,如:D:\Java\jdk1.6.0_06\jre\lib\ext,或者項目1.右鍵->2.Preperties(首選項)->3.Java Build Path->4.Libraries->5.Add External JARs引入

2.把 rxtxSerial.dll放入到java.library.path中,如:D:\Java\jdk1.6.0_06\jre\bin,或C:\windows\system32

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類型的輸入流

序列槽API執行個體

public class ListPort {
	/**
	 * @Description:列出所有可用序列槽
	 * @author:Lu
	 * @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:Lu
	 * @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:Lu
	 * @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();
	}
}
           

測試代碼  

/**
 * 序列槽參數的配置 序列槽一般有如下參數可以在該序列槽打開以前進行配置:
 * 包括波特率,輸入/輸出流控制,資料位數,停止位和奇偶校驗。
 */
// 注:序列槽操作類一定要繼承SerialPortEventListener 
public class SerialPortTest1 implements 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) {
				// 比較序列槽名稱是否是"COM1"
				if ("COM1".equals(portId.getName())) {
					System.out.println("找到序列槽COM1");
					// 打開序列槽
					try {
						// open:(應用程式名【随意命名】,阻塞時等待的毫秒數)
						serialPort = (SerialPort) portId.open(Object.class.getSimpleName(), 2000);
						System.out.println("擷取到序列槽對象,COM1");
						// 設定序列槽監聽
						serialPort.addEventListener(this);
						// 設定序列槽資料時間有效(可監聽)
						serialPort.notifyOnDataAvailable(true);
						// 設定序列槽通訊參數 
						// 波特率,資料位,停止位和校驗方式
						// 波特率2400,偶校驗 
						serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,//
								SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
						test = "";
						outputStream = serialPort.getOutputStream();

					} 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;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 關閉序列槽
	public void closeSerialPort() {
		if (serialPort != null) {
			serialPort.notifyOnDataAvailable(false);
			serialPort.removeEventListener();
			if (inputStream != null) {
				try {
					inputStream.close();
					inputStream = null;
				}
				catch (IOException e) {}
			}
			if (outputStream != null) {
				try {
					outputStream.close();
					outputStream = null;
				}
				catch (IOException e) {}
			}
			serialPort.close();
			serialPort = null;
		}
	}

	public static void main(String[] args) {
		SerialPortTest1 sp = new SerialPortTest1();
		sp.init();
		System.out.println("輸出" + sp.test);
		sp.closeSerialPort();
	}
}
           

之後,用虛拟序列槽工具VSPD建立兩個序列槽,COM1和COM2,運作java程式,打開序列槽調試助手,從COM2向COM1發送資料,java程式就可以在控制台輸出資訊了。