打印
[资料干货]

说明白了,Modbus RTU通信协议解析-成都电路板单片机开发

[复制链接]
82|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yonko|  楼主 | 2024-6-30 22:33 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
Modbus协议在工业控制、电气、电子领域是个很常见的一种通信协议,很多遇见的传感器、控制器、变频器、驱动器之类的基本都支持该协议,常见到什么程度呢,就是你看到的一个设备如果支持串口通信的,那么基本很多都内置了Modbus协议。
作为一个开发者,在做单片机、PLC、电路板、控制器/箱、仪器仪表、机电设备或系统、自动化、工控、传感、数据采集、自控系统、控制系统、物联网、电子产品、软件、APP项目过程中(业务联系:3w点yonko-tech点com,Q:2531二六三七26)也经常会使用到Modbus协议,所以不把此协议搞懂真就没法混。
本文介绍Modbus 协议中的Modbus RTU协议的相关知识,包括理论和案例,该协议常用于串口通信。
一、        Modbus RTU是什么?
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
其实,Modbus协议包含Modbus TCP,Modbus ASCII,Modbus RTU。Modbus TCP和Modbus ASCII本文不作深入描述,本文主要讲Modbus RTU。
概括地说,Modbus RTU是一种串行通信协议, 它关注于通信数据层面,主要是规定通信双方或者多方的每个数据帧发送和接收用什么样的数据格式。
一般来说,串行通信中传输的数据是一位一位(二进制位)地按照一定速率进行传输的。8位数据组成一个字节,Modbus RTU是以字节为最小基本单元定义数据格式的。若干个字节的数据组成数据帧,Modbus RTU协议就关注于这个数据帧里每个字节的数据该是怎样的。
二、        Modbus RTU协议与RS485、RS232、TTL等串口协议的关系是怎样
它们是不同的概念,侧重于不同方面。
先来看看RS485、RS232、TTL:RS485、RS232、TTL串口是串口通信中关于电气协议的通信协议,例如,包括用什么样的电压表示1、用什么样的电压表示0,起始位、停止位、波特率等是怎样的。具体如下:
TTL电平:全双工(逻辑1: 2.4V--5V 逻辑0: 0V--0.5V);
RS-232电平:全双工(逻辑1:-15V~-3V 逻辑0:+3V~+15V);
RS-485:半双工(逻辑1:+2V~+6V 逻辑0: -6V~-2V)这里的电平指AB 两线间的电压差。485由于是差分信号,具有数据传输远、抗干扰能力强、支持多机通信(由于是半双工)的优点。

再看Modbus RTU:Modbus RTU是软件层面的通信协议,它定义通信中的数据帧该是怎么样的格式,它关注于数据,也就是一个数据帧中每个字节该是怎样的数据。
概括地说,Modbus RTU和串口RS485、RS232、TTL是不同的概念,但是也有联系。Modbus RTU是数据层面的,规定通信的数据格式,RS485、RS232、TTL是物理层面的,它们规定了传输的电气协议,Modbus RTU协议需要运行在一定的通信载体(即电气协议,如RS485、RS232、TTL等)上。Modbus RTU在RS485、RS232、TTL串口上都能运行,常见的是在RS485上走Modbus RTU协议。
三、        Modbus RTU协议具体是怎样
Modbus RTU是主从通信模式,需要一个主机,一个或若干个从机。
Modbus RTU的数据帧一般包含:地址码、功能码、若干个数据码、校验码。帧与帧之间的时间间隔为3.5个字符,即假如两个数据传输位之间的时间间隔大于3.5个字符的时间,就会被认为新的一帧开始。一个Modbus RTU数据帧的组成如下:

3.1 Modbus RTU的地址码
地址码,用于定义和识别设备的地址,地址码存储空间为1个字节,所以其范围为0-255,其中0表示广播.
3.2 Modbus RTU的功能码和寄存器分区

