485通讯与MODBUS的区别与联系
最近做智能检测的项目,设备、串口之间的通讯比较多,一会儿485,一会儿modbus RTU,有点晕了,这里重新梳理一下RS485、485通讯协议、ModBus通讯协议、Modbus Rtu通讯这几个点的联系和区别。
先说RS485吧,RS485是一个工业常用的物理接口,是物理层的硬件设备(串口)。是在电脑设置上看得到的(windows系统-我的电脑-设备管理器)设备。
Modbus是一种国际标准的通讯协议,一般用于不同厂商设备之间进行数据交换,是应用层的软件协议,不能自己单独存在,必须要依赖于硬件设备作为实现平台。不同的硬件具有不同的电气特性和连接方式,比如RS232、RS485。可以把Modbus比作英语,RS232比作印度人,RS485比作美国人,印度人之间讲英语时,理解为基于RS232的modbus通信,美国人之间讲英语时,理解为基于RS485的modbus通信。
两台设备之间通过Modbus协议传输数据,需要硬件接口,于是就有了RS232\RS422\RS485等,其中RS485传输距离远,成为现在最主流的工业现场硬件接口。
Modbus按照一般维度分为Modbus Rtu、Modbus ASCII、Modbus TCP三种模式。
从接口类型维度来说,Modbus Rtu和ASCII基于串行链路,属于串行通讯口(serial port),Mobus tcp基于TCP/IP协议,属于网络(以太网)通讯口。
协议分为硬件协议和软件协议。而通讯协议属于软件协议,它包含报头包围的格式,MODBUS是应用层的通讯协议,主要用于传送和接收文件包的格式。而RS232,RS485是物理层的串行接口,它可以支持几十种通讯协议,MODBUS只是其中的一种。
Modbus的特征:(1) 主从协议方式(master/slave)
(2) 通讯规约:初始结构-地址码-功能码-数据区-校验码-结束结构
(3) modbus协议本质是对寄存器读写,通过指定寄存器地址,来交换数据
写了一大堆概念性的东西,现在在面对实际问题,以下是供应商给出来的一句话:
大概需求是我需要通过485串口,给供应商的设备发送一个速度信号,这个速度信号的格式要求,是485格式,波特率、奇偶校验、8个数据位这些就不用再说了,了解下串口基础就知道了,一般都是N81,即“无奇偶校验、8个数据位、1个停止位”,问题的关键在于这个“485格式”。RS485做为一个标准串口通讯,是有自己的标准通信格式的。
可以看到,跟modbus是有些相似的,除了自己的格式外,也可以选择遵循Modbus通讯规约,更多的是使用modbus RTU模式通讯格式。
上图是modbus rtu的通讯规约,接下里的任务,是搞懂485通讯规约和modbus rtu通讯规约的区别,两种格式到底区别在哪里。为什么供应商选择用485格式,而不是使用的更广泛的modbus rtu?这两者之间的优缺点又分别是什么?
补充1:
所谓rs485通讯协议,属于硬件层协议。硬件层管什么呢?决定数据如何传输,比如2进制的数只有0和1,那么如果1个字节现在是'0110 0011',现在这个字节里的0怎么传输和表达,1怎么传输和表达,比如rs232就规定了电压x伏就表示0,y伏表示1。再比如要选择多少条线来传输,选择什么材质的线来传输,这就是硬件层协议规定的事情。
同样RS485通讯协议也是个硬件层协议,实际上是关于电平、电压的约定。搞软件的不一定看得懂,特别是应用层软件。
比如这样
,再比如这样
,亦或是这样
反正我是看不懂的
但是我弄清楚了什么叫所谓的RS485通讯协议,是硬件的、关于电平、电压的,一个规约,跟应用软件没半毛钱关系。
补充2(2020.12.14):
起始我纠结了半天,就是被串口通讯和modbus rtu搞晕了,一周之后回过头来看,就显得有点笨,但是还是硬着头皮补充完整吧。
串口通讯和Modbus rtu的有哪些区别,又有哪些共同点呢?
从概念上来说,都是串口,rtu起始就是基于串口的,所以共同点就是,rtu和串口通讯,都要设置波特率、数据位、停止位、奇偶校验等
// 波特率常量
private final static int BAUD_RATE = 9600;
// 广播地址
// private final static int broadcast_SLAVE_ADDRESS = 0;
// 数据位
private final static int DATABITS_8 = 8;
// 停止位
private final static int STOPBITS_1 = 1;
// 奇偶校验
private final static int PARITY_NONE = 0;
不同点是,串口通讯需要串口对象,而modbus需要从机地址(设备地址),如果是广播模式就需要广播地址,一般是0。
// MODBUS的设备(从机)地址。 默认为254, 但由于本设备不支持254的地址,所以需要修改设备的地址为1.
private final static int SLAVE_ADDRESS = 1;
// 广播地址
private final static int broadcast_SLAVE_ADDRESS = 0;
下面贴一段经典的,串口程序,第一段是打开串口,第二段是往串口发送数据的
public static final SerialPort openPort(String portName, int baudrate) throws PortInUseException {
try {
// 通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
// 打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
// 判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
try {
// 设置一下串口的波特率等参数
// 数据位:8
// 停止位:1
// 校验位:None
serialPort.setSerialPortParams(baudrate, SerialPortManager.DATABITS_8, SerialPortManager.STOPBITS_1,
SerialPortManager.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
return serialPort;
}
} catch (NoSuchPortException e1) {
e1.printStackTrace();
}
return null;
}
入参是串口名称和波特率。返回值是实体对象。
public static void sendToPort(SerialPort serialPort, byte[] order) {
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
out = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
入参是串口对象实体,即第一段打开串口代码的返回值。用的是outPutStream,输出流。
而modbus rtu是怎么实现串口打开和发送呢,起始modbus只是基于串口,本质上还是主从机之间的通讯。
// 实例化ModBus工厂
ModbusFactory modbusFactory = new ModbusFactory();
ModbusMaster master = modbusFactory.createRtuMaster(serialParameters);
跟SerialPort实体类似,ModbusMaster是个主机对象,当你将其init()的时候,在modbus角度你开启了主机,其实就是打开了串口。
modbus的发送不用赘述,基本就是遵循modbus的格式,功能码、寄存器地址、寄存器数量、数据位等等。例如16功能码,代表写入多个连续保持寄存器,支持广播模式。
这是16码的格式: