文章目录

前言

上一篇中介绍了不同硬件层协议传输逻辑0和逻辑1的方式,也知道了软件层协议是需要依托这些硬件层的传输才能实现的。此篇将介绍Modbus的整个通信流程以及数据帧的结构。

Modbus协议硬件连接

根据之前的理论,我们知道Modbus作为一种上层协议,其底层其实可以是任意一种硬件层板级通信的协议UART、IIC、SPI亦或是现场级总线RS485、RS232、CAN总线这些来实现。

基于RS485的Modbus通信

笔者这里采用板级RS485进行描述,学习过232和485的同学肯定都有一个感觉,就是在编程的时候,单片机端的配置其实和普通的UART串口配置没什么太大的区别,以485为例,通过观察下面的代码可以看见,整个485初始化只是在串口初始化的基础上增加了一个接收和发送的模式控制。

//模式控制
#define RS485_TX_EN		PDout(7)	//485模式控制.0,接收;1,发送.
//如果想串口中断接收,请不要注释以下宏定义
#define EN_USART2_RX 	1			//0,不接收;1,接收.

上一篇中我们知道,单片机与485协议的设备通信时需要有硬件电路进行转换,通过观察下面这个硬件电路,可以看出在传统UART的TX、RX两个引脚上多了个RE,这个脚就是用来控制发送和接收过程的。因为UART通信是一种全双工的通信模式,通信双发都可以主动发送数据给对方;而RS485采用的是半双工的通信方式(也有全双工RS422),采用主从通信,同一时间只能主机发送或者只能从机发送,不能主机和从机同时发送,所以需要通过一个控制器来实现发送和接收控制。
SP3485的2号引脚为低电平接收使能,3号引脚为高电平发送使能。在这里我们将两个引脚连接在一起,只需要通过一个信号RS485_RE即可控制收发过程:当RS485_RE为高电平时,SP3485 处于发送状态;当RS485_RE为低电平时,SP3485处于接收状态。

RS485_RE 状态
1 发送
0 接收

Modbus拓扑结构

Modbus采用主从通信结构即:
1.从机不能主动发送数据;
2.系统中只有一个主机。
作为工业控制中一个通用的控制协议,Modbus理论上可以有248个节点(根据地址而定),但是实际使用中只能挂接32个节点(挂载设备数跟接口芯片的驱动能力有关,不唯一);这些节点之间只有唯一的一个主机,其余都是从机,连接示意图如下:

Modbus通信流程

1.系统开机后,所有设备都进入接收模式(监听状态);
2.主机根据需求发送数据包到数据总线上,发送完毕后释放总线,重新进入接收模式;

3.从机检测总线状态并接收数据包;

4.从机判断主机发送数据包是否与自己的地址对应;
(1)地址对应就对数据包进行CRC校验,CRC校验通过就开始处理主机发送的消息,并按照约定的格式给主机打包数据并发送,如果CRC校验不通过,则说明数据丢失或者接收异常,需要反馈给主机错误信息;

(2)地址不对应,不对此数据包做处理也不需要回应主机;
5.如果主机发送的数据后超过设定的时间没有回应则说明从设备掉线,需要检查地址是否正确以及线路连接是否正常。

Modbus主机帧结构

Modbus有两种传输方式,一种是RTU,另外一种是ASC。

传输方式

RTU传输方式

RTU是以16进制形式(或者说是2进制方式)传输数据,例如需要发送 “0x30”这个数据,在485传输初始化是定义的是8位数据位一位停止位,无校验位,其发送格式如下:

ASC传输方式

而ASC传输则是采用的ASCLL码表的形式,例如需要传输"30"则实际发送的数据是‘3’和’0’的ascll码表的值及0x33和0x30。其传输形式如下:

通过上面两个例子可以看出,ASCLL的传输方式明显比RTU的传输方式数据量大,传输效率肯定不如RTU方式,但是ASCLL得方式可以很方便调试,调试时只需要将PC接入总线,就可以实时获取数据包内容,进而进行分析,所以ASCLL的传输方式常用于调试用。而RTU则作为实际生产所用。

数据帧格式

ASCII 帧

用 ASCII 模式, 消息以冒号( :) 字符( ASCII 码 3AH) 开始, 以回车换行符结束( ASCII 码 0DH,0AH)。
其它域可以使用的传输字符是十六进制的 0…9,A…F。 网络上的设备不断侦测“ :” 字符, 当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。消息中字符间发送的时间间隔最长不能超过 1 秒,否则接收的设备将认为传输错误。

RTU 帧

