vesperW · 1 天前

手把手带你搞懂 Modbus 通信协议

Modbus 算是嵌入式领域比较经典一种通信协议,不管你是初学者,还是工作多年的工程师,都很有必要了解一下。

1. 什么是 Modbus?

顾名思义 ,它是一个 Bus,即总线协议。比如串口协议、IIC 协议、SPI 都是通信协议。你接触到这种协议,相信你所处的行业是工业电子方面或者你的产品用于工业。

好了,现在知道了大概知道了,这是一个总线协议,是一个 Mod 什么的公司发表的一个通信协议。那为什么要用这个呢? 答案就是他们都在用,你就得学,啊哈哈!

正经的说,它被工业领域所接受的原因是它具备一下三个优点

  • 公开发表并且无版权要求
  • 易于部署和维护
  • 对供应商来说,修改移动本地的比特或字节没有很多限制

简单的概括,就是免费+简单+方便修改 !

归纳:Modbus 就是一种用在工业上的简单协议!

2. Modbus 用来干什么?

用两个字概括:通信

是的,所有协议都是用来通信的,协议的制定就是让两个人根据这个协议看懂传来的一组数据。比如我给你一个6666 ,你要是没有协议,就只知道这是6666 ,而有了协议,你就知道了这是在问我是不是 大佬? 当然,也可以表示其他意思,具体什么意思就看你协议怎么制定!

归纳:Modbus 用来通信喽,是个人都知道!

3. Modbus 的内容是什么?

大致分为以下几种:

  • Modbus-RTU
  • Modbus-ASCII
  • Modbus-TCP

以上三种协议,一个设备只会有一种协议,如果你的设备使用的是 Modbus-RTU,只需查看以下对应部分,一般来说大部分的设备都是 Modbus-RTU 协议的。

4. 通讯过程

Modbus 是主从方式通信 ,也就是说,不能同步进行通信,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信。(所以说,这也算是一个缺点了)

举例 1 : 一个总线上有一个主机,多个从机,主机查询其中一个从机,首先你必须得这些从机分配地址(这样才能知道哪个从机,而且每个地址必须唯一),分配好地址后,主机要查询,然后数据下发(数据内容下面会介绍),从机得到主机发送的数据,然后对应地址的从机回复,主机得到从机数据,这样就是一个主机到从机的通信过程,是不是很简单呢?

举例 2 : 就像打电话,你得知道对方的电话(这就是唯一地址),然后你打电话过去,相当于主机查找从机,然后对方接通电话,给你回复(返回数据),正常是这样的。

如果这时候,对方正在打电话,你应该听到的是"sorry,you… "这一串英文,说明对方忙,但是 Modbus 总线 不能判断对方是否忙,也没有对应的仲裁机制,好了你又知道了一个缺点了!但是,你可以用软件的办法进行适当的处理数据!

5. Modbus-RTU 协议

设备必须要有 RTU 协议!这是 Modbus 协议上规定的,且默认模式必须是 RTU,ASCII 作为选项 。(也就是说,一般的设备只有 RTU 这个协议,ASCII 一般很少)所以说,一般学习 Modbus 协议,只需要了解 RTU 的协议,ASCII 作为学习的了解就足够了。

5.1 帧结构

帧结构 = 地址 + 功能码+ 数据 + 校验

  • 地址 : 占用一个字节,范围 0-255,其中有效范围是 1-247,其他有特殊用途,比如 255 是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复)。
  • 功能码 :占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能。
  • 数据 :根据功能码不同,有不同结构,在下面的实例中有说明。
  • 校验 :为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了。

5.2 实战

只谈理论大家可能不太明白,下面举一个例子。Modbus-RTU 协议一般我们用的最多功能码就是 03 和 06 ,大部分都是用 modbus 来查询传感器上的信息用 03 查询功能码,如果需要修改传感器寄存器的值就用 06 修改功能码,其他的不需要过多关注,学多了你也记不住,哈哈哈!

2.1 查询功能码 0x03

功能描述:现在我是主机,我要查询从机地址为 01 的数据。我现在用电脑的 modbus 调试助手来代替主机,stm32 来代替从机。

我需要发送以下数据:

主机发送: 01 03 00 00 00 01 84 0A
从机回复: 01 03 02 19 98 B2 7E

那么这一组数据是什么意思呢?

从上面的结构图中,可以看出,主机发送的数据大致是地址+功能码+数据+校验;

所以解析如下:

发送数据解析

01-地址,也就是你传感器的地址
03-功功能码,03代表查询功能,查询传感器的数据
00 00-代表查询的起始寄存器地址.说明从0x0000开始查询。这里需要说明以下,Modbus把数据存放在寄存器中,通过查询寄存器来得到不同变量的值,一个寄存器地址对应2字节数据
00 01-代表查询了一个寄存器.结合前面的00 00,意思就是查询从0开始的1个寄存器值
84 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到84前面为止;
回复数据解析

01-地址,也就是你传感器的地址
03-功功能码,03代表查询功能,查询传感器的数据。这里要注意的是注意发给从机的功能码是啥,从机就要回复同样的功能码,如果不一样说明这一帧数据有错误
02-代表后面数据的字节数,因为上面说到,一个寄存器有2个字节,所以后面的字节数肯定是2*查询的寄存器个数;
19 98-寄存器的值是19 98,结合发送的数据看出,01这个寄存器的值为19 98
B2 7E-循环冗余校验

好了,是不是很简单呢?基本流程就是:

  • 发送 :从机的地址+我要干嘛的功能码+我要查的寄存器的地址+我要查的寄存器地址的个数+校验码
  • 回复 :从机的地址+主机发我的功能码+要发送给主机数据的字节数+数据+校验码

就是这么简单!

