【超多实例】嵌入式外设 —— 搞懂modbus该怎么用

〇 写在前面

  本人本科硕士皆是天坑专业,后自学转行去做了嵌入式软件开发,曾在华为和大疆等公司任职。
  在转行自学的过程中,困难艰辛不必多说,幸运的是得到了网上很多大佬的帮助。现在不敢说小有所成,也算是偶有心得,回顾一路以来的困顿苦恼,决定把自己的一些经验拙见写成博客带给大家,希望能够帮助大家。也欢迎大家积极留言互动问问题,看到消息都会回复的。
  最后,如果觉得文章有用或者能够帮助到你,麻烦点个赞给个关注支持一下,你的点赞关注是对我的最大肯定和支持,也是我不断创作的动力,同时也让我满足一下虚荣心,哈哈~

一 啥是modbus

  modbus 是一家名为Modicon开发的总线协议,于是取公司名缩写(mod)加上总线协议(bus),组成了名字modbus。
  modbus区别于底层硬件,是一种协议

举个例子,如果将modbus比作汉语的话,那么通过打电话,发邮件,或者用嘴巴说,都可以使用汉语,打电话,发邮件,用嘴巴说就是底层实现的硬件,汉语就是上层的协议,无论用什么硬件实现,只要协议是相同的,就可以正常沟通。

二 分类

  按照传输方式来分的话,modbus分为以下几类:

1.串行通信:

  • Modbus RTU:
      运行在串口上的协议,采用二进制表现形式以及紧凑型数据结构,通信效率高,应用广泛。它适用于长距离、低速率的通信场合,如使用RS-485、RS-232等串行接口进行通信。
  • Modbus ASCII:
      也是运行在串口上的协议,但采用ASCII码传输数据,并利用特殊字符作为其字节的开始与结束标识。由于每个数据字节都被转换为两个ASCII字符,因此其传输效率远低于Modbus RTU,通常只在通信数据量较小的情况下使用。

2.以太网通信:

  • Modbus TCP/IP:
      运行在以太网上的协议,采用TCP/IP协议进行通信。它支持全双工通信,即两个方向上的数据传输可以同时进行,适用于需要远程控制和监控的场合。Modbus TCP/IP的端口号默认为502。

  简单来说,用串口的是RTU和ASCII,用网线的是TCP/IP。RTU最常用,其次是TCP/IP,ASCII不常用。
  根据Modbus协议的规定,如果一个设备支持Modbus协议,它通常会默认支持Modbus RTU模式,故Modbus RTU是使用最广泛的Modbus协议,本文以Modbus RTU为例进行讲解,以下如无特别说明,均为Modbus RTU模式。

三 协议详解

1.通信架构

  Modbus RTU采用主从式通讯架构,即网络中只能有一个主站(Master)存在,负责发起通信请求,而从站(Slave)则响应这些请求。
  主站在Modbus网络上没有地址,而每个从站必须有唯一的地址,地址范围通常为1到247(其中0为广播地址,用于向所有从站发送消息)。
Modbus RTU可以是全双工也可以半双工,具体取决于其使用的物理接口,常用的有RS485(半双工),RS422(全双工),RS232(全双工)等。

2. 数据传输方式

  Modbus RTU协议主要位于应用层。它定义了主站和从站之间交换数据的格式和规则,包括设备地址、功能码、数据域等关键元素。
  Modbus RTU协议中数据字节的传输顺序大端模式,即先发送最高位。

3.帧格式:

帧格式如下图:

在这里插入图片描述

解析:

  • 设备地址:
    占用1个字节(8位),用于识别网络上的特定设备。
    合法的设备地址范围是1~247(0通常用作广播地址)。
  • 功能码:
    占用1个字节,用于指示要执行的操作或响应的类型。
    功能码决定了后续数据域的结构和内容。
  • 数据域:
    其长度和内容由功能码决定。
    包含了操作所需的具体数据,如寄存器地址、读取/写入的数据量等。
  • CRC校验(错误校验码):
    用于检测数据传输过程中的错误。
    在Modbus RTU中,通常使用CRC-16校验方法,占用2个字节。
  • 结束符:
    Modbus RTU协议要求每帧报文前后至少需要间隔3.5个字符的传输时间(以波特率计算的时间间隔),以此来区分不同的报文帧。
    举个例子:
      如果采用波特率9600的串口通信,且底层帧格式为1个起始位、8个数据位和1个停止位,则每一帧数据包含10个位,这个一帧数据也对应上文中的一个字符。
      则一帧数据(一个字符)的传输时间为:10 / 9600 ≈ 1.04ms。
      帧间间隔 ≈ 3.5 × 1.04ms ≈ 3.64ms。所以在这种情况下,每帧报文间隔至少为3.64ms。

