之前使用c++/C#时,都是可以直接调用串口API的,java没有源生支持串口,只能通过第三方扩展
这里使用的是javacomm20-win32.zip这个实现的库,可自行网上下载
使用时,先解压,再复制三个文件到jdk中,可执行下面的命令进行复制
copy comm.jar "%JAVA_HOME%\jre\lib\ext\"
copy javax.comm.properties "%JAVA_HOME%\jre\lib\"
copy win32com.dll "%JAVA_HOME%\jre\bin\"
在eclipse中在引入comm.jar即可使用串口api
虽然这中方式确实能够解决java调用串口问题,但是需要复制第三方库到jdk,总感觉不爽。
为了在不修改jdk的情况下,也能够使用串口功能,就需要修改引用串口库的方式了。
在我的项目中,
将comm.jar、javax.comm.properties、win32com.dll放在了src/main/resources/commapi/下
第一步是引入comm.jar
这一步相对比较容易,只要将这个jar包引入项目即可,如果使用maven进行管理的话,可参考如下方式引用
<dependency>
<groupId>javax.comm</groupId>
<artifactId>comm</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/commapi/comm.jar</systemPath>
</dependency>
第二部是引入win32com.dll
这一步实际上需要将win32com.dll所在的路径加入到path路径,但是不想通过直接修改环境变量,而是通过程序动态添加的方式实现,实现方式参考如下代码
public static void addDir(String s){
try {
logger.info(s);
Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
String[] paths = (String[]) field.get(null);
for (int i = 0; i < paths.length; i++) {
if (s.equals(paths[i])) {
return;
}
}
String[] tmp = new String[paths.length + 1];
System.arraycopy(paths, 0, tmp, 0, paths.length);
tmp[paths.length] = s;
field.set(null, tmp);
} catch (Exception e) {
logger.error("加载path异常", e);
}
}
/**
* 动态添加系统path变量,用于动态加载DLL
*/
public static void systemPathInit() {
// 添加串口win32com.dll路径
addDir(PathUtil.getResourcePath() + "\\commapi");
// 添加一个站位用的classpath,用于串口comm.jar寻找javax.comm.properties。详见comm.jar->CommPortIdentifier->findPropFile();
System.setProperty("java.class.path", System.getProperty("java.class.path") + ";" + PathUtil.getResourcePath() + "\\commapi\\comm.jar");
}
设置java.class.path,主要是因为,comm.jar源码中,会通过java.class.path找到comm.jar的路径,再在comm.jar的相同路径中寻找javax.comm.properties文件。这样就可以让comm.jar找到javax.comm.properties文件。
systemPathInit()方法要在程序启动时,使用串口前先调用一次
以下是串口使用的参考代码
import java.io.InputStream;
import java.io.OutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class CommunicateBase {
private Logger logger = LoggerFactory.getLogger(getClass());
protected OutputStream outputStream;
protected InputStream inputStream;
/**
* 打开
* @return
*/
public abstract String open();
/**
*
* @param cmd
*/
public void send(String cmd) {
try {
if (outputStream == null) {
logger.error("连接未打开");
return;
}
outputStream.write(cmd.getBytes());
} catch (Exception e) {
logger.error("发送异常", e);
}
}
/**
* 读取数据
* @return
*/
public String read(int len){
try {
if (inputStream == null) {
logger.error("连接未打开");
return "";
}
int off = 0;//偏移
byte[] readBuffer = new byte[len];//读数据缓存
while (inputStream.available() > 0) {//循环读取接收的数据
// 读取数据
int readCount = inputStream.read(readBuffer, off, len);
// 修正偏移量
off += readCount;
len -= readCount;
ThreadUtil.sleep(50);
}
// 转换为字符串
return new String(readBuffer).trim();
} catch (Exception e) {
logger.error("接收异常", e);
return "";
}
}
/**
* 关闭
*/
public abstract void close();
}
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javax.comm.CommDriver;
import javax.comm.CommPort;
import javax.comm.CommPortIdentifier;
import javax.comm.SerialPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.comm.Win32Driver;
/**
* 串口工具类
*
*
*/
public class Uart extends CommunicateBase{
protected Logger logger = LoggerFactory.getLogger(Uart.class);
private CommPortIdentifier portId;
private SerialPort serialPort;
private String comm = "COM9";// 端口号
private int baudRate = 9600;// 波特率
private int timeout = 100;//open 端口时的等待时间
public Uart(String comm, int baudRate) {
this.comm = comm;
this.baudRate = baudRate;
}
/**
* 通过反射清空CommPortIdentifier中的串口列表
* 可以解决动态添加串口时,也能够识别出的功能,
*/
private void commClear() {
try {
// 清空api链表
Constructor con = CommPortIdentifier.class.getDeclaredConstructor(
String.class, CommPort.class, int.class, CommDriver.class);
con.setAccessible(true);
Object o = (CommPortIdentifier) con
.newInstance(null, null, 0, null);// 获取对象
Field f = CommPortIdentifier.class.getDeclaredField("masterIdList");// 根据key获取参数
f.setAccessible(true);
f.set(o, null);
// 重新初始化串口api
new Win32Driver().initialize();
} catch (Exception e) {
logger.error("清除串口异常", e);
}
}
/**
* 打开串口
* @return
*/
@Override
public String open(){
try {
close();//先保证串口为关闭状态
PathUtil.printAllPath();
// 清空串口链表
commClear();
portId = CommPortIdentifier.getPortIdentifier(comm);
serialPort = (SerialPort) portId.open("Uart", timeout);
outputStream = serialPort.getOutputStream();
inputStream = serialPort.getInputStream();
serialPort.setSerialPortParams(baudRate,// 波特率
SerialPort.DATABITS_8, // 数据位
SerialPort.STOPBITS_1,// 停止位
SerialPort.PARITY_NONE);// 校验位
logger.info("串口已打开");
return "成功";
} catch (Exception e) {
logger.error("串口打开失败", e);
return e.getMessage();
}
}
/**
* 关闭串口
*/
@Override
public void close() {
if (serialPort != null) {
serialPort.close();
portId = null;
serialPort = null;
outputStream = null;
inputStream = null;
logger.info("串口已关闭");
}
}
/**
* 是否连接
* @return
*/
public boolean isConnected() {
if (portId != null) {
logger.info(portId.getCurrentOwner());
return portId.getCurrentOwner().equals("Port currently not owned");
}
return false;
}
public static void main(String[] args) throws InterruptedException{
PathUtil.addDir(PathUtil.getResourcePath() + "\\commapi");
Uart uart = new Uart("COM4", 9600);
uart.open();
uart.send("aa");
Thread.sleep(1000);
System.out.println(uart.read(50));
uart.close();
}
}