Modbus​协议​深入​讲解

语言: CN / TW / HK

Modbus​是​一种​工业​协议,​于​1979​年​开发,​旨​在​实现​自动​化​设备​之间​的​通信。 Modbus​最初​是​作为​通过​串​行​层​传输​数据​的​应用​级​协议​实现​的,​现​已​扩展​到​包括​通过​串​行、​TCP/​IP​和​用户​数据​报​协议​(UDP)​的​实现。 本文​档​提供​了​协议​实现​的​深入​讲解。

内容

什么​是​Modbus​协议?

Modbus​是​使用​主从关系​实现​的​请求 - 响应​协议。 在​主从关系​中,​通信​总是​成​对​发生 - 一个​设备​必须​发起​请求,​然后​等待​响应 - 并且​发起​设备​(主​设备)​负责​发起​每次​交互。 通常,​主​设备​是​人​机​界面​(HMI)​或​监​控​和​数据​采集​(SCADA)​系统,​从​设备​是​传感器、​可​编​程​逻辑​控制器​(PLC)​或可​编​程​自动​化​控制器​(PAC)。 这些​请求​和​响应​的​内容​以及​发送​这些​消息​的​网络​层​由​协议​的​不同​层​来​定义。

图1. 主​从​网络​关系

Modbus​协议层

在​最初​的​做法​中,​Modbus​是​建立​在​串​行​端​口​之上​的​单一​协议,​因此​它​不能​被​分成​多个​层。 随着​时间​的​推移,​该​协议​引入​了​不同​的​应用​程序​数据​单元​来​更改​串​行​通信​使用​的​数据​包​格式,​或​允许​使用​TCP/​IP​和​用户​数据​报​协议​(UDP)​网络。 这​实现​了​定义​协议​数据​单元​(PDU)​的​核心​协议​和​定义​应用​数据​单元​(ADU)​的​网络​层​的​分离。

返回​顶部

协议​数据​单元​(PDU)

PDU​及其​处理​代码​构成了Modbus​应用​协议​规范的​核心。 该​规范​定义​了​PDU​的​格式、​协议​使用​的​各种​数据​概念、​如何​使用​功能​代码​访问​数据,​以及​每​个​功能​代码​的​具体​实现​和​限制。

Modbus PDU​格式​被​定义​为​一个​功能​代码,​后面​跟着​一​组​关联​的​数据。 该​数据​的​大小​和​内容​由​功能​代码​定义,​整个​PDU(功能​代码​和​数据)​的​大小​不能​超过​253​个​字​节。 每​个​功能​代码​都有​一个​特定​的​行为,​从​设备​可以​根据​所需​的​应用​程序​行为​灵活​地​实现​这些​行为。 PDU​规范​定义​了​数据​访问​和​操作​的​核心​概念;​但是,​从​设备​可能​会​以​规范​中​未​明确​定义​的​方式​处理​数据。

访问​Modbus​和​Modbus​数据​模型​中的​数据

通常,​Modbus​可​访问​的​数据​存储​在​四​个​数据​库​或​地址​范围​的​其中​一个: 线圈​状态、​离散​量​输入、​保持​寄存器​和​输入​寄存器。 与​许多​规范​一样,​名称​可能​因​行业​或​应用​而​异。 例如,​保持​寄存器​也可以​称为​输出​寄存器,​线圈​状态​可能​称为​数字​或​离散​量​输出。 这些​数据​库​定义​了​所​包含​数据​的​类型​和​访问​权限。 从​设备​可以​直接​访问​这些​数据,​因为​这些​数据​由​设备​本地​托管。 Modbus​可​访问​的​数据​通常​是​设备​主​存​的​一个​子​集。 相反,​Modbus​主​设备​必须​通过​各种​功能​代码​请求​访问​这些​数据。 表​1​中​描述​了​每​个​区块​的​行为。

 

内存​区块 数据​类型 主​设备​访问 从​设备​访问
线圈​状态 布尔 读/写 读/写
离散​输入 布尔 只读 读/写
保持​寄存器 无​符号​双​字​节​整型 读/写 读/写
输入​寄存器 无​符号​双​字​节​整型 只读 读/写