四 数据类型

  modbus 共有四种基本数据类型:

  • 线圈(Coils/Discrete Outputs)
    定义:
    位数据表示单个开关量的状态,如开/关、真/假等。在Modbus中,位数据通常用于控制设备的输出,如继电器、指示灯等。
    特点:
    只能读取和写入单个位(bit)。 使用功能码01(读线圈状态)或05(写单个线圈)进行读写操作。
    应用场景:
    常用于控制设备的开关状态,如启动/停止按钮、报警指示灯等。

  • 离散量输入(Discrete Inputs)
    定义:
    离散量输入表示输入的状态,如传感器信号、按钮状态等。这些输入状态是离散的,即它们只能取有限个值(如开/关)。
    特点:
    只能读取,不能写入。使用功能码02(读离散输入状态)进行读操作。
    应用场景:
    用于读取外部设备的状态信息,如按钮是否被按下、传感器是否触发等。

  • 输入寄存器(Input Registers)
    定义:
    输入寄存器表示无符号的16位整数(Word),用于存储来自输入设备的数据。这些数据可以是传感器读数、计数器值等。
    特点
    只能读取,不能写入。使用功能码04(读输入寄存器)进行读操作。
    应用场景:
    用于读取外部设备的数据,如温度传感器的读数、流量计的计数值等。

  • 保持寄存器(Holding Registers)
    定义:
    保持寄存器表示有符号或无符号的16位整数(Word),用于存储内部处理的数据或控制参数。这些数据可以是设备的设置参数、运行状态等。
    特点:
    可以读取和写入。使用功能码03(读保持寄存器)、06(写单个保持寄存器)或16(写多个保持寄存器)等进行读写操作。
    应用场景:
    用于存储和处理内部数据,如设置设备的控制参数、读取设备的运行状态等。

五 功能码

以下是一些常见的功能码:

功能码名称描述
01读取线圈状态用于读取从站设备中一个或多个线圈(数字量输出)的状态
02读取离散输入状态用于读取从站设备中一个或多个离散输入(数字量输入)的状态
03读取保持寄存器用于读取从站设备中一个或多个保持寄存器的值
04读取输入寄存器用于读取从站设备中一个或多个输入寄存器的值
05写单个线圈用于将从站设备中的一个线圈设置为指定状态(ON或OFF)
06写单个保持寄存器用于将从站设备中的一个保持寄存器设置为指定的值
15写多个线圈用于同时将从站设备中的多个线圈设置为指定的状态(ON或OFF)
16写多个寄存器用于将从站设备中的多个保持寄存器设置为指定的值

六.实例

功能码01:读取线圈状态

  • 从地址123的设备上,读取离散量输出20~38(离散量从1开始)。

请求帧

从站地址(1字节)功能码(1字节)起始地址(2字节)线圈数量(2字节)CRC校验码(2字节)
7B0100 1300 1398 87

响应帧

从站地址(1字节)功能码(1字节)字节数(1字节)数据区(3字节)CRC校验码(2字节)
7B0103CD 6B 0518 49

数据解释:
  在返回的数据中共三个字节,从左往右数,第一个字节代表27~20的离散量状态,27在MSB,20在LSB,以此类推,第三个字节只有低位的三位有效,代表38 ~ 36 三个离散量状态,剩余的五个位用0填充。

0xCD = 0b11001101 即:

离散量编号2726252423222120
状态10110011

0x6b=0b01101011 即:

离散量编号3534333231302928
状态01101011

0x05 = 0b00000101

离散量编号-----383736
状态00000101

最后用0填充高位

功能码02:读取离散输入状态

  • 从地址123的设备上,读取离散量输出197~218(离散量从1开始)。

请求帧数据

从站地址(1字节)功能码(1字节)起始地址(2字节)输入数量(2字节)CRC校验码(2字节)
7B0200 C400 16A3 B3

