目录
(3) Modbus Poll 与 Modbus Slave 互联/通
学习Modbus的快速方法
1.寄存器速记
作为初学者,你阅读 Modbus 协议时会发现它的概念别扭、重复、不易区分,比如线圈状态(Coil Status)、离散输入状态(Discrete Input Status)、保持寄存器(Holding Register)、输入寄存器(Input Register)。
回到事情的本质,在工业控制 PLC 领域,涉及数字信号的输入、输出,模拟信号的输入、输出,如下图所示:
对于软件开发而言:
① 想得到按键输入状态时,读取到的是一位数据;
② 想控制 LED 时,需要输出一位数据,想读取 LED 当前状态时,也可以读取到一位数据
③ 想读取模拟信号时,读取到的是多位数据,比如 16 位数据
④ 想输出模拟信号时,写入的是多位数据,比如 16 位数据;也可以读取“模拟量输出” 的当前值。
在上图中,“数字量输入 DI”是只读的,“数字量输出 DO”是可读可写的,“模拟量输入 AI”是只读的,“模拟量输出 AO”是可读可写的。
上图里的“模拟量输入 AI”、“模拟量输出 AO”都表示“多位数值”,这些“多位数值”无需局限于只表示“模拟量”,也可以表示“多位数字量”。把 AI、AO 的含义扩展后,如下图所示:
对于软件开发而言:
① 想得到按键输入状态时,读取到的是一位数据(离散输入状态)
② 想控制 LED 时,需要输出一位数据,想读取 LED 当前状态时,也可以读取到一位数据(线圈状态)
③ 想读取参数时,读取到的“输入寄存器”,得到多位数据,比如 16 位数据(输入寄存器)
④ 想设置参数时,写的是“保存寄存器”,写入的是多位数据,比如 16 位数据;也可以读“保存寄存器”(保持寄存器)
在电子系统里,无论是单 bit 的数值、多 bit 的数值,都是保存在寄存器里。根据上图,这些寄存器可以分为 4 类:
寄存器种类 | 说明 | 与PLC类比 | 举例说明 |
---|---|---|---|
线圈状态 (Coil Status) | 输出端口。可设定端口输出状态,也可以读取该位的输出状态。可分为两种不同的执行状态,列如保持型或边沿触发型 | DO(数字量输出) | 电磁阀输出、MOSFEF输出、LED 显示等 |
离散输入状态 (Discrete Input Status) | 输入端口。通过外部设定改变输入状态,可读但不可以写 | DI(数字量输入) | 拨码开关、接近开关等 |
保持寄存器 (Holding Register) | 输出参数或保持参数。控制器运行时被设定的某些参数,可读可写 | AO(模拟量输出) | 模拟量输出设定值,PID运行参数,变量阀输出大小,传感器报警上限下限 |
输入寄存器 (Input Register) | 输入参数。控制器运行时从外部设备获得的参数,但可读不可写 | AI(模拟量输入) | 模拟量输入 |
在 Modbus 中,多位操作时都是 16 位(2bytes)的,总结如下:
① bit 操作涉及的寄存器有 2 类:线圈状态(可读可写)、离散输入状态(只读)
② 16bit 操作的寄存器有 2 类:保存寄存器(可读可写)、输入寄存器(只读)
一个设备里,可能有多个“线圈状态”、多个“离散输入状态”、多个“保存寄存器”、多个“输入寄存器”。怎么分辨某类寄存器中的某一个?它们有“寄存器地址”,如下图所示:
在上表中,“线圈状态”的寄存器 N、“离散输入状态”的寄存器 N,是两个不同的寄存器。
简单记忆方法:
① “偶数类的寄存器”是可读可写的,比如“0x”和“4x”;
② “奇数类的寄存器”是只读的,比如“1x”和“3x”;
③ “0x”和“1x”是 bit 寄存器;
④ “3x”和“4x”是 16bit 寄存器。
2.协议速记
Modbus 是一主多从的协议,如下图所示:
主控发出的数据里,必定含有如下信息:
① 设备地址:你要访问从设备 1,还是访问从设备 2
② 访问哪类寄存器,是读还是写,只访问 1 个寄存器,还是多个寄存器:这被称为功能码
③ 起始寄存器地址、寄存器数量:这在数据里定义
④ 为了保证数据传输的可靠,还附带有 CRC 检验码
以 Modbus RTU 协议为例,主控发出的数据包格式如下:
功能代码(功能码)有哪些?常用的功能码如下:
① 读线圈状态(0x01)
② 读离散输入状态(0x02)
③ 写单个线圈(0x05)、写多个线圈(0x15)
④ 读保持寄存器(0x03)
⑤ 读输入寄存器(0x04)
⑥ 写单个保存寄存器(0x06)、写多个保存寄存器(0x16)
上图中,寄存器起始地址(“Starting Address”)是 16 位的,先传输高字节,再传输低字节。线圈数量(“Quantiti of coils”)也是 16 位的,先传输高字节,再传输低字节。
响应包回复多少个数据呢(上图中 N 为多少)?N = Quantiti of coils / 8,如果余数不等于 0,则 N 再加 1。比如 Quantiti of coils=9,则返回 2 个字节。
在《Modbus_Application_Protocol_V1_1b3.pdf》中,列出了如下功能表。根据次表,在结合《5.5 Moubus 功能码详解》的示例,就可以对 Modbus RTU 协议有很好的理解了。
初识Modbus
1.背景
Modbus 诞生于 1979 年莫迪康(Modicon)公司,后来被施耐德电气公司收购。Modbus 提供通用语言用于彼此通信的设备和设备,是全球第一个真正用于工业现场的总线协议。Modbus 已经成为工业领域通信协议的业界标准,并且现在是工业电子设备之间常用的连接方式。Modbus 作为目前工业领域应用最广泛的协议,之后为了更好地普及和推动 Modbus 基于以太网 (TCP/IP) 的分布式应用,施耐德公司已将 Modbus 协议的所有权移交给 IDA(Interface for Distributed Automation,分布式自动化接口)组织,并成立了 Modbus-IDA 组织,此组织的成立和发展进一步推动了 Modbus 协议的广泛应用。
2.什么是Modbus?
(1) Modbus简介
Modbus 协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus 协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为 Modbus Master,从设备方使用的协议称为 Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如 PLC 可编程控制器等。有了它,不同厂商生产的控制设备就可以连接成工业网络,进行集中监控。Modbus 协议定义了一个控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的;而且描述了控制器请求访问其他设备的过程,如何应答来自其他设备的请求,以及怎样侦测错误并记录,并制定了统一的消息域的结构和内容。 当在 Modbus 网络上通信时,Modbus 协议决定了每个控制器必须要知道它们的设备地址,识别按地址发来的消息决定要产生何种行为。如果需要回应,则控制器将生成反馈信息并通过 Modbus 协议发送。
Modbus 通讯物理接口可以选用串口(包括 RS232、RS485 和 RS422 等),也可以选择以太网口。其通信遵循以下的过程:
① 主设备向从设备发送请求
② 从设备分析并处理主设备的请求,然后向主设备发送结果
③ 如果出现任何差错,从设备将返回一个异常功能码
此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如何回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
当在 Modbus 网络上通信时,此协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成反馈信息并用Modbus 协议发出。在其它网络上,包含了 Modbus 协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。
Modbus 的工作方式是请求/应答,每次通讯都是主站先发送指令,可以是广播,或是向特定从站的单播;从站响应指令,并按要求应答,或者报告异常。当主站不发送请求时,从站不会自己发出数据,从站和从站之间不能直接通讯。
MODBUS 是一种应用层消息传递协议,位于 OSI 模型的第 7 层。它提供连接在不同类型总线或网络上的设备之间的客户端/服务器通信。
Modbus 通信栈如下:
(2) Modbus常用术语
(3) Modbus事务处理
Modbus 协议允许在各种网终体系结构内进行简单通信,每种设备 (包括 PLC、HMI、控制面板、驱动程序、动作控制、输入/输出设备) 都能使用 Modbus 协议启动远程操作。在基于串行链路和以太网 (TCP/IP)的 Modbus 上可以进行相互通信。一些网关允许在几种使用 MODBUS 协议的总线或网络之间进行通讯。
MOUBUS 网络体系结构的实例:
Modbus 是一个请求、应答协议,并且提供统一的功能码用于数据传输服务。Modbus 功能码是 Modbus 请求/应答 PDU (Protocol Data Unit,协议数据单元)的元素之一,所谓的 PDU 其实就是 Modbus 协议定义的一个与基础通信层无关的简单协议数据单元。特定总线或网络上的 Modbus 协议映射能够在 ADU (Application Data UInit ,应用数据单元)上引入一些附加域,从而实现完整而准确的数据传输。
为了寻求一种简洁的通信格式,Modbus 协议定义了 PDU 模型,即功能码+数据的格式,而为了适应多种传输模式,又在 PDU 的基础上增加了必要的前缀 (如地址域)和后缀(如差错校验) ,形成了 ADU 模型(见下图)。
通用 MODBUS 帧如下:
Modbus 事务处理过程:
① 主机设备 (或客户端)创建 Modbus 应用数据单元形成查询报文,其中功能码标识了向从机设备 (或服务器端)指示将要执行的操作。其中功能码占用 1 字节,有效的码字范围是十进制 1 ~ 255 (其中 128 ~255 为异常响应保留) 。查询报文创建完毕,主机设备 (或客户端) 向从机设备 (或服务器端)发送报文,从机设备 (或服务器端)接收报文后根据功能码做出相应的动作,并将响应报文返回给主机设备 (或客户端),如图下所示:
② 如果在一个正确接收的 Modbus ADU 中不出现与请求 Modbus 功能有关的差错,那么从机设备 (或服务器端) 将返回正常的响应报文。如果出现与请求 Modbus 功能有关的差错,那么响应报文的功能码域将包括一个异常码,主机设备(或客户端)能够根据异常码确定下一步执行的操作;对于异常响应,服务器返回一个与原始功能码等同的码,设置该原始功能码的最高有效位为逻辑 1,用于通知主设备(客户端)。如下图所示:
Modbus软件与使用
1.Modbus软件简介
为了更好的学习和理解 Modbus,这里推出三个软件 Modbus Poll(主站设备)、Modbus Slave(从站设备)和虚拟串口软件,借助三款设备我们可以在 PC 上做一些基础实验,更加直观地观察通信数据,加深我们的理解,我们将它称为 Modbus 学习必备三件套,这是一个很好的入门方法。
2.Modbus Poll(主站设备)
(1) Modbus Poll 简介
Modbus Poll 是 Modbus 主站设备仿真器,用于测试和调试 Modbus 从设备便于观察 Modbus 通信过程中的各种报文数据。该软件支持 ModbusRTU、ASCII、TCP/IP。用来帮助开发人员测试Modbus从设备,或者其它Modbus协议的测试和仿真。它支持多文档接口,即可以同时监视多个从设备/数据域。每个窗口简单地设定从设备 ID,功能,地址,大小和轮询间隔。你可以从任意一个窗口读写寄存器和线圈。如果你想改变一个单独的寄存器,简单地双击这个值即可。或者你可以改变多个寄存器/线圈值。提供数据的多种格式方式,比如浮点、双精度、长整型(可以字节序列交换)。该软件支持 Modbus RTU、ASCII、TCP/IP 等协议模式。
Modbus Poll 支持下列协议模式:
(2) Modbus Poll 使用
点击链接获取软件,按照提示安装即可;链接: https://pan.baidu.com/s/1SpTRz6Z1XlkoCZjDozwqog 提取码:timc
下载完界面如下:
状态栏:
① Tx = 0 表示向主站发送数据帧次数,图中为 0 次;
② Err = 0 表示通讯错误次数,图中为 0 次;
③ ID = 1 表示模拟的 Modbus 子设备的设备地址,图中地址为 1;
④ F = 03 表示所使用的 Modbus 功能码,图中为 03 功能码;
⑤ SR = 1000ms 表示发送周期,1S 一次。
⑥ 红字部分,表示当前的错误状态,“No Connection”表示未连接状态。
建立连接:
点击 Connection->Connect 进入配置页面,选择我们想要的连接,选择我们虚拟出来的串口,选择模式,例如:我们选择串口的连接方式,选则 RTU 模式,对应我们的 Modbus RTU 协议;接下来在设置波特率、比特位、校验位、停止位,如下图所示:
设置参数:点击 Setup->Read/Write Definition 进入配置页面,配置从机地址、功能码、地址类型、寄存器地址、访问数量、轮询时间,具体配置如下图:
3.Modbus Slave(从站设备)
(1) Modbus Slave 简介
Modbus 从设备仿真器,可以仿真 32 个从设备/地址域。每个接口都提供了对 EXCEL 报表的 OLE 自动化支持。主要用来模拟 Modbus 从站设备,接收主站的命令包,回送数据包。帮助 Modbus 通讯设备开发人员进行 Modbus 通讯协议的模拟和测试,用于模拟、测试、调试Modbus 通讯设备,便于观察 Modbus 通信过程中的各种报文数据;可以 32 个窗口中模拟多达 32 个 Modbus 子设备。与 Modbus Poll 的用户界面相同,支持功能 01, 02, 03, 04, 05, 06, 15, 16, 22 和 23,监视串口数据。
Modbus Slave 支持下列协议模式:
(2) Modbus Slave 使用
获取软件链接同上,下载完后主页面如图所示:
建立连接:
点击 Connection->Connect 进入配置页面,选择我们想要的连接,选择我们虚拟出来的串口,选择模式,接下来在设置波特率、比特位、校验位、停止位,如下图所示:
设置参数:点击 Setup->Read/Write Definition 进入配置页面,配置从机地址、功能码、地址类型、寄存器地址、访问数量,具体配置如下图:
这里有两点需要我们注意一下:
一是:Function 列表框选择功能中的 0x~4x,表示的是存储区 0 区、1 区、3 区、4 区
① 输出线圈
② 输入线圈
③ 保持寄存器
④ 输入寄存器
Modbus 协议规定了 4 个存储区 分别是 0、1、3、4 区 其中 0 区和 4 区是可读可写,1 区和 3 区是只读。
二是:Address 项,这里需要特别强调一下,Address 表示 Modbus 寄存器地址,其取值范围与设备寄存器地址存在映射关系,如下表所示:
4.虚拟串口软件
(1) 软件简介
虚拟串口工具,可以创建 2 个互联的串口,如下图所示:
比如 Modbus Poll 工具使用 COM1 发送数据给 COM2,Modbus Slave 从 COM2 读到数据。 使用虚拟串口,就可以不使用开发板也可以体验 Modbus Poll、Modbus Slave。
(2) 虚拟串口的使用
安装后运行虚拟串口程序“Virtual Serial Port Tools”,安装下图创建 2 个串口:
打开设备管理器,可以看到如下串口:
(3) Modbus Poll 与 Modbus Slave 互联/通
下面我们进行 Modbus Poll 与 Modbus Slave 互联互通实验,通过形象直观的方式展示 Modbus 数据流,根据前面的设定我们已将知道了如何运用 Modbus 学习必备三件套,下面我们就通过三件套来进行实验,首先打开 VSPD 虚拟串口软件,设置虚拟串口,我这里就以上面设订 COM1,COM2 为例,接下来我们再来配置我们的 Modbus Poll 与 Modbus Slave;
我们首先打开 Modbus Slave 端,设置连接,连接方式我们选择 Serial Port 串口连接,选择我们设置的串口 COM1,模式选择 RTU 模式,如下图所示:
① Modbus Slave 连接设定
在设置参数,从机地址我们设定 1(你也可以自己随意设定),Function 项我们选择 03 Holding Register(4x),地址类型我们选择 DEC(十进制格式),Address 首地址我们设置为 0,访问寄存器数量设置为 10,如下图所示:
② Modbus Slave 参数设定
接下来我们再来设置 Modbus Poll 端,设置方法也是和 Modbus Slave 端一一对应的,连接设定,参数设定,如下图所示:
③ Modbus Poll 连接设定
注意这里串口要选择我们设定的 COM20,其它串口参数必须一一对应。
④ Modbus Poll 参数设定
设置好后,我们主设备和从设备分别连接了我们设置的 COM1,COM2,这样我们便可观察当前寄存器的读取情况。
我们双击 Modbus Poll(主设备端)地址中的 0 值,便可打开值设置窗口如下图所示:
修改值为 66,点击 Send 打开 Modbus Slave(从设备端)便可发现也做出了改变,如下图所示:
我们还可以打开 Modbus Poll,点击 Display,选择 Commuaction,查看发送的报文:
TX 是我们主站发送的报文,RX 是从站返回的报文。
Moubus功能码详解
本节大部分内容参考《Modbus 软件开发实战指南》。
1.功能码概要
Modbus 标准在协议中规定了以下 3 类 Modbus 功能码。
公共功能码:
① 被明确定义的功能码;
② 保证唯一性;
③ 由 Modbus 协会确认,并提供公开的文档;
④ 可进行一致性测试;
⑤ 包括协议定义的功能码和保留将来使用的功能码。
用户自定义功能码:
① 有两个用户自定义功能码区域,分别是 65~72 和 100~110;
② 用户自定义,无法保证唯一性。
保留功能码:
保留功能码因为历史遗留原因,某些公司的传统产品现行使用的功能码不作为公共使用。
(由于功能码篇幅较长,这里只举几个例子,详细内容见《Modbus 软件开发实战指南》)
2.(0x01)读取线圈
(1) 功能说明
01 功能码用于读取从设备的线圈或离散量输出的状态,即各 Do(Discrete Output,离散输出)的 ON/OFF 状态。消息帧中指定了需要读取的线圈起始地址和线圈数目。需要注意的是,在 Modbus 协议规定的 PDU 中,所有线圈或寄存器地址都必须从 0 开始计算。
(2) 查询报文
如下表所示,查询帧的消息中定义了从设备地址为 3,并读取从设备的 Modbus 地址 0001900055(线圈地址 0002000056)共计 37 个状态值。起始线圈地址为 0x13(即十进制00019),因为线圈地址是从 0 开始计数的。
功能码 01 查询报文示例:
Modbus协议规定,起始地址由2字节构成,取值范围为0x0000 ~ 0xFFFF: 线圈数量由 2 字节构成,取值范围为0x0001~ 0x07D0 (即+进制1~2000)另外,注意观察 ASCII 模式和 RTU 模式的区别,ASCII模式直接按每4位拆分成对应的字符表示。
(3) 响应报文
在响应报文的数据字段中,每个线圈占用1位 (bit),状态被表示为1=ON和O=OFF两种类型。第1个数据字节的LSB(最低有效位)标识查询报文中的起始地址线圈的状态值,其他线圈以此类推,一直到这个字节的MSB(最高有效位)为止,并在后续字节中按照同样的方式(由低到高)排列。
例如,下表中线圈20~27的状态值分别是ON - ON - OFF OFF - ON - OFF - ON - OFF表示为二进制则为01010011 (0x53),注意观察对应的顺序。1字节可以表示8个线圈的状态如果最后的数据字节中不能填满8个线圈的状态,则用0填充。对应于查询报文中需要读取 37 个线圈的状态,共需要5字节保存状态值。
功能码01响应报文示例:
3.(0x05)写单个线圈
(1) 功能说明
05功能码用于将单个线圈寄存器 (或离散输入)设置为ON或OFF,该功能码支持广播模式在广播模式下,所有从站设备的同一地址的值将被统一修改。查询报文中的ON/OFF状态由报文数据字段的常数指定,0xFF00表示ON状态,0x0000表示OFF状态。其他值均是非法的,并且对寄存器不起作用,将会返回异常响应。
(2) 查询报文
查询报文中需要指定从设备地址以及需要变更的线圈地址和设定的状态值。需要注意的是,在查询报文中,线圈地址从地址0开始计数。例如,如下表所示,从设备地址为3,设置线圈地址00150为ON状态,则查询报文中的线圈地址设置为0x95 (149)。
功能码05查询报文示例:
本功能码中,起始地址由2字节构成,取值范围为0x0000~0xFFFF:变更目标数据由2字节构成,取值只能为0xFF00或0x0000。
(3) 响应报文
响应报文的各项构成和意义如下表所示。对于从设备,在线圈或离散输出寄存器正常变更的情况下会返回与查询报文相同的响应报文。如果修改失败,则会返回一个异常响应,对于异常响应,后续章节会进一步详细介绍。
功能码05响应报文示例:
4.(0x0F)写多个线圈
(1) 功能说明
15功能码用于将连续的多个线圈或离散输出设置为ON/OFF状态,支持广播模式,在广播模式下,所有从站设备的同一地址的值将被统一修改。15功能码中,起始地址字段由2字节构成,取值范围为0x00000xFFFF:而寄存器数量字段由2字节构成,取值范用为0x00010x07B0。
(2) 查询报文
查询报文中包含请求数据字段,用于定义ON或OFF状态。数据字段中为逻辑1的位对应ON;逻辑0的位对应OFF。其中,ON/OFF与数据字段的对应关系可参考前面的章节“(0x01) 读取线圈” 中的内容举例说明,假设从站设备地址为5,需要设置线圈地址20 ~30的状态如下表所示。
线圈状态:
那么,写入的数据字段被划分为2字节,值分别为0xD1,对应于27 ~ 20的线圈,值0x05对应于30~28的线圈,注意细体会其中的高低位的对应关系。需要注意的是,在查询报文中,Modbus协议的起始地址为19 (0x13) ,即比线圈起始地址20少1。如下表所示,其中字节数字段表示需要变更数据的字节总数。
功能码15查询报文示例:
(3) 响应报文
对于从设备,在正常情况下,响应报文包括功能码、起始地址以及写入的线圈数量,如下表所示。
功能码15响应报文示例:
5.Modbus 异常响应
在通常情况下,从站设备将返回一个正常响应报文,但是在某些特殊情况下将返回异常响应报文。
对于查询报文,存在以下4种处理反馈:
① 正常接收,正常处理,返回正常响应报文;
② 因为通信错误等原因造成从站设备没有接收到查询报文,主站设备将按超时处理:从站设备接收到的查询报文存在通信错误 (如LRC、CRC错误等),此时从站设备将丢弃报文不响应,主站设备将按超时处理;
③ 从站设备接收到正确的报文,但是超过处理范围(如不存在的功能码或者寄存器等),此时从站设备将返回包含异常码 (Exception Code) 的响应报文。
④ 异常响应报文由从站地址、功能码以及异常码构成。其中,功能码与正常响应报文不同,在异常响应报文中,功能码最高位 (即MSB) 被设置为1。因为Modbus协议中的功能码占用1字节故用表达式描述为:
异常功能码=正常功能码+0x80
举例说明,如下表所示,查询报文的起始地址为0x012C (十进制300) ,即需要读取寄存器地址为30301开始的值。若从站设备中不存在输入寄存器30301,则从站设备将返回一个异常响应报文,参见下表的功能码和异常码。
异常响应示例(功能码04查询报文 ):
异常响应示例(功能码04响应报文 ):
常见的异常码如下表所示:
本文都是协议细节,后面会教大家怎么移植libmodbus库,在板子上实现modbus通信。