一、需求说明
将仪表和计算机串口相连,计算机通过软件向仪表发送指令,然后仪表返回结果,在计算机软件界面上显示。
二、实现过程
1、查看仪表说明书
仪表型号为XK3190-A9,主要是查看相关参数(波特率、数据位、停止位、奇偶校验),通讯方式及指令规则。
2、使用串口通讯工具、串口监听工具进行调试
网上可以下到很多相关软件,主要是方便测试。串口通讯工具、串口监听工具(AccessPort)
3、将仪表“通讯方式”改成“指令模式”
根据说明书,在仪表上输入:打印设置--->98--->输入--->....
4、java编码(重点)
(1)使用comm在java程序中管理本地端口,目前,常见的Java串口包有SUN在1998年发布的串口通信API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一个开源的实现。鉴于在Windows下SUN的API比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信API在Windows平台下的使用。
(2)附件中“javacomm20-win32.zip”
(3)解压该压缩包,从commapi\Readme.html开始读起
(4)Copy win32com.dll
to your <JDK>\bin
directory.
(5)Copy comm.jar、javax.comm.properties
to your <APP>\lib
directory.
(6)研究java.comm包中的相关类及功能
----javax.comm.CommPortIdentifier 这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。
----javax.comm.SerialPort 这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作。
(7)开始编写自己的代码
##### 串口参数配置(配置到spring容器里) #####
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean class="self.phoenix.application.SerialParameters"> <!-- 端口名称 --> <property name="portName"><value>COM1</value></property> <!-- 波特率 --> <property name="baudRate"><value>4800</value></property> <!-- 输入流控制 --> <property name="flowControlIn"><value>0</value></property> <!-- 输出流控制 --> <property name="flowControlOut"><value>0</value></property> <!-- 数据位 --> <property name="databits"><value>8</value></property> <!-- 停止位 --> <property name="stopbits"><value>1</value></property> <!-- 奇偶校验 --> <property name="parity"><value>0</value></property> </bean> </beans>
#####用于处理注册驱动、打开端口、发送指令、关闭端口及释放资源#####
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.comm.CommDriver; import javax.comm.CommPortIdentifier; import javax.comm.NoSuchPortException; import javax.comm.PortInUseException; import javax.comm.SerialPort; import javax.comm.UnsupportedCommOperationException; import self.phoenix.application.SerialConnectionException; import self.phoenix.application.SerialParameters; public class SerialConnUtils { private OutputStream os= null; private InputStream is = null; private CommPortIdentifier portId = null; private SerialPort sPort = null; private SerialParameters parameters = null; private CommDriver commDriver = null; public SerialConnUtils(SerialParameters parameters) { this.parameters = parameters; if(commDriver == null){ try { commDriver = (CommDriver) Class.forName("com.sun.comm.Win32Driver").newInstance(); commDriver.initialize(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } public void openConnection() throws SerialConnectionException { // Obtain a CommPortIdentifier object for the port you want to open. try { portId = CommPortIdentifier.getPortIdentifier(parameters.getPortName()); } catch (NoSuchPortException e) { throw new SerialConnectionException(e.getMessage()); } // Open the port represented by the CommPortIdentifier object. Give // the open call a relatively long timeout of 30 seconds to allow // a different application to reliquish the port if the user // wants to. try { sPort = (SerialPort)portId.open("SerialDemo", 30000); } catch (PortInUseException e) { throw new SerialConnectionException(e.getMessage()); } try { setConnectionParameters(); } catch (SerialConnectionException e) { sPort.close(); throw e; } // Set receive timeout to allow breaking out of polling loop during // input handling. /*try { sPort.enableReceiveTimeout(30); } catch (UnsupportedCommOperationException e) { }*/ } public void setConnectionParameters() throws SerialConnectionException { // Save state of parameters before trying a set. int oldBaudRate = sPort.getBaudRate(); int oldDatabits = sPort.getDataBits(); int oldStopbits = sPort.getStopBits(); int oldParity = sPort.getParity(); int oldFlowControl = sPort.getFlowControlMode(); // Set connection parameters, if set fails return parameters object // to original state. try { sPort.setSerialPortParams(parameters.getBaudRate(), parameters.getDatabits(), parameters.getStopbits(), parameters.getParity()); } catch (UnsupportedCommOperationException e) { parameters.setBaudRate(oldBaudRate); parameters.setDatabits(oldDatabits); parameters.setStopbits(oldStopbits); parameters.setParity(oldParity); throw new SerialConnectionException("Unsupported parameter"); } // Set flow control. try { sPort.setFlowControlMode(parameters.getFlowControlIn() | parameters.getFlowControlOut()); } catch (UnsupportedCommOperationException e) { throw new SerialConnectionException("Unsupported flow control"); } } /** * 向端口发送十六进制指令 02 41 44 30 35 03 * 返回:02 41 44 2B 30 30 30 35 36 36 31 31 41 03 * @return 净重 * @throws SerialConnectionException */ public double sendSerialCommand() throws SerialConnectionException { //十六进制指令 byte[] baKeyword = new byte[6]; baKeyword[0] = 0x02; baKeyword[1] = 0x41; baKeyword[2] = 0x44; baKeyword[3] = 0x30; baKeyword[4] = 0x35; baKeyword[5] = 0x03; //净重 Double netWeight = 0.0; try { //向串口发送指令 os = sPort.getOutputStream(); os.write(baKeyword, 0, baKeyword.length); //用于从串口读数据,返回14字节的信息 byte[] readBuffer = new byte[14]; int numBytes = 0; is = sPort.getInputStream(); numBytes = is.read(readBuffer); System.out.println(new String(readBuffer)); //4~12为数据内容 String value = Hex2StringUtils.byte2HexStrByi(readBuffer,4,12); if (value != null && !value.trim().equals("")) netWeight = Double.valueOf(value) / 1000; } catch (IOException e) { e.printStackTrace(); } return netWeight; } public void closeConnection() { // Check to make sure sPort has reference to avoid a NPE. if (sPort != null) { try { // close the i/o streams. os.close(); is.close(); } catch (IOException e) { System.err.println(e); } // Close the port. sPort.close(); //Release resources portId = null; } } }
###### ACSII与16进制转换的Utils类 #####
import java.io.ByteArrayOutputStream; import java.io.IOException; public class Hex2StringUtils { public static void main(String[] args) throws IOException { } /* * 16进制数字字符集 */ private static String hexString = "0123456789ABCDEF"; /* * 将字符串编码成16进制数字,适用于所有字符(包括中文) */ public static String encode(String str) { // 根据默认编码获取字节数组 byte[] bytes = str.getBytes(); StringBuilder sb = new StringBuilder(bytes.length * 2); // 将字节数组中每个字节拆解成2位16进制整数 for (int i = 0; i < bytes.length; i++) { sb.append(hexString.charAt((bytes[i] & 0xf0) >> 4)); sb.append(hexString.charAt((bytes[i] & 0x0f) >> 0)); } return sb.toString(); } /* * 将16进制数字解码成字符串,适用于所有字符(包括中文) */ public static String decode(String bytes) { ByteArrayOutputStream baos = new ByteArrayOutputStream( bytes.length() / 2); // 将每2位16进制整数组装成一个字节 for (int i = 0; i < bytes.length(); i += 2) baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString .indexOf(bytes.charAt(i + 1)))); return new String(baos.toByteArray()); } public static String byte2HexStr(byte[] b) { String hs = ""; String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = (Integer.toHexString(b[n] & 0XFF)); if (stmp.length() == 1) hs = hs + "0" + stmp; else hs = hs + stmp; // if (n<b.length-1) hs=hs+":"; } return hs.toUpperCase(); } public static String byteHexStr(byte b) { String hs = ""; String stmp = ""; // /for (int n = 0; n < b.length; n++) { stmp = (Integer.toHexString(b & 0XFF)); if (stmp.length() == 1) hs = "0" + stmp; else hs = stmp; // if (n<b.length-1) hs=hs+":"; // } return hs.toUpperCase(); } public static String byte2HexStrByi(byte[] b, int start, int end) { String hs = ""; String stmp = ""; for (int n = start; n < end; n++) { stmp = (Integer.toHexString(b[n] & 0XFF)); if (stmp.length() == 1) hs = hs + decode("0" + stmp); else hs = hs + decode(stmp); // if (n<b.length-1) hs=hs+":"; } return hs.toUpperCase(); } }
##### 调用函数,取得净重值 #####
public double getSerialPortNetWeight() { double netWeight = 0; SerialConnUtils serialConnUtils = null; try { //调用串口通讯工具类 serialConnUtils = new SerialConnUtils(parameters); //打开连接 serialConnUtils.openConnection(); //发送指令,并取得返回值 netWeight = serialConnUtils.sendSerialCommand(); } catch (Exception ex) { ex.printStackTrace(); } finally { //释放资源 serialConnUtils.closeConnection(); } return netWeight; }
5、在串口监听工具配合下进行软件调试
129 [00000000] IRP_MJ_CREATE Port Opened - javaw.exe 130 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 131 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 132 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 9600 133 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 134 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 9600 135 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 136 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 137 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 138 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 139 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 140 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 141 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 142 [00000000] IRP_MJ_WRITE Length: 0006, Data: 02 41 44 30 35 03 143 [00000013] IRP_MJ_READ Length: 0014, Data: 02 41 44 2B 30 30 30 35 36 36 31 31 41 03 144 [00001356] IRP_MJ_CLOSE Port Closed 145 [00000000] IRP_MJ_CREATE Port Opened - javaw.exe 146 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 147 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 148 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 9600 149 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 150 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 9600 151 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 152 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 153 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 154 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 155 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 156 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 4800 157 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: No, DataBits: 8 158 [00000000] IRP_MJ_WRITE Length: 0006, Data: 02 41 44 30 35 03 159 [00000013] IRP_MJ_READ Length: 0014, Data: 02 41 44 2B 30 30 30 36 39 36 31 31 36 03 160 [00000349] IRP_MJ_CLOSE Port Closed
Length:0006, Data:02 41 44 30 35 03 为发送的十六进制指令
Length:0014, Data:02 41 44 2B 30 30 30 36 39 36 31 31 36 03 为返回的十六进制内容
三、总结:调试过程中,串口监听工具起着很重要的作用。