2.2 修改功能码 0x06

如果我要修改从机的数据呢?那么这个协议有吗?当然有,那就是 0x06

1、修改—0x06 功能码

主机发送: 01 06 00 00 00 01 48 0A
从机回复: 01 06 00 00 00 01 48 0A

诶,看上去怎么一样的啊?是不是错了?答案是这是正确的;

发送数据解析

01-主机要查询的从机地址
06-功能码,06代表修改单个寄存器功能,修改有些不同,有修改一个寄存器和修改多个寄存器;
00 00-代表修改的起始寄存器地址.说明从0x0000开始.
00 01-代表修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
48 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到48前面为止;
回复数据解析

01-从机返回给主机自己的地址,说明这就是主机查的从机
06-功能码,代表修改单个寄存器功能,主机发啥功能码,从机就必须回什么功能码;
00 00-代表修改的起始寄存器地址.说明是0x0000.
00 01-代表修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
48 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到48前面为止;

如果回复的一样,说明这个数据是修改成功的;如果功能码不是 06,而是别的,说明从机回复的数据有误,主机可以做相应的处理。

2、修改-0x10 功能码

如果我要修改多个寄存器,难道用 06 发好几次,这样不会太傻了吗?所以 Modbus RTU 协议包含了修改连续多个寄存器的方法,就是功能码为 0x10;这个大家自己去查询,基本和上面的数据格式差不多。

归纳

Modbus-RTU 协议只需要看懂功能码 0x03 、0x06 、0x10 这三个基本的就已经足够了,分别回想下其数据域部分:

0x03—主机需要发送起始地址 +寄存器数量 ,从机回复总字节数 +数据 ;

0x06—主机发送起始地址 +数据内容 (因为你只需要修改一个,所以起始地址就是所要修改的地址),从机返回起始地址 +数据内容 (发现居然一样!)

0x10—主机发送起始地址 +寄存器个数 +总字节数 +数据 ,从机返回起始地址 +寄存器数量

6. Modbus-ACSII 协议

一般只需要了解 RTU 协议,因为前面有说过,必须要有 RTU 协议的,所以只需要了解了 RTU 协议,就可以读出设备信息了,至于 ACSII 协议,做个大概了解即可。

6.1 帧形式

对于 RTU 协议,比如 RTU 发送一个字节:0x12 ;ASCII 协议则需要发送 2 个字节:一个字节代表 ASCII 码 1 ,一个代表 ASCII 码 2 ,即 0x31 和 0x32 ,才能代表 0x12 。所以,ASCII 协议的效率比较低。但是 ASCII 更符合串口打印查看,因为串口发送的数据一般都是文本模式(ASCII)。

比如用 RTU 方式,也叫 16 进制方式,要发 0x03 数据,RTU 方式就发送 00000011 。用 ASCII 发送 0x03 ,就要发送 0 的 ASCII 码 0x30 和 3 的 ASCII 码 0x33 ,对应到 2 进制也就是发送 00110000 和 00110011 。很显然 RTU 方式只需要发送 8 位就可以了(加上起始位和停止位就是 10 位数据)。那么 ASCII 码方式发送就需要两个 8 位(每个 8 位分别加上起始位和停止位就是 20 位数据)。也就是说 ASCII 码发送数据量是 RTU 方式的 2 倍,所以 ASCII 码效率更低。

那么 ASCII 码效率更低,数据发送量大为啥还采用这种方式呢?

因为假如你要发送数据 0x03 ,采用 RTU 方式(16 进制发送),计算机中端设备接收到 0x03 后是不可以显示的,就是不能把 0x03 打印出来。因为可见字符的 ASCII 码是从 32—126 ,不是这个范围以外的显示屏上都看不到,会出现乱码,如果是串口助手的话就会显示 □□□□。 如果采用 ASCII 方式(文本模式发送),就不会出现不可显示和乱码的情况,因为文本模式发送 0x03,就是发送 ASCII 码 0 和 ASCII 码 3,也就是 0x30 和 0x33 ,是可以正常显示在计算机中端的。所以现在知道为什么还要使用 ASCII 效率比较低的方式发送了吧,只是为了方便调试显示而已。

从上面的图中,看出:

1)比 RTU 多了起始段: ,多个结束符 CR ,LF
2)地址和功能都变成了 2 个字节 ;
3)数据部分更加繁琐,但是更符合人们的查看;

6.2 归纳

由于 Modbus-RTU 和 Modbus-ACSII 都是基于 232 和 485 链路的,所以其通讯模式半双工,一般是主机和从机的模式。其差别就是其字节的格式不同,一个是 16 进制的数据,一个是 ASCII 数据。ASCII 多了帧头和帧尾,也就是说可以有用这个头尾判断一帧字节来判断是否结束;而 RTU 没有帧头和帧尾,所以协议里明确两帧之间要大于 3.5 个字节时间间隔,作为一帧结束的判断依据。对于 RS485 来说,总线上一般允许最大 32 个设备。

7. 备注

最后再补充点:Modbus 从设备的回应数据格式是:1、回应的数据包与主机查询的数据包格式一致。从机正常回应时:功能码与主机发送的功能码一致(1-127) 如果异常回应时:功能码要在收的主机的功能码的基础上加 128。不要问为啥加 128,你去问造协议的那一帮人吧!

因为电脑只支持 USB,所以我们需要把 USB 转 TTL 串口,再转成 485 接口之后与单片机相连,这是硬件最基本的,但是也需要注意。

最后修改了一下串口调试助手,加了一个 CRC 校验的功能,就可以获取。

END

来源:strongerHuang

推荐阅读

欢迎大家点赞留言,更多 Arm 技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。

推荐阅读
关注数
2921
内容数
358
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息