参考链接https://www.cnblogs.com/Dreamer-1/p/5523046.html
在http://fizzed.com/oss/rxtx-for-java下载RXTX包。
根据RXTX包里的说明文档,将RXTXcomm.jar包放在JRE安装目录下的lib\ext下。
rxtxSerial.dll放在JRE安装目录的bin下。
在项目根目录下放rxtxSerial.dll。
JAR包可放在项目根目录下的任意方便的位置,在IJ里找到JAR包选择Add as Library即可。
本部分代码使用了https://www.cnblogs.com/Dreamer-1/p/5523046.html提供的SerialTool代码,并在其上进行了一定的修改。
注意:
RXTX包在JDK10.0.1上使用会出现
EXCEPTION_ACCESS_VIOLATION (0xc0000005)
。 JDK10.0.1版本信息如下:
D:\Program Files\Java\jdk-10.0.1\bin λ java.exe -version java version "10.0.1" 2018-04-17 Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
请使用JDK1.18进行编译。
可用的JDK版本:
D:\Program Files\Java\jdk1.8.0_161\bin λ java.exe -version java version "1.8.0_161" Java(TM) SE Runtime Environment (build 1.8.0_161-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
使用Double Check的懒汉式单例模式来保证线程安全,同时在不使用该类的时候不创建类对象,减少内存消耗。
public class SerialTool {
private static volatile SerialTool serialTool = null;
//私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
private SerialTool() {}
/**
* 获取提供服务的SerialTool对象
* @return serialTool
*/
public static SerialTool getInstance() {
if (serialTool == null) {
synchronized (SerialTool.class) { //使用同步锁进行双重确认,防止生成两个实例
if(serialTool == null) {
serialTool = new SerialTool();
}
}
}
return serialTool;
}
//...
}
备注:
经查看源代码后,发现本类的所有方法都是static方法,直接调用类名+方法名的方法即可实现调用。
使用静态方法CommPortIdentifier.getPortIdentifiers()
,可获得一个CommPortIdentifier
对象的枚举(Enumeration
)。
该枚举可直接用hasMoreElements()
方法和nextElement()
进行遍历。
在JDK10中,也可使用Enumeration类的
asIterator()
方法转换Enumeration为Iterator,使用hasNext()
方法和next()
方法进行遍历。
实际使用代码范例,返回存放可用串口的CommPortIdentifier对象的ArrayList:
/**
* Clair Modified.
* 查找所有可用端口,以CommPortIdentifier对象的ArrayList返回
* @return 可用端口的ArrayList
*/
public static final ArrayList getPortList() {
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
ArrayList portArrayList = new ArrayList<>();
while(portList.hasMoreElements()) {
CommPortIdentifier cpi = portList.nextElement();
portArrayList.add(cpi);
}
return portArrayList;
}
步骤如下:
open(String portName, int timeOut)
方法打开串口,得到CommPort对象。修改后的打开串口方法如下:
PortConfig类,用于存放串口设置:
public class PortConfig {
public int baudRate;
public int dataBit;
public int stopBit;
public int parity;
}
openPort方法,用于打开串口:
public static final SerialPort openPort (CommPortIdentifier cpi,PortConfig pc) throws NotASerialPort, PortInUse, SerialPortParameterFailure {
try {
CommPort commPort = cpi.open(cpi.getName(),2000);
if(commPort instanceof SerialPort) {
try {
((SerialPort) commPort).
setSerialPortParams(pc.baudRate,pc.dataBit,pc.stopBit,pc.parity);
return (SerialPort) commPort;
} catch (UnsupportedCommOperationException e) {
throw new SerialPortParameterFailure();
}
} else {
throw new NotASerialPort();
}
} catch (PortInUseException e) {
throw new PortInUse();
}
}
调用方法:
ArrayList comlist = SerialTool.getInstance().getPortList();
//...
//串口参数
PortConfig sercfg = new PortConfig();
sercfg.baudRate = 115200;
sercfg.dataBit = SerialPort.DATABITS_8;
sercfg.parity = SerialPort.PARITY_NONE;
sercfg.stopBit = SerialPort.STOPBITS_1;
//打开串口
SerialPort commPort = null;
try {
commPort = SerialTool.openPort(comlist.get(inv),sercfg); //打开和设置串口
} catch (NotASerialPort | PortInUse | SerialPortParameterFailure notASerialPort) {
notASerialPort.printStackTrace();
}
if(commPort == null) {
System.out.println("Err.");
return;
}
System.out.println("OK.");
串口参数的相关定义在SerialPort类里:
public static final int DATABITS_5 = 5;
public static final int DATABITS_6 = 6;
public static final int DATABITS_7 = 7;
public static final int DATABITS_8 = 8;
public static final int PARITY_NONE = 0;
public static final int PARITY_ODD = 1;
public static final int PARITY_EVEN = 2;
public static final int PARITY_MARK = 3;
public static final int PARITY_SPACE = 4;
public static final int STOPBITS_1 = 1;
public static final int STOPBITS_2 = 2;
public static final int STOPBITS_1_5 = 3;
public static final int FLOWCONTROL_NONE = 0;
public static final int FLOWCONTROL_RTSCTS_IN = 1;
public static final int FLOWCONTROL_RTSCTS_OUT = 2;
public static final int FLOWCONTROL_XONXOFF_IN = 4;
public static final int FLOWCONTROL_XONXOFF_OUT = 8;
串口的读取写入都是通过SerialPort对象的getInputStream()
和getOutputStream()
方法获取输入输出流,之后通过输入输出流对串口进行操作。
SerialTool类里提供了两个静态方法
public static void sendToPort(SerialPort serialPort, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure
和
public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure
用于串口的读写。
其中串口读取函数在读取之前使用了available()
方法查看可读取的数据长度,若不为0则进行读取并返回读取到的byte[],为0则返回null。(非堵塞方法)
RXTX类里可以通过设置串口监听器的方式来实现在发生串口事件时执行一些动作。
SerialPortEventListener类的定义如下:
public interface SerialPortEventListener extends EventListener {
void serialEvent(SerialPortEvent var1);
}
SerialPortEvent类的定义:
public class SerialPortEvent extends EventObject {
public static final int DATA_AVAILABLE = 1;
public static final int OUTPUT_BUFFER_EMPTY = 2;
public static final int CTS = 3;
public static final int DSR = 4;
public static final int RI = 5;
public static final int CD = 6;
public static final int OE = 7;
public static final int PE = 8;
public static final int FE = 9;
public static final int BI = 10;
private boolean OldValue;
private boolean NewValue;
private int eventType;
public SerialPortEvent(SerialPort var1, int var2, boolean var3, boolean var4) {
super(var1);
this.OldValue = var3;
this.NewValue = var4;
this.eventType = var2;
}
public int getEventType() {
return this.eventType;
}
public boolean getNewValue() {
return this.NewValue;
}
public boolean getOldValue() {
return this.OldValue;
}
}
本代码中使用了一个Thread对串口进行循环读取,因此没使用监听器接口。