欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Java 串口通信—收发,监听数据(代码实现)

Java 串口通信—收发,监听数据(代码实现)

2024/10/25 0:27:58 来源:https://blog.csdn.net/weixin_49171365/article/details/139674930  浏览:    关键词:Java 串口通信—收发,监听数据(代码实现)

一、串口通信与串行通信的原理

        串行通信是指仅用一根接收线和一根发送线,将数据以位进行依次传输的一种通讯方式,每一位数据占据一个固定的时间长度。

        串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节的通信方式。

        串口通信时首先要约定好帧格式与波特率,并且串口通信采用TTL逻辑(Transistor-Transistor Logic) 是指晶体管-晶体管逻辑。

TTL逻辑门的标准输出状态是:

高电平(逻辑“1”或HIGH):典型值为+2.4V至+5V(对于标准5V电源供电的TTL),代表有效信号。
低电平(逻辑“0”或LOW):典型值接近于地电压(0V至+0.4V),代表无效信号。
这种通信方式抗干扰能力弱,下图中,静电让某一个低电平变高,就会导致通信距离很短,越1米。

        所以为了提示通信的稳定性与距离,产生了由美国制订的推荐标准RS232、RS485,都是在基础串口通信的基础上加了特定的电平转换芯片改变电压传输值。

串口通信主要可分为232、485、422三种通信方式。

232通信

232通信主要是由RX、TX、GND三根线组成。RX接TX,TX接RX,GND接GND。发送和接收由不同的线路处理,所以能够同时进行发送和接收数据,因此232是全双工通信。

485通信

为了解决232通信距离受限问题,于是有了485通信。 485通信只需要+、-两根线(也叫A、B两根线),A、B两根线的差分电平信号就是作为数据信号传输。发送和接收都用这两根线,每次只能发送数据或者接收数据,因此485是半双工通信。

422通信

        422通信兼具232与485的特点,既能实现全双工通信,又能增大传输距离。422也常被标注为485-4,而485被标注为485-2。因为485-2是2根线,485-4是4根线。

并行和串行的区别:

  • 并行传输: 多根线路传输,每条物理线代表一个比特位,可以同时发送和接受多个字节;

  • 串行传输: 只用一条线路传输,逐位传输,字节内允许出现延迟(如开始或结束等),而每个字节之间必须有完整的停止和起始位置。

同步传输和异步传输的差别:

  • 同步传输:在知道对方状态下进行全双工传输,需要调节系统时钟,使得传送速度不超过缓存容量,并保持包络上均衡。

  • 异步传输:既能针对字符流,也可针对比特流。异步传输没有明确定义的“帧”模式,因此不需要同步时钟,但会增加一个帧同步域,用于在接收端恢复字节边界,并通过位数逐个地进行帧的识别。

在实际应用中,串口通常使用异步技术来发送和接收数据。异步传输可以按需动态调整数据传输速率,以便快速响应用户操作或自动控制事件的发生。

二、Java 实现串口通信

准备工作

要想使用串口包进行串口通信,除了设置好环境变量之外,还要准备对应的串口通信的jar包

1. 下载对应版本的rxtx 解压后得到三个文件 rxtxParallel.dll、rxtxSerial.dll、RXTXcomm.jar/

下载地址如下:RXTX for Java (fizzed.com)


2. 将rxtxParallel.dll、rxtxSerial.dll 放进当前使用的jdk的bin目录( jdk版本必须与下载的rxtx一致 ,区分32位/64位 AMD/IA)


3. (windows x64系统可以将当前项目libs下的 rxtxParallel.dll、rxtxSerial.dll 直接放入jdk下的bin目录)
4. 需要将libs下的RXTXcomm.jar加入项目依赖,将javax.usb.properties复制到 JRE\lib或者JDK\lib.

<dependency><groupId>org.rxtx</groupId><artifactId>rxtx</artifactId><version>2.2</version><scope>system</scope><systemPath>${project.basedir}/src/main/resources/lib/RXTXcomm.jar</systemPath>
</dependency>

说明:(如果没有正常引入将会出现这个错误)

代码实现 