表​1. Modbus​数据​模型​区块

这些​区块​允许​您​限制​或​允许​访问​不同​的​数据​元素,​并且​为​应用​层​提供​简化​的​机制​来​访问​不同​的​数据​类型。

这些​区块​是​完全​概念​性的。 它们​可能​作为​独立​的​内存​地址​存在​于​给​定​的​系统​中,​但​也​可能​重叠。 例如,​线圈​状态​1​可能​存在​于​与​保持​寄存器​1​所​代表​的​字​的​第​一位​相同​的​内存​中。 寻​址​方案​完全​由​从​设备​定义,​其​对​每​个​内存​区​的​解释​是​设备​数据​模型​的​重要​组成​部分。

数据​模型​寻址

该​规范​将​每​个​区块​定义​为​包含​多​达​65,536(216)​个​元素​的​地址​空间。 在​PDU​的​定义​中,​Modbus​定义​了​每​个​数据​元素​的​地址,​范围​从​0​到​65,535。​但是,​每​个​数据​元素​的​编号​从​1​到​n,​其中​n​的​最大值​为​65,536。​也就是说,​线圈​状态​1​位于​地址​0​的​线圈​状态​区块​中,​而​保持​寄存器​54​位于​从​机​被​定义​为​保持​寄存器​的​内存​部分​中的​地址​53。

规范​允许​的​全部​范围​不需要​给​定​设备​实现。 例如,​设备​可能​会​选择​不​执行​线圈、​离散​输入​或​输入​寄存器,​而​只​使用​保持​寄存器​150​至​175​和​200​至​225。​这​是​完全​可以​接受​的,​并且​通过​例外​来​处理​无效​的​访问​尝试。

数据​寻​址​范围

虽然​规范​将​不同​的​数据​类型​定义​为​存在​于​不同​的​区块​中,​并​为​每​种​类型​分配​一个​本地​地址​范围,​但​这​并不​一定​会​转化​为​用于​记录​或​理解​给​定​设备​的​Modbus​可​访问​内存​的​直观​编​址​方案。 为了​简化​对​内存​区块​位置​的​理解,​引入​了​一种​编号​方案,​其​将​前​缀​添加​到​所​讨论​的​数据​的​地址​中。

例如,​设备​手册​不会​引用​地址​13​寄存器​14​的​数据​项,​而是​引用​地址​4,014,40,014​或​400,014​的​数据​项。​在​任何​情况​下,​第​一个​数字​都是​4,​表示​保持​寄存器,​剩余​数字​则​表示​指定​地址。 4XXX、​4XXXX​和​4XXXXX​的​区别​取决​于​设备​使用​的​地址​空间。 如果​所有​65,536​个​寄存器​都在​使用​中,​应该​使用​4XXXXX​符号,​因为​其​允许​范围​为​400,001~465,536。​如果​只​使用​几个​寄存器,​通常​的​做法​是​使用​范围​4,001​到​4,999。

在​这种​寻​址​方案​中,​每​种​数据​类型​都​被​分配​了​一个​前​缀,​如​表​2​所​示。

 

数据​区块 前缀
线圈​状态 0
离散​输入 1
输入​寄存器 3
保持​寄存器 4

表​2. 数据​范围​前缀

线圈​状态​存在​前​缀​为​0​的​情况。​这​意味​着​4001​的​引用​可能​指​的是​保持​寄存器​1​或​线圈​4001。​因此,​建议​所有​新​寻​址​方案​都​采用​带​前​导​零​的​6​位​寻​址,​并​在​文​档​中​进行​标​注。 因此,​保持​寄存器​1​的​地址​为​400,001,​而​线圈​4001​的​地址​则为​004,001。

数据​地址​起始值

内存​地址​和​参考​数字​之间​的​差异​会​由​给​定​应用​程序​选择​的​索引​进一步​复杂​化。 如​前​所述,​保存​寄存器​1​位于​地址​零。 通常,​参考​号码​是​1​索引,​这​意味​着​给​定​范围​的​起始​值​为​1。 因此,​400,001​就​表示​为​地址​0​的​保持​寄存器​00001。​一些​做法​选择​以​零​开始​其​范围,​这​意味​着​400,000​转换​为​地址​零​的​保持​寄存器。 表​3​展示​了​这个​概念。

地址 寄存器​编号 编号​1(1​索引,​标准) 编号​(0​索引,​替换)
0 1 400001 400000
1 2 400002 400001
2 3 400003 400002

表​3.寄存器​索引​方案

 

1​索引​范围​应用​较​为​广泛,​强烈​建议​采用。 无论​哪​种​情况,​每​个​范围​的​起始​值​都​应​在​文​档​中​注明。

大​数据​类型

Modbus​标准​提供​了​一个​相对​简单​的​数据​模型,​它​不​包含​无​符号​字​和​位​值​之外​的​其他​数据​类型。 如果​系统​的​位​值​对应​于​螺线​管​和​继电器,​并且​字​值​对应​于​未​缩​放​的​ADC​值,​这​是​足够​的,​但​对于​更​高级​的​系统​则​可能​不足。 因此,​许多​Modbus​实现​都​包含​跨​寄存器​边界​的​数据​类型。 NI LabVIEW​数据​记录​和​监​控​(DSC)​模块KEPServerEX都​定义​了​许多​参考​类型。 例如,​存储​在​保持​寄存器​中的​字符​串​遵循​标准​格式​(400,001),​但​后​跟​一个​十进制​数、​长度​和​字符​串​的​字​节​顺序​(400,001.2H​是​指​保持​寄存器​1​中的​两​个​字符​串,​其中​高位​字​节​对应​到​字符​串​的​第​一个​字符)。 这​是​必需​的,​因为​每​个​请求​的​大小​都是​有限​的,​所以​Modbus​主机​必须​知道​字符​串​的​确切​范围,​而不是​像​NULL​那样​搜索​长度​或​分隔​符。

位​访问

除了​允许​访问​跨​寄存器​边界​的​数据​之外,​一些​Modbus​主​设备​还​支持​对​寄存器​中​各个​位​的​引用。 这​是有​好处​的,​因为​它​允许​设备​将​相同​内存​范围​内的​每​种​类型​的​数据​组合​在一起,​而不​必将​二​进制​数据​分成​线圈​整体​和​离散​量​输入​范围。 这​通常​使用​小数点​和​位​索引​或​数字​进行​索引,​具体​取决​于​如何​实现。 也就是说,​第​一个​寄存器​的​第​一位​可能是​400,001.00​或​400,001.01。 建议​任何​文​档​都要​说明​所​使用​的​索引​方案。

数据​字​节​顺序

多​寄存器​数据​(单​精度​浮点​值),​可以​通过​将​数据​拆​分​到​两​个​寄存器,​轻松​地​在​Modbus​中​传输。 由于​这​不是​由​标准​定义​的,​因此​分割​的​字​节​顺序​没有​规定。 尽管​每​个​无​符号​字​必须​以​网络​(big-​endian)​字​节​顺序​发送​以​满足​标准,​但​许多​设备​会​颠倒​多​字​节​数据​的​字​节​顺序。 图​2​所​示​的是​一个​不​常见​但​有效​的​例子。

图​2.多​字​数据​的​字​节​顺序​交换

请​务必​理解​设备​如何​将​信息​存储​在​内存​中​并​对​其​进行​正确​解码。 建议​文​档​写​明​系统​所​使用​的​字​顺序。 如果​需要​灵活​性,​也可以​将​Endian​添加​为​系统​配置​选项,​提供​基础​的​编码​和​解码​功能。

字符串

字符​串​可以​很​容易​地​存储​在​Modbus​寄存器​中。 为了​简单​起​见,​一些​方法​要求​字符​串​长度​为​2​的​倍数,​并​使用​控制​来​填充​额外​的​空间。 字​节​顺序​也是​字符​串​交互​中的​一个​变量。 字符​串​格式​可能​包含​也​可能​不​包含​NULL​作为​最终​值。 举​个​例子,​一些​设备​的​数据​存储​方法​可能​如​图​3​所​示。

图​3. Modbus​字符​串​中的​字​节​顺序​反转

了解​功能​代码

与​数据​模型​可能​因​设备​而​异​不同,​功能​代码​及其​数据​由​标准​明确​定义。 每​个​功能​都​遵循​一种​模式。 首先,​从​设备​会​验证​功能​代码、​数据​地址​和​数据​范围​等​输入。 然后​执行​所​请求​的​操作​并​发送​与​代码​相符​的​响应。 如果​此​过程​中的​任何​步骤​失败,​则会​向​请求​程序​返回​异常。 这些​请求​的​数据​传输​就​称为​PDU。

Modbus PDU

PDU​由​一个​单字​节​的​功能​代码​组成,​后面​跟着​多​达​252​字​节​的​针对​特定​函数​的​数据。

图4. Modbus PDU

功能​代码​是​第​一个​需要​验证​的​项。 如果​功能​代码​没有​被​接收​到​请求​的​设备​识别,​则会​回应​一个​异常。 如果​功能​代码​被​接受,​则​从​设备​根据​功能​定义​开始​分解​数据。

 

由于​数据​包​大小​限制​为​253​字​节,​设备​可​传输​的​数据​量​有限。 最​常见​的​功能​代码​可以​240​到​250​字​节​的​从​设备​数据​模型​数据,​具体​取决​于​代码。

从​函数​执行

不同​的​函数​由​数据​模型​定义​访问​不同​的​概念​数据​块。 一个​常见​的​做法​是​让​代码​访问​静态​内存​位置,​但​其他​行为​是​可用​的。 例如,​功能​代码​1(读​取​线圈​状态)​和​3(读​取​保持​寄存器)​可以​访问​内存​中​相同​的​物理​位置。 而​功能​代码​3(读​取​保持​寄存器)​和​16(写​入​保持​寄存器)​可以​访问​内存​中​完全​不同​的​位置。 因此,​建议​在​定义​从​数据​模型​时​同时​考虑​每​个​功能​代码​的​执行。

无论​执行​的是​何​种​实际​行为,​所有​的​从​设备​都应该​遵循​每​个​请求​的​简单​状态​流程​图。 图​5​是​代码​1​读​取​线圈​状态​的​一个​例子。

图​5.Modbus​协议​规范​定义的​读​取​线圈​状态​流程图

每​个​从​设备​必须​验证​功能​代码、​输入​数量、​起始​地址、​总​范围​以及​实际​进行​读​取​行为​的​从属​定义​函数​(slave-​defined function)​的​执行。

尽管​上面​的​状态​图​包含​了​静态​地址​范围,​但​真实​系统​的​需求​可能​会​使​静态​地址​范围​与​定义​的​数字​有所不同。 在​某些​情况​下,​从​设备​无法​传输​协议​定义​的​最大​字​节​数。 也就是说,​如果​主​设备​请求​0x07D0​输入,​从​设备​只能​用​0x0400​进行​响应。 如果​主​设备​从​地址​0​开始​请求​125,​则​这​是​正确​的,​但是​如果​主​设备​从​地址​400​开始​发出​相同​的​请求,​最后​一个​线圈​状态​将​位于​地址​525,​超出​了​该​设备​的​范围,​会​导致​出现​状态​图​定义​的​异常​02。

标准​功能​代码

每​个​标准​功能​代码​的​定义​都​包含​在​说明​书​中。 即使​对于​最​常见​的​功能​代码,​在​主​设备​上​启用​的​功能​与​从​设备​可以​处理​的​功能​之间​也​存在​不可避免​的​不​匹配。 为了​解决​这个​问题,​Modbus TCP​规范​的​早期​版本​定义​了三​个​一致性​类。 官方的Modbus​一致性​测试​规范没有​引用​这些​类,​而是​在​每​个​功能​的​基础​上​定义​一致性;​但是,​这些​仍然​很​容易​理解。 建议​任何​文​档​都​遵循​测试​规范,​并​根据​其​支持​的​代码​而不是​传统​分类​来​定义​它们​的​一致性。

0​类​代码

0​类​代码​通常​被​认为​是​有用​Modbus​设备​的​最低​配置,​因为​它们​使​主​设备​能够​读​取​或​写​入​数据​模型。

代码 说明
3 读​多​寄存器
16 写​多​寄存器

表​4.0​类​一致性​代码

1​类​代码

1​类​功能​代码​由​访问​所有​类型​的​数据​模型​所需​的​其他​代码​组成。 在​原始​定义​中,​这个​列表​包含​功能​代码​7(读​取​异常)。 但是,​此​代码​由​当前​规范​定义​为​仅​限于​串​行​的​代码。

代码 说明
1 读​线圈
2 读​离散​输入
4 读​输入​寄存器
5 写​单​线圈
6 写​单​寄存器
7 读​取​异常​状态​(仅​限​串​行)

表​5. 1​类​一致性​代码

2​类​代码

2​类​功能​代码​用于​更​为​专业​化​的​功能,​不太​常用。 例如,​读/​写​多个​寄存器​可能​有助​于​减少​请求/​响应​周期​的​总数,​但​该​行为​仍​可以​用​0​类​代码​实现。

代码 说明
15 写​多​线圈
20 读​文件​记录
21 写​文件​记录
22 屏蔽​写​寄存器
23 读/​写​多​寄存器
24 读​取​FIFO

表​6. 2​类​一致性​代码

Modbus​封​装​接口

Modbus​封​装​接口​(MEI)​代码​功能​43​用于​封​装​Modbus​数据​包​内的​其他​数据。 目前,​有​两​个​MEI​号码​可用,​13(CANopen)​和​14(设备​识别)。

功能​43/14(设备​识别)​非常​有用,​因为​它​允许​传送​多​达​256​个​唯一​的​对象。 其中​一些​对象​已​预​定义​且​预​留​好,​例如​供应​商​名称​和​产品​代码,​但​应用​程序​可以​将​其他​对象​定义​为​通用​数据​集。

此​代码​并不​常用。

例外

从​设备​使用​异常​来​指示​各种​不良​状况,​比如​错误​请求​或​不​正确​输入。 但是,​异常​也可以​作为​对​无效​请求​的​应用​程序​级​响应。 从​设备​不​响应​发出​异常​的​请求。 相反,​从​设备​忽略​不​完整​或​损坏​的​请求,​并​开始​等待​新的​消息​传​入。

异常​以​定义​好的​数据​包​格式​报告​给​用户。 首先​将​一个​功能​代码​返回​给​等​同​于​与​原始​功能​代码​的​请求​主​设备,​除了​设置​了​最高​有效​位。 这​等​同​于​为​原始​功能​代码​的​值​加上​0x80。 异常​响应​包括​一个​异常​代码​来​代替​与​给​定​函数​响应​相关​的​正常​数据。

在​标准​内,​四​种​最​常见​的​异常​代码​是​01,02,03​和​04。​表​7​介绍​了​这些​代码​以及​每​种​功能​的​标准​含义。

异常​代码 含义
01 不​支持​接收​到​功能​代码。 要​确认​原始​功能​代码,​请​从​返回​值​中​减去​0x80。
02 尝试​访问​的​请求​是​一个​无效​地址。 在​标准​中,​只有​起始​地址​和​请求​的​数值​超过216时​才​会​发生​这种​情况。 但是,​有些​设备​可能​会​限制​其​数据​模型​中的​地址​空间。
03 请求​包含​不​正确​的​数据。 在​某些​情况​下,​这​意味​着​参数​不​匹配,​例如​发送​的​寄存器​的​数量​与“字​节​数”字​段​之间​的​参数​不​匹配。 更​常见​的​情况​是,​主机​请求​的​数据​比​从​机​或​协议​允许​的​要​多。 例如,​主​设备​一次​只能​读​取​125​个​保持​寄存器,​而​资源​受限​的​设备​可能​会​将​此​值​限制​为​更少​的​寄存器。 例如,​主​设备​一次​只能​读​取​125​个​保持​寄存器,​而​资源​受限​的​设备​可能​会​将​此​值​限制​为​更少​的​寄存器。
04 尝试​处理​请求​时​发生​不可​恢复​的​错误。 这​是​一个​异常​的​代码,​表示​请求​有效,​但从​设备​无法​执行​该​请求。

表​7.常见​的​Modbus​异常​代码

 

每​个​功能​代码​的​状态​图​至少​应​包含​异常​代码​01,​通常​包含​异常​代码​04,02,03,​并且​任何​其他​定义​的​异常​代码​都是​可​选​的。

返回​顶部

应用​数据​单元​(ADU)

除了​Modbus​协议​的​PDU​核心​定义​的​功能​外,​您​还​可以​使用​多种​网络​协议。 最​常见​的​协议​是​串​行​和​TCP/​IP,​但​也可以​使用​其他​协议,​如​UDP。 为了​在​这些​层​之间​传输​Modbus​所需​的​数据,​Modbus​包含​一​组​适用​于​每​种​网络​协议​的​ADU。

通用​特征

Modbus​需要​某些​功能​来​提供​可靠​的​通信。 单元​ID​或​地址​用​在​每​个​ADU​格式​中,​为​应用​层​提供​路​由​信息。 每​个​ADU​都​带有​一个​完整​的​PDU,​其中​包含​给​定​请求​的​功能​代码​和​相关​数据。 为了​可靠性,​每​条​消息​都​包含​错误​检查​信息。 最后,​所有​的​ADU​都​提供​了​一种​机制​来​确定​请求​帧​的​开始​和​结束,​但​实现​这些​机制​的​方式​各不相同。

标准​格式

ADU​的​三​种​标准​格式​是​TCP、​远程​终端​单元​(RTU)​和​ASCII。 RTU​和​ASCII ADU​通常​用于​串​行​线路,​而​TCP​则​用于​现代​TCP/​IP​或​UDP/​IP​网络。

TCP/​IP

TCP ADU​由​Modbus​应用​协议​(MBAP)​报​文​头​和​Modbus PDU​组成。 MBAP​是​一个​通用​的​报​文​头,​依赖​于​可靠​的​网络​层。 此​ADU​的​格式​(包括​报​文​头)​如​图​6​所​示。

图​6.TCP/​IP ADU

报​文​头​的​数据​字​段​代表​其​用途。 首先,​它​包含​一个​事务​处理​标识​符。 这​有助​于​网络​允许​同时​发生​多个​未​处理​的​请求。 也就是说,​主​设备​可以​发送​请求​1、​2​和​3。​在​稍​后​的​时间​点,​从​设备​可以​以​2、​1、​3​的​顺序​进行​响应,​并且​主​设备​可以​将​请求​匹配​到​响应​并​准确​解析​数据。 这​对​以太​网​网络​很有​用。

协议​标识​符​通常​为​零,​但​您​可以​使用​它​来​扩展​协议​的​行为。 协议​使用​长度​字​段​来​描述​数据​包​其余​部分​的​长度。 这个​元素​的​位置​也​表明​了​这个​报​文​头​格式​在​可靠​的​网络​层​上​的​依赖​关系。 由于​TCP​数据​包​具有​内​置​的​错误​检查​功能,​可​确保​数据​一致性​和​传送,​因此​数据​包​长度​可​位于​报​文​头​的​任何​位置。 在​可靠性​较​差​的​网络​上​(比如​串​行​网络),​数据​包​可能​会​丢失,​其​影响​是​即使​应用​程序​读​取​的​数据​流​包含​有效​的​事务​处理​和​协议​信息,​长度​信息​的​损坏​也​会​使​报​文​头​无效。 TCP​为​这种​情况​提供​了​适当的​保护。

TCP/​IP​设备​通常​不​适用​单元​ID。 但是,​Modbus​是​一种​常见​的​协议,​因此​通常​会​开发​一些​网​关​来​将​Modbus​协议​转换​为​另​一种​协议。 在​最初​的​预期​应用​中, Modbus TCP/​IP​转​串​行​网​关​用于​连接​新的​TCP/​IP​网络​和​旧​的​串​行​网络。 这时,​单元​ID​用于​确定​PDU​对应​的​从​设备​的​地址。

最后,​ADU​包含​一个​PDU。 对于​标准​协议,​PDU​的​长度​仍​限制​为​253​字​节。

RTU

RTU ADU​看起来​要​简单​得​多,​如​图​7​所​示。