用 RTU 模式,消息发送至少要以 3.5 个字符时间的停顿间隔开始。 在网络波特率下多样的字符时间,这是最容易实现的(如下图的 T1-T2-T3-T4 所示)。传输的第一个域是设备地址。可以使用的传输字符是十六进制的 0…9,A…F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收
到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少 3.5 个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。
整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过 1.5 个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于 3.5 个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC 域的值不可能是正确的。

此模式没有类似ASCLL模式的固定的起始位和结束位,而是通过时钟周期来判断发送开始和结束的,当开始接收数据时定时器开始准备计时,当接收完毕后定时器开始计时,当时间超过3.5个字节接收周期后则认为接收完毕。
例如当波特率为9600bit/s时,每位数据传输的时间t为:
t=1s/9600bit=104us
则传输一个字节数据所用时间:(8位数据位加1位停止位)
T=t9=936us约等于1ms时间,
那么3.5个字节所需时间T1=3.51=3.5MS;
也就是说,在接受不到数据后开始计时,记录没有数据传输的时间,如果大于3.5ms(3.5个字符周期)则接收完毕,如果小于3.5ms则说明此帧数据有误,丢弃不做处理。

设备地址(找谁)

modbus中一共有0~247个地址,其中0号地址作为广播地址,即主机向0号地址发送数据时,所有从设备都需要接收此数据包(从设备不需要回应)。

功能码(干什么)

modbus中一共有128个功能码,每个功能码都有其对应的功能,功能码不同后面的数据位的定义也各不相同,常用的几个功能码如下所示,需要进一步了解的可以自己去搜索详细介绍。

校验

数据包的传输过程中一般都会包括校验位,目的是为了验证数据包的正确性,在打包前主机会对数据位按照约定进行计算,得到一个校验位,并放在包内指定位置,
从机接收到数据包后也会再按照约定计算一遍数据位得到一个校验码,此时对比两个校验位如果一致则说明数据包正确,如果不一致则说明数据有误,这个数据包应该被丢弃。
可以发现acsll方式和RTU方式的校验方法不同,一个是使用的LRC校验,另外一个是CRC校验,有关两种校验方式的具体实现形式如下,可以参考,实际使用中已经有前辈做好了封装,可以直接调用。

CRC-16(循环冗余错误校验)

CRC-16 校验字节的步骤如下:
①装如一个 16 位寄存器,所有数位均为 1。
②该 16 位寄存器的高位字节与开始 8 位字节进行“异或” 运算。运算结果放入这个 16 位寄存器。
③把这个 16 寄存器向右移一位。
④若向右(标记位) 移出的数位是 1, 则生成多项式 1010000000000001 和这个寄存器进行“异或” 运
算;若向右移出的数位是 0,则返回③。
⑤重复③和④,直至移出 8 位。
⑥另外 8 位与该十六位寄存器进行“异或” 运算。
⑦重复③~⑥,直至该报文所有字节均与 16 位寄存器进行“异或” 运算,并移位 8 次。
⑧这个 16 位寄存器的内容即 2 字节 CRC 错误校验,被加到报文的最高有效位。
另外,在某些非 ModBus 通信协议中也经常使用 CRC16 作为校验手段,而且产生了一些 CRC16 的变种,
他们是使用 CRC16 多项式 X↑ 16+X↑ 15+X↑ 2+1,单首次装入的 16 位寄存器为 0000;使用 CRC16 的反序
X↑ 16+X↑ 14+X↑ 1+1,首次装入寄存器值为 0000 或 FFFFH。

LRC(纵向冗余错误校验)

LRC 错误校验用于 ASCII 模式。这个错误校验是一个 8 位二进制数,可作为 2 个 ASCII 十六进制字节传送。把十六进制字符转换成二进制,加上无循环进位的二进制字符和二进制补码结果生成 LRC 错误校验。 这个 LRC 在接收设备进行核验, 并与被传送的 LRC 进行比较, 冒号(: ) 、 回车符号( CR) 、
换行字符( LF)和置入的其他任何非 ASCII 十六进制字符在运算时忽略不计。
LRC校验码=~(地址+功能码+数据1+…+数据n)%256+1
实际上LRC校验码是一个0-255的数据。

总结

有关modbus的通信流程就介绍到此,如有问题欢迎指正,后面还会再出一篇基于STM32的RTU通信实例。

合集

STM32 Modbus通信学习笔记——理论基础
STM32 Modbus通信学习笔记——通信流程
STM32 Modbus通信学习笔记—— 代码及示例
STM32 使用MODBUS与维控屏通信(modbus系列代码)