目录
- 一、背景
- 二、串口通信技术介绍
- 三、代码示例
- 3.1 创建串口监听器
- 3.2 串口初始化
- 3.3 启动类
- 3.4 pom文件
- 3.5 发送的Job
- 3.6 模拟窗口发布的软件
- 四、总结
一、背景
近期,我的项目需要对接各种设备,其中有几台设备采用了串口通信方式。由于之前对串口通信方式接触不多,深感自己在这方面知识的欠缺,于是我决定对串口通信进行深入的研究和开发。
我仔细学习了串口通信的原理、协议以及相关的编程技术,并通过实践不断加深对串口通信的理解。
在这个过程中,我遇到了不少挑战,但通过不断的学习和实践,最终成功实现了与设备的串口通信。现在,我将自己的学习和开发经验记录下来,形成这篇博文,希望能够为有需要的同学提供一些帮助和指导。
二、串口通信技术介绍
串口是串行接口(serial port
)的简称,也称为串行通信接口或COM
接口。
串口通信是指采用串行通信协议(serial communication
)在一条信号线上将数据一个比特一个比特地逐位进行传输的通信模式。
串口通信
是一种利用串行通信协议,在计算机与外部设备之间进行异步通信的技术。串行通信按照时间顺序,按位依次发送通信字节,相较于并行通信,它仅需较少的数据线,通常两根线即可实现双向通信。在串行通信中,并行数据被转换为串行数据后,通过传输线路依次传输。异步通信
则是发送端和接收端通过起始位、停止位来同步数据块的方式。发送端在发送数据字节前,会先发送一个起始位,然后依次发送数据字节的每个位,最后发送一个或多个停止位。接收端通过检测起始位的状态转变来同步接收数据,一旦检测到起始位,便会根据事先约定的规则接收之后的数据位和停止位。
三、代码示例
3.1 创建串口监听器
// 串口监听器 @Slf4j public class MyLister implewww.devze.comments SerialPortEventListener { //private static final Logger log = LoggerFactory.getLogger(MyLister.class); @Override public void serialEvent(SerialPortEvent event) { switch (event.getEventType()) { // 串口存在有效数据 case SerialPortEvent.DATA_AVAILABLE: byte[] bytes = SerialPortUtil.getSerialPortUtil().readFromPort(PortInit.serialPort); log.info("===========start==========="); String str = new String(bytes, StandardCharsets.UTF_8); log.info("{}【读到的字符】:-----{}", LocalDateTime.now(), str); log.info("{}【字节数组转16进制字符串】:-----{}", LocalDateTime.now(), stringToHex(str)); log.info("===========end==========="); break; // 2.输出缓冲区已清空 case SerialPortEvent.OUTPUT_BUFFER_EMPTY: log.error("输出缓冲区已清空"); break; // 3.清除待发送数据 case SerialPortEvent.CTS: javascript log.error("清除待发送数据"); break; // 4.待发送数据准备好了 case SerialPortEvent.DSR: log.error("待发送数据准备好了"); break; // 10.通讯中断 case SerialPortEvent.BI: log.error("与串口设备通讯中断"); break; default: break; } } public static String stringToHex(String input) { byte[] bytes = input.getBytes(StandardCharsets.UTF_8); StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xFF & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } /** * ASCII码转字节数组 */ public static byte[] asciiToIntArray(List<Integer> asciiValues) { byte[] byteArray = new byte[asciiValues.size()]; for (int i = 0; i < asciiValues.size(); i++) { int asciiValue = asciiValues.get(i); byteArray[i] = (byte) asciiValue; } return byteArray; } /** * 字符串转ASCII码 */ public static List<Integer> stringToAscii(String input) { List<Integer> asciiValues = new ArrayList<>(); for (char c : input.toCharArray()) { asciiValues.add((int) c); } return asciiValues; } /** * 16进制字符串转字节数组 * * @param hex 16进制字符串 * @return 字节数组 */ public static byte[] hex2byte(String hex) { if (!isHexString(hex)) { return null; } char[] arr = hex.toCharArray(); byte[] b = new byte[hex.length() / 2]; for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) { String swap = "" + arr[i++] + arr[i]; int byteint = Integer.parseInt(swap, 16) & 0xFF; b[j] = new Integer(byteint).byteValue(); } return b; } /** * 校验是否是16进制字符串 * * @param hex * @return */ public static boolean isHexString(String hex) { if (hex == null || hex.length() % 2 != 0) { return false; } for (int i = 0; i < hex.length(); i++) { char c = hex.charAt(i); if (!isHexChar(c)) { return false; } } return true; } /** * 校验是否是16进制字符 * * @param c * @return */ private static boolean isHexChar(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } }
3.2 串口初始化
/** * 串口初始化 */ @Component @Slf4j public class PortInit implements ApplicationRunner { public static SerialPort serialPort = null; @Override public void run(ApplicationArguments args) { String portName = "COM1"; //查看所有串口 SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil(); ArrayList<String> port = serialPortUtil.findPort(); log.info("发现全部串口:{}", port); log.info("打开指定portName:{}", portName); //打开该对应portName名字的串口 PortInit.serialPort = serialPortUtil.openPort( portName, 9600, SerialPort.DATABITS_8, SerialPort.PARITY_NONE, SerialPort.PARITY_ODD); //给对应的serialPort添加监听器 serialPortUtil.addListener(PortInit.serialPort, new MyLister()); } }
3.3 启动类
@SpringBootApplication public class SerialPortProjectApplication { public static void main(String[] args) { SpringApplication.run(SerialPortProjectApplication.class, args); } @PreDestroy public void destroy() { //关闭应用前 关闭端口 SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil(); serialPortUtil.removeListener(PortInit.serialPort); serialPortUtil.closePort(PortInit.serialPort); } }
3.4 pom文件
<?XML version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.xsd</groupId> <artifactId>SerialPortProject</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SerialPortProject</name> <description>SerialPortProject</description> <properties> <Java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>com.baomidou</groupId>--> <!-- <artifactId>myBATis-plus-boot-starter</artifactId>--> <!-- <version>3.5.2</vvYjciOersion>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>mysql</groupId>--> <!-- <artifactId>mysql-connector-java</artifactId>--> <!-- <scope>runtime</scope>--> <!-- </dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- <dependency>--> <!-- <groupId>org.springframework.security</groupId>--> <!-- <artifactId>spring-security-thttp://www.devze.comest</artifactId>--> <!-- <scope>test</scope>--> <!-- </dependency>--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency> <!-- PropertyUtils工具类 --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.9</version> </dependency> <!-- jwt依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <!-- 串口内容读取 --> <dependency> <groupId>org.bidib.jbidib.org.qbang.rxtx</groupId> <artifactId>rxtxcomm</artifactId> <version>2.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin&lwww.devze.comt;/artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
3.5 发送的Job
@Component @EnableScheduling public class SendJob { /** * 定时发送数据 * initialDelay :系统启动后,3秒后开始运行此方法 * fixedDelay:表示此方法运行结束后,过1秒再次运行此方法 */ @Scheduled(initialDelay = 1000 * 3, fixedDelay = 1000) public void plcAnalytic() { String s = "4040000900手动滑稽阿萨德和静安寺060004000000100172323"; SerialPortUtil.getSerialPortUtil().sendToPort(PortInit.serialPort, MyLister.asciiToIntArray(MyLister.stringToAscii(s))); } }
3.6 模拟窗口发布的软件
四、总结
串口通信因其简单性、灵活性和通用性,在众多应用场景中仍然保持着极高的实用价值。
深入理解串口的工作原理,掌握串口通信接口的选择与编程技巧,对于更好地应用串口技术、设计出更为可靠的通信系统具有重要意义。
这不仅能够帮助我们充分发挥串口通信的优势,还能在不同应用场景中灵活应对各种通信需求,确保数据传输的稳定性和准确性。因此,深入学习和掌握串口通信的相关知识,是提升通信系统设计能力、实现高效数据传输的关键所在。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论