常见的功能码有01、02、03、04、05、06、15、16等,分别表示着读线圈状态、读离散输入状态、读保持寄存器、读输入寄存器、写单个线圈、写单个保持寄存器、写多个线圈、写多个保持寄存器的功能。
寄存器分区:
线圈,可以看作是一个可读可写的位变量,Modbus RTU支持对其的读写操作。允许多位操作。
离散输入寄存器,可以看作是一个只读的位变量,Modbus RTU支持对其的读操作。
保持寄存器,可以看作是一个可读可写的字节变量,Modbus RTU支持对其的读写操作。允许多字节操作。一个保持寄存器为2个字节。
输入寄存器,可以看作是一个只读的字节变量,Modbus RTU支持对其的读操作。一个输入寄存器为2个字节。
寄存器地址:Modbus RTU的寄存器地址有00001~09999(0区,表示线圈寄存器)、10001~19999(1区,表示离散输入寄存器)、30001~39999(3区,表示输入寄存器)、40001~49999(4区,表示保持寄存器),其中3区和4区,每个寄存器由2个字节组成。
注意:在Modbus二进制数据指令里,表示寄存器地址的指令数据是从0开始的,Modbus RTU的寄存器地址是从1开始,注意对应关系。
用功能码是可以识别到Modbus寄存器分区的,所以在Modbus二进制数据指令里,是不填写分区代码的,这在第四、节的案例里可以看出对应关系。
3.3 Modbus RTU的数据位
Modbus RTU的数据位根据不同的功能码有不同的长度。
3.4 Modbus RTU的数据校验
Modbus RTU采用CRC-16校验,对一个数据帧里校验数据前面所有的数据进行CRC校验,得出的校验结果为2个字节,低字节在前(先发),高字节在后(后发)。
一个参考的单片机CRC计算C程序如下:
#include "crc16.h"
unsigned short modbus_crc_16(unsigned char *adata,unsigned int asize)        //CRC计算:计算结果为16位数据,CRC低字节在左,高字节在右
{
unsigned short crc_out=0xffff;
unsigned int i,j;
unsigned char crc_low,crc_high;
for(i=0;i<asize;i++)
        {
        crc_out^=adata;
        for(j=0;j<8;j++)
                {
                if ((crc_out&0x01)==0x01)
                        {
                        crc_out>>=1;
                        crc_out^=0xa001;
                        }
                else
                        {
                        crc_out>>=1;
                        }
                }
        }
//exchange high and low 8 bits
//业务联系:3w点yonko-tech点com, Q:二五三一26三七二六。
crc_low=(unsigned char)crc_out;
crc_high=(unsigned char)(crc_out>>8);
crc_out=(unsigned int)((crc_low<<8)+crc_high);
return crc_out;
}
四、        不理解吗?来点例子,Modbus RTU数据帧案例详解(重点)
为了更清晰地理解,本节介绍Modbus RTU的通信例子。本章节大部分内容引用自网络文献。
4.1 读取输出线圈状态
01功能码的作用是读取从站里输出线圈的状态,主站发送指令后从站响应并返回数据,返回的线圈数据由低位线圈到高位线圈,注意这里的线圈数量是表示有多少个二进制位。

关于CRC:
上图中从站返回的除了校验码的数据是0x11 0x 01 0x 04 0x cd 0x 6b 0x b2 0x 05,那么计算出来的CRC结果为0x 11 0x C3,其中0x 11是低字节,0x C3是高字节,那么完整的数据帧是:0x11 0x 01 0x 04 0x cd 0x 6b 0x b2 0x 05 0x 11 0x C3。CRC可以通过3.4节中的程序计算,或者使用网络上的CRC在线计算工具。
4.2 读取离散输入状态
    02功能码的作用是读取从站输入线圈的状态,主站发送指令后从站响应并返回数据,返回的线圈数据由低位线圈到高位线圈,注意这里的线圈数量也是表示有多少个二进制位。

4.3 读取保持寄存器
     03功能码的作用是读取从站保持寄存器的状态,主站发送指令后从站响应并返回数据,返回的寄存器数据由低位寄存器到高位寄存器,注意这里的每个寄存器有2个字节组成,寄存器先发低的再发高的,每个寄存器先发高字节,再发低字节。

4.4 读取输入寄存器
    04功能码的作用是读取从站输入寄存器的状态,主站发送指令后从站响应并返回数据,返回的寄存器数据由低位寄存器到高位寄存器,注意这里的每个寄存器有2个字节组成,寄存器先发低的再发高的,每个寄存器先发高字节,再发低字节。

4.5 强制单个线圈
05功能码的作用是设置从站的单个线圈值,主站发送指令后从站响应并返回数据。

4.6 强制多个线圈
0F功能码的作用是设置从站的多个线圈值,主站发送指令后从站响应并返回数据。

4.7 预置单个寄存器
06功能码的作用是设置从站的单个寄存器值,主站发送指令后从站响应并返回数据。

4.8 预置多个寄存器
10功能码的作用是设置从站的多个寄存器值,主站发送指令后从站响应并返回数据。

编程时,可以把Modbus RTU的线圈看作为位变量,寄存器看作为双字节变量(一个寄存器为2个字节,16位)。
可以看出,Modbus RTU是主从模式,是主站发出指令,从站响应,从站不能直接主动地向主站发出指令。
Modbus RTU基本可以在所有串行通信里面使用,但是Modbus RTU一般在RS485通信里使用得较多一些。
后续大可能会写单片机与昆仑通态触摸屏通信的实操,如有兴趣可以关注避免失误。
如有错误,感谢指正。本文有一部分资料来自网络资源,感谢其他大牛的分享,绿水青山,后会有期,全文暂时完。
沙鸥 成都 2024-6

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

115

主题

228

帖子

0

粉丝