Java串口通信介绍
概述:用Java的方式和我们的硬件(传感器)通过串口(COM3)实现数据交互等功能,基于UART协议实现
串口(串行端口)通信
Java串口通讯是指使用Java编程语言与串行端口(串口)进行数据通讯的过程,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式(注意,是一位一位的传输,区别于并口通讯,传输慢)。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。虽然说慢,不如并,但不代表就要抛弃,某些项目还是很有用的,例如公司最近营养探索馆的一个血压探测仪,就是串口通讯,需要开发串口。
优点:
- 适用性广泛: 串口通讯通常用于连接各种外部设备,如传感器、控制器、调制解调器等,因此适用性非常广泛。
- 线缆简单: 串口通讯只需要少量的线缆就能实现数据传输,因此在布线和连接方面比较简单。
- 长距离传输: 串口通讯可以通过串口转以太网等设备实现远程通讯,支持长距离传输。
缺点:
- 速度相对较慢: 串口通讯的传输速度通常比较慢,受到硬件限制,无法满足高速数据传输的需求。
- 连接设备数量有限: 串口通讯通常只支持连接少量外部设备,无法承载大规模设备连接的需求。
并口通信
并口通讯是通过计算机的并行接口(并口)进行数据传输的方式。并口通讯可以同时传输多个比特(通常是8位或更多),因此传输速度相对较快。并口通讯通常用于连接一些需要高速数据传输的外部设备,如打印机、扫描仪等。并口通讯所需的线缆较为复杂,包括大量的数据线和控制线,连接和布线相对复杂。
优点:
- 高速传输: 并口通讯支持并行传输,因此传输速度相对较快,在某些应用场景下能够满足高速数据传输的需求。
- 多设备连接: 并口通讯通常支持连接多个外部设备,能够满足一定规模的设备连接需求。
缺点:
- 线缆复杂: 并口通讯所需的线缆较为复杂,包括大量的数据线和控制线,因此在布线和连接方面相对复杂。
- 不易远程传输: 由于线缆复杂和传输方式的限制,通常不能实现远程数据传输。
综合来看,串口通讯适用性广泛、连接简单,但传输速度较慢;而并口通讯传输速度快,能够连接多个设备,但线缆复杂且不易远程传输。因此在实际应用中,选择串口通讯还是并口通讯取决于具体的应用场景和需求。
实现方式
1.通过comm.jar实现太老了,对jdk版本有要求(jdk 1.8 32bit),也要配置dll文件
2.通过RXTXcomm.jar实现(更新慢),需要配置dll文件,打包可能会出错
3.通过jserialcomm实现(推荐)跨平台,兼容性好
1.通过comm.jar实现
准备工作
1.下载comm.jar相关资源,将javax.comm.properties,win32com.dll配置文件放到jdk的bin和lib目录
2.准备好一个串口工具,和模拟串口的工具,便于测试
关于依赖引用问题:
当maven拉取不到相关依赖时,有两种解决方案
1.作为资源文件夹内的资源引用
我们可以选择将该依赖的jar包下载下来,通过创建lib文件夹的方式将依赖添加到项目中
<dependency>
<groupId>gun.io</groupId>
<artifactId>rxtx</artifactId>
<version>1.0.0</version>
<!--system,类似provided,需要显式提供依赖的jar以后,Maven就不会在Repository中查找它-->
<scope>system</scope>
<systemPath>${project.basedir}/lib/RXTXcomm.jar</systemPath>
</dependency>
2.将jar包手动安装到maven仓库
实现步骤
1.用Configure Virtual Serial Port Driver虚拟串口工具模拟2个串口号
2.导入相应依赖 comm.jar包并添加到项目中,jdk版本为1.8 32bit
3.通过comm.jar包提供的方法和串口建立通讯
4.通过CommPortIdentifier.getPortIdentifiers()方法获取可用串口
5.选择合适端口,通过该commPort.open()方法打开该端口
6.设置常用参数,如波特率,停止位,校验位等 serialPort.setSerialPortParams()
7.端口打开后,可用通过serialPort.addEventListener()方法添加监听器监听我们之前打开的端口
8.通过程序向端口发送数据,核心是基于流的形式,通过outputStream.write()发送数据
相关代码
package com.xxxx.ckcomm.utils;
import javax.comm.*;
import java.io.*;
import java.util.*;
public class DSerialPort implements Runnable, SerialPortEventListener {
private String appName = "串口通讯测试";
private int timeout = 2000;// open 端口时的等待时间,延迟时间(毫秒数)
private int threadTime = 0;
private CommPortIdentifier commPort;
private SerialPort serialPort;
private InputStream inputStream;
private OutputStream outputStream;
//当前接收COM口的数据
public static String receiptDataString = "";
//当前已取的数组下标
public static int nowDataIndex = 0;
//当前接收COM口的数据自动切割成StringList
public static ArrayList<String> receiptDataList = new ArrayList<String>();
static {
String driverName = "com.sun.comm.Win32Driver";
CommDriver driver;
try {
driver = (CommDriver) Class.forName(driverName).newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
driver.initialize();
}
/**
* @方法名称 :listPort
* @功能描述 :列出所有可用的串口
* @返回值类型 :List<String>
*/
@SuppressWarnings("rawtypes")
public List<String> listPort() {
//获得当前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> portNameList = new ArrayList<>();
//将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* @param portName
* @方法名称 :selectPort
* @功能描述 :选择一个端口,比如:COM1
* @返回值类型 :void
*/
@SuppressWarnings("rawtypes")
public void selectPort(String portName) {
this.commPort = null;
CommPortIdentifier cpid;
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
System.out.println("串口接口调用成功");
cpid = (CommPortIdentifier) portList.nextElement();
if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL && cpid.getName().equals(portName)) {
this.commPort = cpid;
break;
}
}
openPort();
}
/**
* @方法名称 :openPort
* @功能描述 :打开SerialPort
* @返回值类型 :void
*/
private void openPort() {
if (commPort == null)
log("无法找到串口!");
else {
log("端口选择成功,当前端口:" + commPort.getName() + ",现在实例化 SerialPort:");
try {
serialPort = (SerialPort) commPort.open(appName, timeout);//打开端口
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
//设置参数
log("实例 SerialPort 成功!");
} catch (PortInUseException e) {
throw new RuntimeException(String.format("端口'%1$s'正在使用中!", commPort.getName()));
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
}
}
/**
* @方法名称 :checkPort
* @功能描述 :检查端口是否正确连接
* @返回值类型 :void
*/
public void checkPort() {
if (commPort == null)
throw new RuntimeException("没有选择端口,请使用 " + "selectPort(String portName) 方法选择端口");
if (serialPort == null) {
throw new RuntimeException("SerialPort 对象无效!");
}
}
/**
* @方法名称 :write
* @功能描述 :向端口发送数据,请在调用此方法前 先选择端口,并确定SerialPort正常打开!
* @返回值类型 :void
*/
public void write(String message) {
checkPort();
try {
outputStream = new BufferedOutputStream(serialPort.getOutputStream());
} catch (IOException e) {
throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
}
try {
outputStream.write(message.getBytes());
log("信息发送成功!");
} catch (IOException e) {
throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
} finally {
try {
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void write(byte[] message