嵌入式--->Modbus

基础认识

  • Modbus协议包括ASCII、RTU(Remote Terminal Unit)、TCP/IP等,此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。
  • 可以理解ModBus是工作在应用层的,对应到OSI七层模型,其实也只是用到了应用层和物理层,并没有规定物理层,所以对于物理层既可以使用RS232又可以使用RS485实现。
  • Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。

功能码对照表

功能码英文名中文名
01Read Coils读线圈
02Read Discrete Inputs读离散寄存器
03Read Holding Registers读保持寄存器
04Read Input Registers读取输入寄存器
05Write Singled Coil写线圈
06Write Singled Register写单个寄存器
15Write Multiple Coils写多个线圈
16Write Multiple Registers写多个寄存器

相关名词与概念

输入寄存器与保持寄存器

  • 保持寄存器的值可以读取也可以修改,而输入寄存器的值对于master来说就只能读取。
  • 保持寄存器(Holding Register),指的是可以通过通信命令读写的寄存器;通常是一些功能控制寄存器或者输出寄存器等。不同的设计中,有些保持寄存器是掉电保持;有些则不。
  • 输入寄存器(Input Register),指的是只能读不能写的寄存器,通常是状态寄存器或者是输入结果寄存器等。(输入是模拟量的输入,保持寄存器就是设备内部的寄存器了)

线圈寄存器与离散输入寄存器

  • 这两个寄存器每一个位都表示一个开关状态,不同的是离散输入寄存器是只读的。
  • 线圈寄存器(Coil):实际上就可以类比为开关量(继电器状态),每一个bit对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。对应功能码也就是:0x01 0x05 0x0f
  • 离散输入寄存器(Discrete) :如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。所以功能码也简单就一个读的 0x02

如下为Modbus的概述,粗略的读过去便可, 跳至最后两部分内容实践即可
使用Modbus poll&Modbus slave模拟Modbus主从通信
Modbus帧分析,虚拟串口使用Modbus-RTU通信


Modbus定义

  • Modbus​是​使用​主从关系​实现​的​请求-响应​协议。

    在这里插入图片描述

  • Modbus协议​引入​了​不同​的​应用​程序​数据​单元​来​更改​串​行​通信​使用​的​数据​包​格式,​或​允许​使用​TCP/​IP​和​用户​数据​报​协议​(UDP)​网络。 其通信协议理解也比较简单,其帧结构如下:在这里插入图片描述

协议​数据​单元​PDU(功能码和数据)

  • PDU​(Packet Data Unit)及其​处理​代码​构成了Modbus​应用​协议​规范的​核心。 该​规范​定义​了​PDU​的​格式、​协议​使用​的​各种​数据​概念、​如何​使用​功能​代码​访问​数据,​以及​每​个​功能​代码​的​具体​实现​和​限制。
  • Modbus PDU​格式​被​定义​为​一个​功能​代码,​后面​跟着​一​组​关联​的​数据。 该​数据​的​大小​和​内容​由​功能​代码​定义,​整个​PDU(功能​代码​和​数据)​的​大小​不能​超过​253​个​字​节。 每​个​功能​代码​都有​一个​特定​的​行为,​从​设备​可以​根据​所需​的​应用​程序​行为​灵活​地​实现​这些​行为。
  • 与​数据​模型​可能​因​设备​而​异​不同,​功能​代码​及其​数据​由​标准​明确​定义。 每​个​功能​都​遵循​一种​模式。 首先,​从​设备​会​验证​功能​代码、​数据​地址​和​数据​范围​等​输入。 然后​执行​所​请求​的​操作​并​发送​与​代码​相符​的​响应。 如果​此​过程​中的​任何​步骤​失败,​则会​向​请求​程序​返回​异常。 这些​请求​的​数据​传输​就​称为​PDU。

Modbus PDU结构

  • PDU​由​一个​单字​节​的​功能​代码​组成,​后面​跟着​多​达​252​字​节​的​针对​特定​函数​的​数据。
    在这里插入图片描述
  • 功能​代码​是​第​一个​需要​验证​的​项。 如果​功能​代码​没有​被​接收​到​请求​的​设备​识别,​则会​回应​一个​异常。 如果​功能​代码​被​接受,​则​从​设备​根据​功能​定义​开始​分解​数据。
  • 由于​数据​包​大小​限制​为​253​字​节,​设备​可​传输​的​数据​量​有限。 最​常见​的​功能​代码​可以​240​到​250​字​节​的​从​设备​数据​模型​数据,​具体​取决​于​代码。

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

通常,​Modbus​可​访问​的​数据​存储​在​四​个​数据​库​或​地址​范围​的​其中​一个: 线圈​状态、​离散​量​输入、​保持​寄存器​和​输入​寄存器。 与​许多​规范​一样,​名称​可能​因​行业​或​应用​而​异。 例如,​保持​寄存器​也可以​称为​输出​寄存器,​线圈​状态​可能​称为​数字​或​离散​量​输出。 这些​数据​库​定义​了​所​包含​数据​的​类型​和​访问​权限。 从​设备​可以​直接​访问​这些​数据,​因为​这些​数据​由​设备​本地​托管。 Modbus​可​访问​的​数据​通常​是​设备​主​存​的​一个​子​集。 相反,​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。
在​这种​寻​址​方案​中,​每​种​数据​类型​都​被​分配​了​一个​前​缀:
在这里插入图片描述
 线圈​状态​存在​前​缀​为​0​的​情况。​这​意味​着​4001​的​引用​可能​指​的是​保持​寄存器​1​或​线圈​4001。​因此,​建议​所有​新​寻​址​方案​都​采用​带​前​导​零​的​6​位​寻​址,​并​在​文​档​中​进行​标​注。 因此,​保持​寄存器​1​的​地址​为​400,001,​而​线圈​4001​的​地址​则为​004,001。