响应帧数据

从站地址(1字节)功能码(1字节)字节数(1字节)数据区(3字节)CRC校验码(2字节)
7B0203AC DB 35XX

数据解释:
0xAC = 0b10101100 即:

离散量编号204203202201200199198197
状态10101100

0xDB = 11011011 即:

离散量编号212211210209208207206205
状态11011011

0x35 = 0b10101100 即:

离散量编号--218217216215214213
状态00110101

功能码03(读取保持寄存器)

  • 从地址123的设备上,读保持寄存器 108~110

请求帧数据

从站地址(1字节)功能码(1字节)起始地址(2字节)寄存器数量(2字节)CRC校验码(2字节)
7B0300 6B00 03XX

响应帧数据

从站地址(1字节)功能码(1字节)字节数(1字节)数据区(3字节)CRC校验码(2字节)
7B030602 2B 00 00 00 64XX

数据解释:
寄存器108值为 0x022b = 555
寄存器109值为 0x00 = 0
寄存器110值为 0x64= 100

功能码04(读取输入寄存器)

  • 从地址123的设备上,读取输入寄存器9

请求帧数据

从站地址(1字节)功能码(1字节)起始地址(2字节)寄存器数量(2字节)CRC校验码(2字节)
7B0400 0800 01XX

响应帧数据

从站地址(1字节)功能码(1字节)字节数(1字节)数据区(3字节)CRC校验码(2字节)
7B040200 0AXX

数据解释:
寄存器9的值为 0x000a = 10

功能码05(写单个线圈)

  • 从地址123的设备上,写线圈173为ON

请求帧数据

从站地址(1字节)功能码(1字节)线圈地址(2字节)线圈值(2字节)CRC校验码(2字节)
7B0500 ACFF 00XX

线圈值: 0xFF00请求线圈为ON,0x0000请求为OFF

响应帧数据

从站地址(1字节)功能码(1字节)线圈地址(2字节)线圈值(2字节)CRC校验码(2字节)
7B0500 ACFF 00XX

功能码06(写单个寄存器)

  • 从地址123的设备上,将0x03写入寄存器2

请求帧数据

从站地址(1字节)功能码(1字节)寄存器地址(2字节)寄存器值(2字节)CRC校验码(2字节)
7B0600 0100 03XX

响应帧数据

从站地址(1字节)功能码(1字节)线圈地址(2字节)线圈值(2字节)CRC校验码(2字节)
7B0600 0100 03XX

功能码15(写多个线圈)

  • 从地址123的设备上,从线圈20开始写入10个线圈,写入内容为 0xcd01

请求帧数据

从站地址(1字节)功能码(1字节)起始地址(2字节)线圈数量(2字节)字节数(1字节)线圈值(n字节)CRC校验码(2字节)
7B0F00 1300 0A02CD 01XX

响应帧数据

从站地址(1字节)功能码(1字节)线圈地址(2字节)线圈值(2字节)CRC校验码(2字节)
7B0F00 1300 0AXX

功能码16(写多个寄存器)

  • 从地址123的设备上,将0x000a和0x0102写入从第2个寄存器开始的两个寄存器(03 04号寄存器)

请求帧数据

从站地址(1字节)功能码(1字节)起始地址(2字节)寄存器数量(2字节)字节数(1字节)寄存器值(2n字节)CRC校验码(2字节)
7B1000 0100 020400 0a 01 02XX

响应帧数据

从站地址(1字节)功能码(1字节)线圈地址(2字节)线圈值(2字节)CRC校验码(2字节)
7B1000 0100 02XX

错误响应

从站地址(1字节)异常功能码(1字节)异常码(1字节)CRC校验码(2字节)
7B0x80+功能码01/02/03/04XX

七 modbus 国标下载

我用夸克网盘分享了「GB_T 19582.1-2008 基于Modbus协议的工业自动化网络规范 第1部分:Modbus应用协议.pdf」,点击链接即可保存。打开「夸克APP」在线查看,支持多种文档格式转换。
链接:https://pan.quark.cn/s/0155530b636f
提取码:GK1V

  
  
  
  
  
  如果帮助到你,麻烦点个赞给个关注,你的支持和关注是我持续更新的动力!谢谢~

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值