package com.ss.serialport.controller;import gnu.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;public class SerialPortTool {public static void main(String[] args) {// 查询串口findSystemAllComPort();//        //发送16进制数据——实际应用中串口通信传输的数据,大都是 16 进制String hexStrCode = "455A432F5600";SerialPort serialPort = SerialPortTool.openComPort(null, 38400, 8, 1, 0);// 设置串口监听SerialPortTool.setListenerToSerialPort(serialPort, new SerialPortEventListener() {@Overridepublic void serialEvent(SerialPortEvent arg0) {if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {//数据通知byte[] bytes = SerialPortTool.readData(serialPort);System.out.println("收到的数据长度:" + bytes.length);System.out.println("收到的数据:" + new String(bytes));}}});new Thread(new Runnable() {@Overridepublic void run() {int i = 1;while (i < 1000) {byte[] bytes = hexStrCode.getBytes();SerialPortTool.sendDataToComPort(serialPort, bytes);//发送数据i++;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}/*** 查找电脑上所有可用 com 端口** @return 可用端口名称列表,没有时 列表为空*/public static final ArrayList<String> findSystemAllComPort() {/***  getPortIdentifiers:获得电脑主板当前所有可用串口*/Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();ArrayList<String> portNameList = new ArrayList<>();/***  将可用串口名添加到 List 列表*/System.out.println("===========串口信息start==============");while (portList.hasMoreElements()) {CommPortIdentifier commPortIdentifier = portList.nextElement();StringBuffer returnStr = new StringBuffer();String portName = commPortIdentifier.getName();//名称如 COM1、COM2....String isOwner = String.valueOf(commPortIdentifier.isCurrentlyOwned());returnStr.append(isOwner).append("\t 占用情况:").append(isOwner);System.out.println(returnStr);portNameList.add(portName);}System.out.println("===========串口信息end==============");return portNameList;}/*** 打开电脑上指定的串口** @param portName 端口名称,如 COM1,为 null 时,默认使用电脑中能用的端口中的第一个* @param b        波特率(baudrate),如 9600* @param d        数据位(datebits),如 SerialPort.DATABITS_8 = 8* @param s        停止位(stopbits),如 SerialPort.STOPBITS_1 = 1* @param p        校验位 (parity),如 SerialPort.PARITY_NONE = 0* @return 打开的串口对象,打开失败时,返回 null*/public static final SerialPort openComPort(String portName, int b, int d, int s, int p) {CommPort commPort = null;try {//当没有传入可用的 com 口时,默认使用电脑中可用的 com 口中的第一个if (portName == null || "".equals(portName)) {List<String> comPortList = findSystemAllComPort();if (comPortList != null && comPortList.size() > 0) {portName = comPortList.get(0);}}System.out.println("开始打开串口:portName=" + portName + ",baudrate=" + b + ",datebits=" + d + ",stopbits=" + s + ",parity=" + p);//通过端口名称识别指定 COM 端口CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);/*** open(String TheOwner, int i):打开端口* TheOwner 自定义一个端口名称,随便自定义即可* i:打开的端口的超时时间,单位毫秒,超时则抛出异常:PortInUseException if in use.* 如果此时串口已经被占用,则抛出异常:gnu.io.PortInUseException: Unknown Application*/commPort = portIdentifier.open(portName, 5000);/*** 判断端口是不是串口* public abstract class SerialPort extends CommPort*/if (commPort instanceof SerialPort) {SerialPort serialPort = (SerialPort) commPort;/*** 设置串口参数:setSerialPortParams( int b, int d, int s, int p )* b:波特率(baudrate)* d:数据位(datebits),SerialPort 支持 5,6,7,8* s:停止位(stopbits),SerialPort 支持 1,2,3* p:校验位 (parity),SerialPort 支持 0,1,2,3,4* 如果参数设置错误,则抛出异常:gnu.io.UnsupportedCommOperationException: Invalid Parameter* 此时必须关闭串口,否则下次 portIdentifier.open 时会打不开串口,因为已经被占用*/serialPort.setSerialPortParams(b, d, s, p);System.out.println("打开串口 " + portName + " 成功...");return serialPort;} else {System.out.println("当前端口 " + commPort.getName() + " 不是串口...");}} catch (NoSuchPortException e) {e.printStackTrace();} catch (PortInUseException e) {System.out.println("串口 " + portName + " 已经被占用,请先解除占用...");e.printStackTrace();} catch (UnsupportedCommOperationException e) {System.out.println("串口参数设置错误,关闭串口,数据位[5-8]、停止位[1-3]、验证位[0-4]...");e.printStackTrace();if (commPort != null) {//此时必须关闭串口,否则下次 portIdentifier.open 时会打不开串口,因为已经被占用commPort.close();}}System.out.println("打开串口 " + portName + " 失败...");return null;}/*** 往串口发送数据** @param serialPort 串口对象* @param orders     待发送数据*/public static void sendDataToComPort(SerialPort serialPort, byte[] orders) {OutputStream outputStream = null;try {if (serialPort != null) {outputStream = serialPort.getOutputStream();outputStream.write(orders);outputStream.flush();System.out.println("往串口 " + serialPort.getName() + " 发送数据:" + Arrays.toString(orders) + " 完成...");} else {System.out.println("gnu.io.SerialPort 为null,取消数据发送...");}} catch (IOException e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 从串口读取数据** @param serialPort 要读取的串口* @return 读取的数据*/public static byte[] readData(SerialPort serialPort) {InputStream is = null;byte[] bytes = null;try {is = serialPort.getInputStream();//获得串口的输入流int bufflenth = is.available();//获得数据长度while (bufflenth != 0) {bytes = new byte[bufflenth];//初始化byte数组is.read(bytes);bufflenth = is.available();}} catch (IOException e) {e.printStackTrace();} finally {try {if (is != null) {is.close();is = null;}} catch (IOException e) {e.printStackTrace();}}return bytes;}/*** 给串口设置监听** @param serialPort* @param listener*/public static void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener) {try {//给串口添加事件监听serialPort.addEventListener(listener);} catch (TooManyListenersException e) {e.printStackTrace();}serialPort.notifyOnDataAvailable(true);//串口有数据监听serialPort.notifyOnBreakInterrupt(true);//中断事件监听}/*** 关闭串口** @param serialPort 待关闭的串口对象*/public static void closeComPort(SerialPort serialPort) {if (serialPort != null) {serialPort.close();System.out.println("关闭串口 " + serialPort.getName());}}/*** 16进制字符串转十进制字节数组* 这是常用的方法,如某些硬件的通信指令就是提供的16进制字符串,发送时需要转为字节数组再进行发送** @param strSource 16进制字符串,如 "455A432F5600",每两位对应字节数组中的一个10进制元素*                  默认会去除参数字符串中的空格,所以参数 "45 5A 43 2F 56 00" 也是可以的* @return 十进制字节数组, 如 [69, 90, 67, 47, 86, 0]*/public static byte[] hexString2Bytes(String strSource) {if (strSource == null || "".equals(strSource.trim())) {System.out.println("hexString2Bytes 参数为空,放弃转换.");return null;}strSource = strSource.replace(" ", "");int l = strSource.length() / 2;byte[] ret = new byte[l];for (int i = 0; i < l; i++) {ret[i] = Integer.valueOf(strSource.substring(i * 2, i * 2 + 2), 16).byteValue();}return ret;}}

整合成串口工具类 :

package com.ss.serialport.utils;import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;/*** @ClassName DLSC_SerialPort* @Description 串口工具类 :打开串口 ;接受数据 ;发送数据* @Version 1.0*/
public class DLSC_SerialPort {private SerialPort serialPort;private InputStream inputStream;private OutputStream outputStream;private BlockingQueue<String> receivedDataQueue = new LinkedBlockingQueue<>();private volatile boolean isRunning = true;private void connect(String portName) throws Exception {// 获取串口通信端口实例CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(portName);if (portId.isCurrentlyOwned()) {System.err.println("Error: Port is currently in use");} else {// 打开端口,并获取输入、输出流serialPort = (SerialPort) portId.open("SerialCommExample", 2000);inputStream = serialPort.getInputStream();outputStream = serialPort.getOutputStream();// 设置串口参数serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);// 启动读取数据的线程Thread readThread = new Thread(() -> {try {while (isRunning) {int available = inputStream.available();if (available > 0) {byte[] bytes = new byte[available];inputStream.read(bytes);String data = new String(bytes, "UTF-8");receivedDataQueue.put(data); // 将数据放入队列}Thread.sleep(100); // 等待一段时间再检查是否有新数据}} catch (IOException | InterruptedException e) {e.printStackTrace();}});readThread.start();}}private void sendData(String data) throws IOException {if (outputStream != null) {outputStream.write(data.getBytes("UTF-8"));outputStream.flush(); // 确保数据被发送出去}}private String receiveData(long timeout) throws InterruptedException {return receivedDataQueue.poll(timeout, TimeUnit.MILLISECONDS); // 尝试从队列中获取数据,超时则返回null}private void disconnect() throws Exception {isRunning = false; // 设置标志位以停止读取数据的线程if (serialPort != null) {serialPort.removeEventListener();serialPort.close();}}public static void send (String msg) {DLSC_SerialPort example = new DLSC_SerialPort();try {// 连接到串口example.connect("COM3"); // 替换为你的串口名称// 发送数据example.sendData(msg + "\r\n");// 断开连接example.disconnect();} catch (Exception e) {e.printStackTrace();}}public static Double sendAndReceives(String msg) {DLSC_SerialPort example = new DLSC_SerialPort();try {// 连接到串口example.connect("COM1"); // 替换为你的串口名称// 发送数据example.sendData(msg + "\r\n");// 等待并接收数据String receivedData = example.receiveData(5000); // 等待最多5秒来获取数据if (receivedData != null) {Double backData = Double.valueOf(receivedData);System.out.println("Received data: " + backData);return backData;} else {System.out.println("No data received within the timeout period.");}// 断开连接example.disconnect();} catch (Exception e) {e.printStackTrace();}return null;}//    public static String sendAndReceive(String msg) {
//
//        SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
//        ArrayList<String> port = serialPortUtil.findPort();
//        if (port != null && !port.isEmpty()) {
//            System.out.println("没有发现任何端口");
//        }
//        String portName = port.get(2);
//        SerialPort serialPort = serialPortUtil.openPort(
//                portName, 9600, 8, 1, 0);
//        if (serialPort == null) {
//            System.out.println("打开串口(" + portName + ")失败");
//        }
//        System.out.println("成功连接串口:" + portName);serialPortUtil.sendToPort(serialPort, "SOUR:VOLT 28\r\n".getBytes());
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        serialPortUtil.sendToPort(serialPort, "SOUR:CURR:LIMIT:HIGH?\r\n".getBytes());
//        byte[] bytes = serialPortUtil.readFromPort(serialPort);
//        if (bytes != null) {
//            return new String(bytes);
//        }
//        serialPortUtil.closePort(serialPort);
//        return null;
//    }//    public static String sendAndReceive(String msg) {
//
//        SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
//        ArrayList<String> port = serialPortUtil.findPort();
//        if (port != null && !port.isEmpty()) {
//            System.out.println("没有发现任何端口");
//        }
//        String portName = port.get(0);
//        SerialPort serialPort = serialPortUtil.openPort(
//                portName, 9600, 8, 1, 0);
//        if (serialPort == null) {
//            System.out.println("打开串口(" + portName + ")失败");
//        }
//        System.out.println("成功连接串口:" + portName);serialPortUtil.sendToPort(serialPort, "SOUR:VOLT 28\r\n".getBytes());
//        try {
//            Thread.sleep(6000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        serialPortUtil.sendToPort(serialPort, "SOUR:CURR:LIMIT:HIGH?\r\n".getBytes());
//        byte[] bytes = serialPortUtil.readFromPort(serialPort);
//        if (bytes != null) {
//            return new String(bytes);
//        }
//        System.out.println(serialPort);
//        serialPortUtil.closePort(serialPort);
//        return null;
//    }public static String sendAndReceive(String msg) {SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();ArrayList<String> port = serialPortUtil.findPort();System.out.println(port);if (port == null || port.isEmpty()) {System.out.println("没有发现任何端口");return null;}String portName = port.get(0);SerialPort serialPort = serialPortUtil.openPort(portName, 9600, 8, 1, 0);if (serialPort == null) {System.out.println("打开串口(" + portName + ")失败");return null;}System.out.println("成功连接串口:" + portName);try {// serialPortUtil.sendToPort(serialPort, "SOUR:VOLT 28\r\n".getBytes());Thread.sleep(6000);serialPortUtil.sendToPort(serialPort, "SOUR:CURR:LIMIT:HIGH?\r\n".getBytes());byte[] bytes = serialPortUtil.readFromPort(serialPort);if (bytes != null) {return new String(bytes);} else {System.out.println("未读取到任何数据");return null;}} catch (InterruptedException e) {e.printStackTrace();return null;} finally {// 确保串口在所有情况下都关闭serialPortUtil.closePort(serialPort);}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com