图7. RTU ADU

与​较​为​复杂​的​TCP/​IP ADU​不同​的是,​除了​核心​PDU​之外,​该​ADU​仅​包含​两​条​信息。 首先,​地址​用于​定义​PDU​对应​的​从​设备。 在​大​多数​网络​中,​地址​0​定义​的是“广播”地址。 也就是说,​主​设备​可以​发送​输出​命令​到​地址​0,​而​所有​从​设备​应​处理​该​请求,​但是​不​做出​任何​响应。 除了​这个​地址​外,​CRC​还​用于​确保​数据​的​完整性。

然而,​现在​的​实现​机制​远​没有​那么​简单。 数据​包​的​首尾​一对​沉默​时间​(silent time),​即​总​线上​没有​通信​的​时​段。​对于​9,600​的​波特​率,​这个​速率​大约是​4ms。​该​标准​定义​了​一个​最小​沉默​长度,​不论​波特​率​如何,​都​低于​2 ms。

 

首先,​这​存在​性能​缺陷,​因为​在​处理​数据​包​之前​设备​必须​等待​空闲​时间​结束。 然而,​更​危险​的是​串​行​传输​引入​了​不同​技术,​并且​波特​率​比​标准​更​快。 例如,​使用​USB/​串​口​转换​器​电缆,​您​无法​控制​数据​的​数据​包​和​数据​传输。 测试​表明,​结合​NI-​VISA​驱动​程序​使用​USB​转​串​口​电缆​会​在​数据​流​中​引入​了​尺寸​可变​的​大​间隙,​而​这些​间隙 – 沉默​期 – 会“诱​骗”符合​规范​的​代码​相信​消息​是​完整​的。 由于​消息​不​完整,​通常​会​导致​CRC​无效,​并​导致​设备​将​ADU​解释​为​损坏。

除了​传输​问题​之外,​现代​驱动​程序​技术​还​大量​提取​串​行​通信,​并且​通常​需要​应用​程序​代码​中的​轮​询​机制。 例如,​除非​通过​轮​询​端​口上​的​字​节,.NET Framework 4.5 SerialPort Class和​NI-​VISA​驱动​程序​都不​提供​检测​串​行​线路​上​的​沉默​的​机制。 这​会​导致​性能​降低​(如果​轮​询​执行​过​慢)​或​CPU​使用​率​过​高​(如果​轮​询​执行​过​快)。

解决​这些​问题​的​常用​方法​是​打破​Modbus PDU​和​网络​层​之间​的​抽象​层。 也就是说,​串​行​代码​询问​Modbus PDU​数据​包​以​确定​功能​代码。 结合​数据​包​中的​其他​数据,​可以​发现​剩余​数据​包​的​长度,​从而​确定​数据​包​的​结尾。 利用​这些​信息,​可以​使用​更​长​的​超​时​时间,​以​允许​传输​间隙,​并且​应用​程序​级​的​轮​询​速度​可能​更慢。 这种​机制​推荐​用于​新的​开发。 不​采用​此​方法​可能​会​遇到​大于​预期​数量​的“损坏”数据​包。

ASCII

如​图​8​所​示,​ASCII ADU​比​RTU​更​复杂,​但​也​避免​了​RTU​数据​包​的​许多​问题。 然而,​它​自身​也有​一些​缺点。

图 8. ASCII ADU

为了​解决​确定​数据​包​大小​的​问题,​ASCII ADU​为​每​个​数据​包​定义​了​一个​明确​且​唯一​的​开始​和​结束。 也就是说,​每​个​数据​包​以“:”开始 并​以​回车​(CR)​和​换​行​符​(LF)​结束。 另外,​像​NI-​VISA​和.NET Framework SerialPort Class​这样​的​串​行​API​可以​轻松​读​取​缓冲​区​中的​数据,​直到​收到​特定​字符​的​CR/​LF​为止。 这些​特性​有助​于​在​现代​应用​程序​代码​中​有效​地​处理​串​行​线路​上​的​数据​流。

