1.需求
现在有8个小车它的的功能:
- 前进
- 倒退
- 加速 减速
- 停止
串口通信的协议(与嵌入式开发者进行协商):字头 小车编号 运动方式 速度 CRC效验码
- 字头 1字节 (规定20)
- 小车编号 1字节(8位 1位表示一个小车 最多只能表示8个小车)
- 运动方式 1字节 (01 前进 02倒退 03停止)
- 速度 1字节 (1-100)
- CRC效验码 2字节 (根据前面的所有数字进行效验)
当速度为0就表示停止
2.通过sscom串口工具测试协议是否可行
sscom的串口工具下载:https://www.aliyundrive.com/s/VEuGRQBc1fq
串口工具测试时不需要CRC效验码,测试主要用于看指令是否有效,为下一步编程纠错
sscom测试使用:
3.SpringBoot整合RXTXComm实现案例
3.1 SerialPort串口类交给IOC容器管理
步骤方法:
- 创建配置类@Configuration
- 获取系统接口列表
- 打开串口并设置监听器
- 将SerialPort放入到ioc容器中让spring管理
@Configuration @Slf4j public class RxtxcommConfig { @Value("${RXTX.baudRate}") int baudRate;//波特率 //打开端口 @Bean @Scope(value = "singleton")//单例 public SerialPort openSerialPort() throws PortInUseException { System.out.println(baudRate); //获取所有的串口 List<CommPortIdentifier> portSet = getPortSet(); //判断列表的接口是否是串口,如果是获取第一个串口就ok Optional<CommPortIdentifier> first = portSet.stream().filter(x -> x.getPortType() == CommPortIdentifier.PORT_SERIAL).findFirst(); //打开串口 SerialPort serialPort = openPort(first.get(),first.get().getName(),50); return serialPort; } //获取接口列表 private List<CommPortIdentifier> getPortSet(){ //系统端口列表 List<CommPortIdentifier> portList = new ArrayList<>(); //枚举类 Enumeration tempPortList; //串口管理和设置类 CommPortIdentifier portIdentifier; //得到系统中的端口枚举列表 tempPortList = CommPortIdentifier.getPortIdentifiers(); //遍历端口枚举列表 while (tempPortList.hasMoreElements()) { //把端口添加倒list集合中 portList.add((CommPortIdentifier) tempPortList.nextElement()); } return portList; } //串口打开方法 private SerialPort openPort(CommPortIdentifier portIdentifier, String portName, int delayed) throws PortInUseException { //打开并获取串口 SerialPort serialPort = (SerialPort)portIdentifier.open(portName, delayed); try { //设置串口参数(比特率、数据位、停止位、奇偶校验位) serialPort.setSerialPortParams(baudRate,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE); //设置监听器 addListener(serialPort); //通信中断时触发监听 serialPort.notifyOnBreakInterrupt(true); } catch (UnsupportedCommOperationException | TooManyListenersException e) { log.error("设置端口失败"); } return serialPort; } //设置异常报警侦听器 private void addListener(SerialPort serialPort) throws TooManyListenersException { serialPort.addEventListener(new RS485Listener()); }
设置初始化监听类(可选)
public class RS485Listener implements Runnable, SerialPortEventListener { @Override public void serialEvent(SerialPortEvent serialPortEvent) { switch (serialPortEvent.getEventType()) { case SerialPortEvent.BI://通信中断捕获 System.out.println("通信中断"); break; } } @Override public void run() { try { Thread.sleep(50);//每次收发数据完毕后线程暂停50ms } catch (InterruptedException e) { e.printStackTrace(); } } }
3.2RXTXCommService串口服务类
串口服务类需要的方法:
- 获取串口数据
- 向串口发送控制数据
其他功能可拓展:
- 拓展监听器
- 拓展发送其他控制数据
@Service public class RxtxcommService { @Autowired SerialPort serialPort;//获取串口 @Autowired RS485DataProcess rs485DataProcess; //发送数据协议化处理 //串口输出流 OutputStream out = null; //往串口发送小车控制数据 public void BaCarMoveSpeedsendToPort(String initial,//协议的字头 20表示小车控制 String serialnumber,//小车编号 String direction,//小车方向 01 前进 02 后退 String speed//小车速度 速度为00表示停止 ) { //获取发送数据 byte[] baCarMoveSpeed = rs485DataProcess.getBaCarMoveSpeed(initial, serialnumber, direction, speed); try { out = serialPort.getOutputStream(); out.write(baCarMoveSpeed); out.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } //读取串口数据 public byte[] readFromPort() { InputStream in = null; byte[] bytes = null; try { //给串口返回数据时间 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } try { in = serialPort.getInputStream(); // 获取buffer里的数据长度 int bufferlength = in.available(); while (bufferlength != 0) { // 初始化byte数组为buffer中数据的长度 bytes = new byte[bufferlength]; in.read(bytes); bufferlength = in.available(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } return bytes; } //有需要可以拓展添加监听器 //有需要可以拓展发送其他控制数据 }
3.3 RxtxcommDataProcess发送数据的协议化处理
协议化处理类的方法:对发送的数据进行协议化处理
@Component public class RxtxcommDataProcess{ //小车运行发送数据协议化处理 public byte[] getBaCarMoveSpeed(String initial,//协议的字头 20表示小车控制 String serialnumber,//小车车编号 String direction,//小车行驶 01 前进 02 后退 String speed//小车行驶速度 速度为00表示停止 ){ //行驶方向和行驶速度(<10)补零操作 if(Integer.parseInt(speed)<10){ speed="0"+speed; } direction="0"+direction; //获取小车16进制编号 String numberProtocolProcess = BaCarNumberProtocolProcess(serialnumber); //控制字头、编号、方向、速度进行拼接 String pjnumber = initial+numberProtocolProcess + direction + speed; //获取CRC16效验码 String crc16 = getCRC16(pjnumber); //最终发送数据拼接并且去空格,转化成byte数组返回 return RxtxcommUtil.toBytes((pjnumber + crc16).replace(" ", "")); } //可以拓展处理其他数据协议化 //对小车编号进行协议化处理 private String BaCarNumberProtocolProcess(String serialnumber){ //总共1个字节8位,每一位代表一个小车(最多只有8个小车) StringBuilder bianHaoBin = new StringBuilder("00000000"); if(!serialnumber.isEmpty()){ //按照,分割成数组 String[] splitlist = serialnumber.split(","); //补零 for (int i = 0; i < splitlist.length; i++) { //对应位置补1 bianHaoBin.replace(Integer.parseInt(splitlist[i])-1,Integer.parseInt(splitlist[i]), "1"); } } //将转化好的二进制字符串转化位16进制字符串 return RxtxcommUtil.bin2hex(bianHaoBin.reverse().toString()); } //CRC16效验码 private String getCRC16(String FasongData){ return RxtxcommUtil.getCRC16(RxtxcommUtil.toBytes(FasongData)); } }
3.4 RxtxcommUtil工具类
工具类的方法:
- 二进制字符串转化位16进制字符串
- 获取CRC16效验码
- 将16进制字符串转换为byte[]
public class RxtxcommUtil{ //一个字节包含位的数量 8 private static final int BITS_OF_BYTE = 8; //多项式 private static final int POLYNOMIAL = 0xA001; //初始值 private static final int INITIAL_VALUE = 0xFFFF; //二进制字符串转16进制 public static String bin2hex(String input) { StringBuilder sb = new StringBuilder(); int len = input.length(); for (int i = 0; i < len / 4; i++){ //每4个二进制位转换为1个十六进制位 String temp = input.substring(i * 4, (i + 1) * 4); int tempInt = Integer.parseInt(temp, 2); String tempHex = Integer.toHexString(tempInt).toUpperCase(); sb.append(tempHex); } return sb.toString(); } //将16进制字符串转换为byte[] public static byte[] toBytes(String str) { byte[] bytes = new BigInteger(str, 16).toByteArray(); return bytes; } public static String getCRC16(byte[] bytes) { // CRC寄存器全为1 int CRC = 0x0000ffff; // 多项式校验值 int POLYNOMIAL = 0x0000a001; int i, j; for (i = 0; i < bytes.length; i++) { CRC ^= ((int) bytes[i] & 0x000000ff); for (j = 0; j < 8; j++) { if ((CRC & 0x00000001) != 0) { CRC >>= 1; CRC ^= POLYNOMIAL; } else { CRC >>= 1; } } } // 结果转换为16进制 String result = Integer.toHexString(CRC).toUpperCase(); if (result.length() != 4) { StringBuffer sb = new StringBuffer("0000"); result = sb.replace(4 - result.length(), 4, result).toString(); } //高位在前地位在后 //String str = result.substring(2, 4) + " " + result.substring(0, 2); // 交换高低位,低位在前高位在后 String str = result.substring(2, 4) + " " + result.substring(0, 2); return str; }}
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109