数据​地址​起始值

 内存​地址​和​参考​数字​之间​的​差异​会​由​给​定​应用​程序​选择​的​索引​进一步​复杂​化。 如​前​所述,​保存​寄存器​1​位于​地址​零。 通常,​参考​号码​是​1​索引,​这​意味​着​给​定​范围​的​起始​值​为​1。 因此,​400,001​就​表示​为​地址​0​的​保持​寄存器​00001。​一些​做法​选择​以​零​开始​其​范围,​这​意味​着​400,000​转换​为​地址​零​的​保持​寄存器。
如下为寄存器索引方案,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)​字​节​顺序​发送​以​满足​标准,​但​许多​设备​会​颠倒​多​字​节​数据​的​字​节​顺序。 下图​所​示​的是​一个​不​常见​但​有效​的​例子。
在这里插入图片描述

字符串

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

应用​数据​单元​(ADU(ADU包含PDU))

 除了​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&ADU

 TCP/​IP TCP ADU​由​Modbus​应用​协议​(MBAP)​报​文​头​和​Modbus PDU​组成。 MBAP​是​一个​通用​的​报​文​头,​依赖​于​可靠​的​网络​层。 此​ADU​的​格式​(包括​报​文​头)​如​图​所​示。
在这里插入图片描述
 报​文​头​的​数据​字​段​代表​其​用途。 首先,​它​包含​一个​事务​处理​标识​符。 这​有助​于​网络​允许​同时​发生​多个​未​处理​的​请求。 也就是说,​主​设备​可以​发送​请求​1、​2​和​3。​在​稍​后​的​时间​点,​从​设备​可以​以​2、​1、​3​的​顺序​进行​响应,​并且​主​设备​可以​将​请求​匹配​到​响应​并​准确​解析​数据。 这​对​以太​网​网络​很有​用。
 协议​标识​符​通常​为​零,​但​您​可以​使用​它​来​扩展​协议​的​行为。 协议​使用​长度​字​段​来​描述​数据​包​其余​部分​的​长度。 这个​元素​的​位置​也​表明​了​这个​报​文​头​格式​在​可靠​的​网络​层​上​的​依赖​关系。 由于​TCP​数据​包​具有​内​置​的​错误​检查​功能,​可​确保​数据​一致性​和​传送,​因此​数据​包​长度​可​位于​报​文​头​的​任何​位置。 在​可靠性​较​差​的​网络​上​(比如​串​行​网络),​数据​包​可能​会​丢失,​其​影响​是​即使​应用​程序​读​取​的​数据​流​包含​有效​的​事务​处理​和​协议​信息,​长度​信息​的​损坏​也​会​使​报​文​头​无效。 TCP​为​这种​情况​提供​了​适当的​保护。
  TCP/​IP​设备​通常​不​适用​单元​ID。 但是,​Modbus​是​一种​常见​的​协议,​因此​通常​会​开发​一些​网​关​来​将​Modbus​协议​转换​为​另​一种​协议。 在​最初​的​预期​应用​中, Modbus TCP/​IP​转​串​行​网​关​用于​连接​新的​TCP/​IP​网络​和​旧​的​串​行​网络。 这时,​单元​ID​用于​确定​PDU​对应​的​从​设备​的​地址。
 TCP/​IP​设备​通常​不​适用​单元​ID。 但是,​Modbus​是​一种​常见​的​协议,​因此​通常​会​开发​一些​网​关​来​将​Modbus​协议​转换​为​另​一种​协议。 在​最初​的​预期​应用​中, Modbus TCP/​IP​转​串​行​网​关​用于​连接​新的​TCP/​IP​网络​和​旧​的​串​行​网络。 这时,​单元​ID​用于​确定​PDU​对应​的​从​设备​的​地址。
 最后,​ADU​包含​一个​PDU。 对于​标准​协议,​PDU​的​长度​仍​限制​为​253​字​节。

RTU&ADU

 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&ADU

​ASCII ADU​比​RTU​更​复杂,​但​也​避免​了​RTU​数据​包​的​许多​问题。 然而,​它​自身​也有​一些​缺点。
在这里插入图片描述
 为了​解决​确定​数据​包​大小​的​问题,​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 poll&Modbus slave模拟Modbus主从通信

 下载和破解搜一下都很方便,使用的时候Modbus Poll作为Master,Modbus slave作为从机,设置相同的Setup和Connection就可以进行通信了。
 详细教程见博客园的一篇不错的的文章

Modbus帧分析,虚拟串口使用Modbus-RTU通信

由于篇幅比较长,另起文章https://blog.csdn.net/qq_42069216/article/details/118519822

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值