ASCII ADU​的​缺点​是​所有​数据​都​以​ASCII​编码​的​十六​进制​字符​进行​传输。 也就是说,​针对​功能​代码​3(0x03)​发送​的​不是​单​个​字​节,​而是​发送​ASCII​字符“0”和“3”或​0x30/0x33。 这​使​协议​更​具​可读​性,​但​也​意味​着​必须​通过​串​行​网络​传输​两​倍​的​数据,​并且​发送​和​接收​应用​程序​必须​能够​解析​ASCII​值。

扩展​Modbus

Modbus​是​一种​相对​简单​和​开放​的​标准,​可以​进行​修改​以​适应​给​定​应用​的​需求。 这​常用​于​HMI​和​PLC​或​PAC​之间​的​通信,​因为​在​这种​情况​下​组织​可以​控制​协议​的​首尾。 例如,​传感器​的​开发​人员​更​可能​遵守​书面​标准,​因为​他们​通常​只​控制​其​从​设备​的​实现,​互通性​也是​可能​实现​的。

一般​来说,​不​建议​修改​协议。 本​节​仅​作为​对​其他​人​用​来​调整​协议​行为​的​机制​的​确认。

返回​顶部

新​功能​代码

Modbus​标准​定义​了​一些​功能​代码,​但​也​可​允许​您​开发​更多​的​功能​代码。 具体​而言,​功能​代码​1​至​64,73​至​99​以及​111​至​127​是​预​留​的​并​保证​唯一​的​公共​代码。 其余​代码​65​至​72​和​100​至​110​可​由​用户​自​定义。 使用​这些​用户​定义​的​代码​时,​您​可以​使用​任何​数据​结构。 数据​甚至​可能​超过​Modbus PDU​的​标准​253​字​节​限制,​但​应​验证​整个​应用​程序​以​确保​其他​层​在​PDU​超过​标准​限制​时​按​预期​工作。 高于​127​的​功能​代码​预​留作​异常​响应。

返回​顶部

网络层

除了​串​行​和​TCP​之外,​Modbus​还​可以​在​许多​网络​层​上​运行。 一个可能的​实现​是​UDP,​因为​它​适合​于​Modbus​通信​风格。 Modbus​本质​上​是​基于​消息​的​协议,​因此​UDP​能够​发送​明确​定义​的​信息​包,​而​不需要​任何​额外​的​应用​程序​级​信息,​如​起始​字符​或​长度,​这​使得​Modbus​非常​易​于​实现。 Modbus PDU​数据​包​可以​使用​标准​的​UDP API​发送,​不需要​额外​的​ADU​或​重新​使用​现有​的​ADU,​并​由​另一​端​完全​接收。 虽然​由于​其​内​置​确认​系统,​TCP​对​某些​协议​有利,​但​Modbus​是在​应用​层​执行​确认。 因此,​以​这种​方式​使用​UDP​会​消除​TCP ADU​中的​事务​处理​标识​符​字​段,​从而​消除​了​存在​多个​同时​发生​的​未完成​事务​的​可能性。 因此,​主​设备​必须​是​同步​主​设备,​或者​UDP​数据​包​必须​有​一个​标识​符​以​帮助​主​设备​组织​请求​和​响应。 建议​的​做法​是在​UDP​网络​层​上​使用​TCP/​IP ADU。

返回​顶部

ADU​修改

最后,​应用​程序​可以​选择​修改​ADU,​或​使用​现有​ADU​的​未​使用​部分​(如​TCP)。 例如,​TCP​定义​了​一个​16​位​长度​字​段、​一个​16​位​协议​和​一个​8​位​单元​ID。​鉴于​最大​的​Modbus PDU​是​253​字​节,​长度​字​段​的​高​字​节​始终​为​零。 对于​Modbus/​TCP,​协议​字​段​和​单元​ID​始终​为​零。 协议​的​简单​扩展​可以​通过​将​协议​字​段​更改​为非​零​数字​并​使用​两​个​未​使用​的​字​节​(单元​ID​和​长度​字​段​的​高​字​节)​来​发送​两​个​附加​PDU​的​长度,​从而​同时​发送​三​个​数据​包​(请​参阅​图​9)。

图​9. TCP ADU​修改​示例

 

分享到: