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