龙空技术网

Java jSerialComm串口通信(USB RS485/232) 查询/应答、主动上报模式

摘星喵Pro 105

前言:

目前同学们对“java获取串口数据”大约比较着重,咱们都想要剖析一些“java获取串口数据”的相关知识。那么小编同时在网摘上网罗了一些有关“java获取串口数据””的相关知识,希望大家能喜欢,看官们一起来学习一下吧!

#2023年度创作挑战#

Java jSerialComm库串口通信(USB RS-485/232) 查询/应答、主动上报模式查询/应答模式

要在Java中通过USB RS-485接口发送和接收特定的数据帧,你需要利用适当的串行通信库。在Java中,一个常见的选择是使用RXTX或jSerialComm库。这些库允许Java应用程序与串行端口进行通信。

以下是实现你描述的特定通信过程的步骤:

1. 添加串行通信库依赖

如果你选择使用jSerialComm库,可以在你的Maven pom.xml 文件中添加以下依赖:

<dependency>    <groupId>com.fazecast</groupId>    <artifactId>jSerialComm</artifactId>    <version>2.6.2</version></dependency>
2. 串行端口通信代码

以下是一个基本的示例代码,展示如何发送和接收数据:

import com.fazecast.jSerialComm.SerialPort;public class RS485Communication {    public static void main(String[] args) {        SerialPort serialPort = SerialPort.getCommPort("COM3"); // 替换为你的端口名        serialPort.setComPortParameters(9600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY); // 设置端口参数        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, 1000, 0);        if (serialPort.openPort()) {            System.out.println("Port opened successfully.");        } else {            System.out.println("Unable to open the port.");            return;        }        try {            // 发送数据            byte[] writeBuffer = new byte[]{(byte) 0xFA, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, (byte) 0xCE, (byte) 0xFA};            serialPort.writeBytes(writeBuffer, writeBuffer.length);            // 接收数据            byte[] readBuffer = new byte[1024]; // 调整数组大小以适应预期的响应长度            int numRead = serialPort.readBytes(readBuffer, readBuffer.length);            System.out.println("Read " + numRead + " bytes.");            // 将读取的字节转换为十六进制字符串            StringBuilder data = new StringBuilder();            for (int i = 0; i < numRead; i++) {                data.append(String.format("%02X ", readBuffer[i]));            }            System.out.println("Received data: " + data.toString());        } catch (Exception e) {            e.printStackTrace();        } finally {            serialPort.closePort();        }    }}
3. 数据解析

在接收到数据后,你可能需要根据你的协议解析这些数据。例如,你可能需要检查帧头、SN码、命令字等,并从数据内容中提取所需的信息。

4. 注意事项确保你的USB RS-485适配器已正确安装,并且你知道它在你的系统中的端口名称(如COM3、COM4等)。适当设置串行通信参数(如波特率、数据位、停止位和奇偶校验位)以匹配你的设备要求。如果你的系统不是基于Windows,串行端口名称可能会有所不同(如在Linux上通常是 /dev/ttyUSB0)。异常处理对于处理通信错误和意外情况非常重要。根据实际情况调整代码中缓冲区大小。主动上报模式(监听)1. 实现方法

一种是轮询模式(Polling),另一种是事件监听模式(Event Listener)。以下是关于这两种方法的说明:

轮询模式(Polling)在轮询模式下,程序会周期性地(通常使用循环)检查串口是否有可用数据。使用一个循环来检查串口COM3是否有可用数据,如果有数据,则读取并处理数据。这种方式比较简单,但可能会造成CPU的浪费,因为程序会不断地检查串口,即使没有数据到达。事件监听模式(Event Listener)在事件监听模式下,程序注册了一个事件监听器(SerialPortEventListener),当串口有数据到达时,事件监听器会触发相应的事件。使用事件监听器来监听串口CO3,当有数据到达时,事件监听器会调用serialEvent方法来处理数据。这种方式相对更高效,因为程序只有在有数据到达时才会执行相应的处理代码,而不需要不断地轮询串口。

根据你的应用需求,选择轮询模式还是事件监听模式都是可以的。事件监听模式通常更加高效,特别是在需要实时处理数据或需要减少CPU占用的情况下。但需要注意的是,使用事件监听模式需要注册事件监听器,并确保程序不会在数据到达前退出。

无论哪种模式,都需要确保串口保持打开状态,以便能够接收数据。串口被打开后,程序进入一个循环或事件监听状态,以便随时接收数据。如果在数据到达之前关闭串口,数据将会丢失。

2. 事件监听模式

import com.fazecast.jSerialComm.SerialPort;import com.fazecast.jSerialComm.SerialPortDataListener;import com.fazecast.jSerialComm.SerialPortEvent;public class RS485CommunicationEventListener {    public static void main(String[] args) {        SerialPort serialPort = SerialPort.getCommPort("COM3"); // 替换为你的端口名        serialPort.setComPortParameters(9600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY); // 设置端口参数        /**         你可以根据具体的需求选择适当的超时模式来控制串口通信的行为。在你的代码中,如果希望立即输出接收到的数据,可以选择 TIMEOUT_NONBLOCKING 模式。如果希望等待数据并确保完整读取,可以选择 TIMEOUT_READ_BLOCKING 模式。         **/        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_NONBLOCKING, 0, 0);        if (serialPort.openPort()) {            System.out.println("Port opened successfully.");        } else {            System.out.println("Unable to open the port.");            return;        }        // 创建数据监听器        serialPort.addDataListener(new SerialPortDataListener() {            @Override            public int getListeningEvents() {                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;            }            @Override            public void serialEvent(SerialPortEvent event) {                if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {                    // 接收数据                    byte[] readBuffer = new byte[1024]; // 调整数组大小以适应预期的响应长度                    int numRead = serialPort.readBytes(readBuffer, readBuffer.length);                    if (numRead > 0) {                        // 将读取的字节转换为十六进制字符串                        StringBuilder data = new StringBuilder();                        for (int i = 0; i < numRead; i++) {                            data.append(String.format("%02X ", readBuffer[i]));                        }                        System.out.println("Received data: " + data.toString());                        // 在这里进行收到数据的处理操作,例如加入阻塞队列,另一个模块消费队列解析收到的数据                    }                }            }        });        // 程序会持续监听串口数据,无需手动创建新线程    }}
3. 轮询模式3.1 实现方法

为了让程序持续监听串口并输出收到的数据,可以在一个单独的线程中运行一个循环来读取串行端口。

import com.fazecast.jSerialComm.SerialPort;public class RS485CommunicationPolling {    public static void main(String[] args) {        SerialPort serialPort = SerialPort.getCommPort("COM3"); // 替换为你的端口名        serialPort.setComPortParameters(9600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY); // 设置端口参数        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_NONBLOCKING, 0, 0);        if (serialPort.openPort()) {            System.out.println("Port opened successfully.");        } else {            System.out.println("Unable to open the port.");            return;        }        // 创建一个新线程来处理输入        Thread thread = new Thread(() -> {            while (true) {                try {                    // 接收数据                    byte[] readBuffer = new byte[1024]; // 调整数组大小以适应预期的响应长度                    int numRead = serialPort.readBytes(readBuffer, readBuffer.length);                    if (numRead > 0) {                        // 将读取的字节转换为十六进制字符串                        StringBuilder data = new StringBuilder();                        for (int i = 0; i < numRead; i++) {                            data.append(String.format("%02X ", readBuffer[i]));                        }                        System.out.println("Received data: " + data.toString());                        // 收到数据的处理操作,例如加入阻塞队列,另一个模块消费队列解析收到的数据                    }                } catch (Exception e) {                    e.printStackTrace();                }            }        });        // 启动线程        thread.start();    }}

上面创建了一个无限循环的线程来读取串行端口。它会持续检查串行端口,当有数据到达时,它会读取数据,将其转换成十六进制格式的字符串,然后输出。

请注意,这样的代码会创建一个永远不会停止的线程,除非你在代码中添加了一种方法来停止它,比如检测到特定的输入或者程序关闭时。在实际应用中,你通常需要一种机制来安全地停止线程,并在不需要它时关闭串行端口。

3.2 轮询模式是否会丢失数据?

在轮询模式下,程序会周期性地使用循环来检查串口是否有可用数据。如果在轮询的间隙内有数据到达串口,这些数据通常会被串口驱动程序缓存起来,等待程序读取。

串口驱动程序通常会提供一个输入缓冲区,用于存储从串口接收到的数据。当数据到达串口时,它们会被放入这个缓冲区中,直到程序来读取它们。如果数据到达速度比程序读取速度快,那么这些数据会在缓冲区中等待。

因此,在轮询模式下,如果程序在轮询间隙内没有及时读取串口数据,已到达但尚未读取的数据会保留在串口的输入缓冲区中,等待程序的读取。程序可以随时读取这些数据,只要它们仍然存在于缓冲区中。

需要注意的是,串口的输入缓冲区大小是有限的,如果数据到达速度非常快,缓冲区可能会被填满,导致后续到达的数据丢失。因此,程序应该以足够快的速度读取串口数据,以避免数据丢失。如果需要处理大量数据或数据到达速度非常快,可以考虑使用事件监听模式,以便在数据到达时立即处理,而不是周期性地轮询。这可以提高数据的实时性。

3.3 串口缓冲区

串口缓冲区通常由串口设备的驱动程序和操作系统共同管理,它们在计算机系统中的位置是软件实现的。

具体来说,串口缓冲区通常包括两个部分:

硬件缓冲区:这部分是串口硬件上的缓冲区,用于存储从外部串口接收到的数据和将要发送的数据。串口硬件上的缓冲区大小是有限的,通常是几个字节到数十个字节不等,具体取决于串口设备的规格和型号。硬件缓冲区的大小是固定的,不可更改。操作系统缓冲区:这部分缓冲区位于操作系统内核中,用于管理串口数据的传输。当数据从串口硬件传输到计算机时,操作系统会将数据从硬件缓冲区复制到操作系统缓冲区中,然后提供给应用程序进行读取。同样,当应用程序要发送数据时,数据首先被写入操作系统缓冲区,然后由操作系统传输到串口硬件。

应用程序通过串口API(如Java中的javax.comm或其他串口库)与操作系统交互,操作系统负责管理硬件缓冲区和数据传输。

因此,串口缓冲区的管理是由操作系统和串口驱动程序协同工作的结果,它们确保数据能够以可靠的方式在计算机和串口设备之间传输。

标签: #java获取串口数据