PLC
直播中

哼小曲

13年用户 1188经验值
私信 关注

什么是Modbus协议?如何写稳定的Modbus代码?

什么是Modbus协议?
Modbus协议有哪些分类?

使用Modbus时有哪些注意事项?
如何写稳定的Modbus代码?

回帖(1)

周奕

2021-7-2 10:47:05
  一、介绍
  Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。Modbus协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
  Modbus 是一个请求/应答协议。也叫做Slave和Master与Server和Client。
  同一种设备在不同领域的不同叫法。
  Slave:工业自动化用语;响应请求;
  Master:工业自动化用语;发送请求;
  Server:IT用语;响应请求;
  Client:IT用语;发送请求;
  在Modbus中,Slave和Server意思相同,Master和Client意思相同。
  modbus结构示意图
  二、协议分类
  ModBus协议是应用层报文传输协议(OSI模型第7层),它定义了一个与通信层无关的协议数据单元(PDU),即PDU=功能码+数据域。
  ModBus协议能够应用在不同类型的总线或网络。对应不同的总线或网络,Modbus协议引入一些附加域映射成应用数据单元(ADU),即ADU=附加域+PDU。目前,
  Modbus有下列三种通信方式:
  1.以太网,对应的通信模式是MODBUS TCP。
  2.异步串行传输(各种介质如有线RS-232-/422/485/;光纤、无线等),对应的通信模式是MODBUS RTU或MODBUS ASCII。
  3.高速令牌传递网络,对应的通信模式是Modbus PLUS。
  ASCII模式
  :
  地址
  功能代码
  数据数量
  数据1
  。。。
  数据n
  LRC高4位字节值
  LRC低4位字节值
  回车
  换行
  RTU模式
  地址
  功能代码
  数据数量
  数据1
  。。。
  数据n
  CRC高字节
  CRC低字节
  所选的ASCII或RTU方式仅适用于标准的Modbus网络,它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。“数据数量”字段只有在响应包中才有。
  三、Modbus消息帧
  两种传输模式中(ASCII或RTU),传输设备以将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个设备被选中(广播方式则传给所有设备),判知何时信息已完成。部分的消息也能侦测到并且错误能设置为返回结果。
  1、ASCII帧
  使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。
  其它域可以使用的传输字符是十六进制的0.。.9,A.。.F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。
  消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。一个典型消息帧如下所示:
  起始位
  设备地址
  功能代码
  数据
  LRC校验
  结束符
  1个字符
  2个字符
  2个字符
  n个字符
  2个字符
  2个字符
  2、RTU帧
  使用RTU模式,消息发送至少要以3.5个字符时间的停顿间隔开始。在网络波特率下多样的字符时间,这是最容易实现的(如下图的T1-T2-T3-T4所示)。传输的第一个域是设备地址。可以使用的传输字符是十六进制的0.。.9,A.。.F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少3.5个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。
  整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过1.5个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于3.5个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC域的值不可能是正确的。一典型的消息帧如下所示:
  起始位
  设备地址
  功能代码
  数据
  CRC校验
  结束符
  T1-T2-T3-T4
  8Bit
  8Bit
  n个8Bit
  16Bit
  T1-T2-T3-T4
  消息帧的地址域包含两个字符(ASCII)或8Bit(RTU)。可能的从设备地址是0.。.247 (十进制)。单个设备的地址范围是1.。.247。主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。地址0是用作广播地址,以使所有的从设备都能认识。当Modbus协议用于更高水准的网络,广播可能不允许或以其它方式代替。
  应答包中,数据包括了数据字节长度+数据值,请求包中数据只包含数据值。
  Modbus TCP/IP数据帧结构
  Modbus TCP/IP数据帧除了TCP已经有的包头外,还有modbus TCP协议数据单元(ADU),包括MBAP帧头以及与RTU数据内容相同的应用数据单元(PDU),地址码除外。
  其中与单纯的TCP/IP或是modbus-RTU相比,多的内容就是一个MBAP报文头:
  MBAP报文头定义
  可以看出来,MBAP报文头主要添加了以下附加信息,为了识别是请求还是响应而设置的事务元标识符(2个字节,通常为0,客户端发出的检验信息,服务器端只是需要将这两个字节的内容复制以后再放到回复报文的相应位置就可以)、为了判断协议类型设置的协议标识符(2个字节,0=MODBUS协议)、为了区分可变长度数据帧结束的数据帧长度(从下一个字节起至结束的长度,2个字节)、还有用于标识从站地址的单元标识符(1个字节,即从站地址),与RTU不同的是,从站地址放在了MBAP帧头里。这个里面的2个字节默认都是大端对齐(高字节、低字节)。
  PDU单元与MODBUS RTU数据内容基本相同,由于有TCP/IP和链路层(以太网)校验和机制所以去掉了CRC校验码,从站地址也放在了MBAP帧头里。
  另外Modbus TCP/IP默认端口为502。
  例子:
  Modbus 控制命令为:
  00 01 00 00 00 09 04 10 00 00 00 01 02 00 01
  MBAP PDU
  上述命令可简单的解释为:00 01(事务标识符)00 00(协议标识符)00 09(后续字节数)04(设备标识符,即从站地址)10(功能码,写多个保持寄存器值)00 00(第一个地址,即地址 1)00 01(写寄存器的个数,1个)02(后续所写数据的长度)00 01(具体写的数据)。
  Modbus RTU与Modbus TCP读指令对比
  MBAP报文头
  地址码
  功能码
  寄存器地址
  寄存器数量
  CRC校验
  Modbus RTU
  无
  01
  03
  01 8E
  00 04
  25 DE
  Modbus TCP
  00 00 00 00 00 06 00
  无
  03
  01 8E
  00 04
  无
  指令的涵义:从地址码为01(TCP协议单元标志为00)的模块0x18E(01 8E)寄存器地址开始读(03)四个(00 04)寄存器。
  Modbus RTU与Modbus TCP写指令对比
  MBAP报文头
  地址码
  功能码
  寄存器地址
  寄存器数量
  数据长度
  正文
  CRC校验
  RTU
  无
  01
  10
  01 8E
  00 01
  02
  00 00
  A8 7E
  TCP
  00 00 00 00 00 09 00
  无
  10
  01 8E
  00 01
  02
  00 00
  无
  指令的涵义:从地址码为01(TCP协议单元标志为00)的模块0x18E(01 8E)寄存器地址开始写(10)一个(00 01)寄存器,具体数据长度为2个字节(02),数据正文内容为00 00(00 00)。
  MODBUS ASCII和RTU两种模式的区别、优缺点
  MODBUS ASCII协议和RTU协议的比较:
  协议 开始标记 结束标记 校验 传输效率 程序处理
  ASCII :(冒号) CR,LF LRC 低 直观,简单,易调试
  RTU 无 无 CRC 高 稍复杂
  MODBUS的ASCII协议和RTU协议相比,MODBUS ASCII协议拥有开始和结束标记,而MODBUS RTU却没有,所以ASCII协议的程序中对数据包的处理能更加方便。MODBUS ASCII协议的DATA域传输的都是可见的ASCII字符,因此在调试阶段就显得更加直观,另外它的LRC校验程序也比较容易编写,这些都是MODBUS ASCII的优点。MODBUS ASCII的主要缺点是传输效率低,因为它传输的都是可见的ASCII字符,原来用RTU传输的数据每一个字节,用ASCII的话都要把这个字节拆分两个字节,比如RTU传输一个十六进制数0xF9,ASCII就需要传输字符‘F’和字符‘9’,对应的ASCII码0x46和0x39两个字节,这样它的传输的效率肯定就比RTU低。所以一般来说,如果所需要传输的数据量较小可以考虑使用ASCII协议,如果所需传输的数据量比较大,最好能使用RTU协议。
  另外,由于ASCII协议有开始标志和结束标志,所以一个数据包之间的各字节间的传输间隔时间可以大于1秒,而MODBUS RTU方式下,由于没有规定开始和结束标记,所以协议规定每两个字节之间发送或者接收的时间间隔不能超过3.5倍字符传输时间。如果两个字符时间间隔超过了3.5倍的字符传输时间,就认为一帧数据已经接收,新的一帧数据传输开始,所以RTU方式下两个字节间传输间隔有时间要求。MODBUS 的ASCII和RTU两种协议的这一区别可能决定某些应用场合只能选用其中一种协议。
  四、modbus功能码
  下表列出MODBUS支持的部分功能代码:以十进制表示。
  表1.1 MODBUS部分功能码
  代码
  中文名称
  寄存器PLC地址
  位操作/字操作
  操作数量
  01
  读线圈状态
  00001-09999
  位操作
  单个或多个
  02
  读离散输入状态
  10001-19999
  位操作
  单个或多个
  03
  读保持寄存器
  40001-49999
  字操作
  单个或多个
  04
  读输入寄存器
  30001-39999
  字操作
  单个或多个
  05
  写单个线圈
  00001-09999
  位操作
  单个
  06
  写单个保持寄存器
  40001-49999
  字操作
  单个
  15
  写多个线圈
  00001-09999
  位操作
  多个
  16
  写多个保持寄存器
  40001-49999
  字操作
  多个
  modbus协议的数据主要分为四类:离散量输入、线圈状态、输入寄存器、保持寄存器。离散量输入对应开入(遥信),线圈状态对应哪开出(遥控),输入寄存器对应只读的模拟量(遥测),保持寄存器对应可读可写的模拟量(遥调)。
  1.1功能码说明
  功能码可以分为位操作和字操作两类。位操作的最小单位为BIT,字操作的最小单位为两个字节。
  【位操作指令】 读线圈状态01H,读(离散)输入状态02H,写单个线圈06H和写多个线圈0FH。
  【字操作指令】 读保持寄存器03H,写单个寄存器06H,写多个保持寄存器10H。
  1.2Modbus数据模型
  Modbus中,数据可以分为两大类,分别为Coil和Register,每一种数据,根据读写方式的不同,又可细分为两种(只读,读写)。
  Modbus四种数据类型:
  数据名称
  数据含义
  读写状态
  Discretes Input
  位变量
  只读
  Coils
  位变量
  读写
  Input Registers
  16-bit类型
  只读
  Holding Registers
  16-bit类型
  读写
  1.3寄存器地址分配
  表1.3 MODBUS寄存器地址分配
  寄存器PLC地址
  寄存器协议地址
  适用功能
  寄存器种类
  读写状态
  描述
  00001-09999
  0000H-FFFFH
  01H 05H 0FH
  线圈状态
  可读可写
  10001-19999
  0000H-FFFFH
  02H
  离散输入状态
  可读
  30001-39999
  0000H-FFFFH
  04H
  输入寄存器
  可读
  每个寄存器表示一个16-bit无符号整数(0~65535)
  40001-49999
  0000H-FFFFH
  03H 06H 0FH
  保持寄存器
  可读可写
  两个连续16-bit寄存器表示一个浮点数
  50001-59999
  0000H-FFFFH
  03H 04H 06H 0FH
  ASCII字符
  可读可写
  每个寄存器表示两个ASCII字符
  1.4寄存器种类说明
  表1.4 MODBUS寄存器种类说明
  寄存器种类
  说明
  PLC类比
  举例说明
  线圈
  状态
  输出端口。可设定端口的输出状态,也可以读取该位的输出状态。可分为两种不同的执行状态,例如保持型或边沿触发型。
  DO
  数字量输出
  电磁阀输出,MOSFET输出,LED显示等。
  离散
  输入状态
  输入端口。通过外部设定改变输入状态,可读但不可写。
  DI
  数字量输入
  拨码开关,接近开关等。
  保持
  寄存器
  输出参数或保持参数,控制器运行时被设定的某些参数。可读可写。
  AO
  模拟量输出
  模拟量输出设定值,PID运行参数,变量阀输出大小,传感器报警上限下限。
  输入
  寄存器
  输入参数。控制器运行时从外部设备获得的参数。可读但不可写。
  AI
  模拟量输入
  模拟量输入
  1.5 PLC地址和协议地址区别
  PLC地址可以理解为协议地址的变种,在触摸屏和PLC编程中应用较为广泛。
  1.5.1寄存器PLC地址
  寄存器PLC地址指存放于控制器中的地址,这些控制器可以是PLC,也可以使触摸屏,或是文本显示器。PLC地址一般采用10进制描述,共有5位,其中第一位代码寄存器类型。第一位数字和寄存器类型的对应关系如表1所示。PLC地址例如40001、30002等。
  1.5.2寄存器协议地址
  寄存器协议地址指指通信时使用的寄存器地址,例如PLC地址40001对应寻址地址0x0000,40002对应寻址地址0x0001,寄存器寻址地址一般使用16进制描述。再如,PLC寄存器地址40003对应协议地址0002,PLC寄存器地址30003对应协议地址0002,虽然两个PLC寄存器寄存器通信时使用相同的地址,但是需要使用不同的命令访问,所以访问时不存在冲突。
  五、注意事项
  1、MODBUS 3.5T是如何计算的?
  T = 1000毫秒*(1起始位+数据位+奇偶校验+停止位)/波特率
  如果你的通讯方式是:波特率115200(表示每秒传输多少字符),数据位8,无奇偶校验。那么你发送一个字符的时间是:T=1000* (1起始位+8数据位+0奇偶校验+1停止位)/ 115200=0.087ms。
  发送端:发送一帧后延时7*T(其中3.5T是停止时间,3.5T是起始时间)再发送第二帧,保证一帧数据里头各字节间间隔延时不能超过1.5T。
  接收端:接收一个字节,查询2T时间,是否有接收到下一个字节,有则这帧数据未完,继续循环接收;没有则默认这帧已经接收完毕。
  2、串口超时时间设置
  COMMTIMEOUTS timeout;
  //填充timeout结构
  GetCommTimeouts(m_h232Port,&timeout);
  timeout.ReadIntervalTimeout=100;//两字符之间最大的延时
  timeout.ReadTotalTimeoutConstant=500;
  timeout.ReadTotalTimeoutMultiplier=0;//读取每字符间的超时
  timeout.WriteTotalTimeoutConstant=2000;
  timeout.WriteTotalTimeoutMultiplier=60;//写入每字符间的超时
  BOOLerror=SetCommTimeouts(m_hPort,&timeout);
  ReadIntervalTimeout:两字符之间最大的延时,当读取串口数据时,一旦两个字符传输的时间差超过该时间,读取函数将返回现有的数据。设置为0表示该参数不起作用。
  ReadTotalTimeoutMultiplier:读取每字符间的超时。
  ReadTotalTimeoutConstant:一次读取串口数据的固定超时。所以在一次读取串口的操作中,其超时为ReadTotalTimeoutMultiplier乘以读取的字节数再加上 ReadTotalTimeoutConstant。将ReadIntervalTimeout设置为MAXDWORD,并将ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant设置为0,表示读取操作将立即返回存放在输入缓冲区的字符。
  WriteTotalTimeoutMultiplier:写入每字符间的超时。
  WriteTotalTimeoutConstant:一次写入串口数据的固定超时。所以在一次写入串口的操作中,其超时为WriteTotalTimeoutMultiplier乘以写入的字节数再加上 WriteTotalTimeoutConstant。
  3、在程序中如果要用到多个串口,而且还要做很多复杂的处理,那么最好不用MSComm通讯控件,网络上有很多封装好的串口类。
  4、如何写稳定的modbus代码?
  需要从稳定性和易读性来考虑,如果稳定性较差会造成系统控制故障,如果易读性差就会造成难以维护,这些控制指令之间差别很小,如果一个一个单独写命令,非常容易出错。对应举措如下:
  1)避免每个指令写一部分代码,需要统一处理,比如校验函数,发送函数,接收函数等。
  通信协议已知,从中可以知道通信的实际数据长度(不包含包头包尾和校验的部分),所以可以控制读写多少个字节,并且可以知道什么时候启动校验,那些数据参与校验计算。
  2)为指令建立指令列表,这样后来需要添加功能,就可以直接把指令字加入列表即可。
  3)适当抽象。为每个指令的动作写回调函数,这样就可以在应用层,用一句简单的回调函数指针直接操作具体的动作函数,而不是应用逻辑层操作具体的驱动层面的接口。
  4)超时,必须有超时机制。通信失败怎么处理?通信之后一般线断了怎么处理?不能让系统死等后面的几个字节发过来。
  5)接收超时机制,不能依靠数传输的字节个数来停止接收来和区分帧间隔,因为可能通信就是断掉了,所以要按照协议,串口通信情况下3~5个T空闲就认为一帧结束。也可以通过协议事先计算出接收数据的总时间(字符数*(字符时间+字符间间隔时间)),使用该使用作为等待超时时间,但最长超时时间不能超过5T时间。
  6)响应超时机制,Modbus是请求/应答式通信,那么主机就需要知道到底多久从机才会应答,主机等待从机应答的最长时间就是从机的最大回复间隔,超过这个时间后从机即使已经完成计算也不能回复,因为此时主机可能已经开始给其他从机发送数据了。
  7)如果通信都是由你发起的,那么无论此次通信是完成还是超时或失败,都应该关闭通信端口。
  8)如果是线程中操作串口,可以提升线程优先级,让操作系统在一定程度上提高串口读写性能。
  9)ModbusRTU设备参数设置如下:
  (1)内部属性:单击“查看设备内部属性” ,点击按钮进入内部属性
  (2)最小采集周期:组态软件对设备进行操作的时间周期, 单位为 ms, 默认为100ms,根据采集数据量的大小,设置值可适当调整
  (3)设备地址:必须和实际设备的地址相一致,范围为0-255,默认值为 0。
  (4)通讯等待时间:通讯数据接收等待时间,默认设置为 200ms,根据采集数据量的大小,设置值可适当调整。
  (5)快速采集次数:对选择了快速采集的通道进行快采的频率(已不使用,为与老驱动兼容,故保留,无需设置) 。
  (6)16位整数解码顺序:调整字元件的解码顺序,对于Modicon PLC 及标准 PLC设备,使用默认值即可。
  16 位整数解码顺序 举例:0x0001
  0―12 表示字元件高低字节不颠倒(默认值) 表示 1
  1―21 表示字元件高低字节颠倒 表示 256
  (7)32位整数解码顺序:调整双字元件的解码顺序,对于Modicon PLC,请设置为“2-3412”顺序解码。
  32 位整数解码顺序 举例: 0x0000 0001
  0―1234 表示双字元件不做处理直接解码(默认值) 表示 1
  1―2143 表示双字元件高低字不颠倒,但字内高低字节颠倒 表示256
  2—3412表示双字元件高低字颠倒,但字内高低字节不颠倒 表示65536
  3—4321表示双字元件内4个字节全部颠倒 表示 16777216
  (8)32位浮点数解码顺序:调整双字元件的解码顺序,对于Modicon PLC,请设置为“2-3412”顺序解码。
  32 位浮点数解码顺序 举例:0x3F80 0000
  0―1234 表示双字元件不做处理直接解码(默认值) 表示 1.0
  1―2143 表示双字元件高低字不颠倒,但字内高低字节颠倒 表示-5.78564e-039
  2—3412表示双字元件高低字颠倒,但字内高低字节不颠倒 表示2.27795e-041
  3—4321表示双字元件内4 个字节全部颠倒 表示 4.60060e-041
  (9)校验方式: 选择LRC校验值的组合方式, 对于 Modicon PLC及标准 PLC 设备,
  使用默认设置即可。
  0—LH[低字节,高字节]:校验结果为2 个字节,低字节在前,高字节在后。
  1—HL[高字节,低字节]:校验结果为2 个字节,高字节在前,低字节在后。默认值。
  (10)分块采集方式:驱动采集数据分块的方式,对于Modicon PLC及标准 PLC设备,使用默认设置可以提高采集效率。
  0— 按最大长度分块:采集分块按最大块长处理,对地址不连续但地址相近的多个分块,分为一块一次性读取,以优化采集效率。
  1— 按连续地址分块:采集分块按地址连续性处理,对地址不连续的多个分块,每次只采集连续地址,不做优化处理。
  例如:有4区寄存器地址分别为 1~5,7,9~12的数据需采集,如果选择“0-按最大长度分块” ,则两块可优化为地址1~12的数据打包1次完成采集;如果选择“1-按连续地址分块” ,则需要采集 3 次。
  (11)4区16 位写功能码选择:写 4 区单字时功能码的选择,这个属性主要是针对自己制作设备的用户而设置的,这样的设备4区单字写可能只支持 0x10 功能码,而不支持0x06 功能码。
  0—0x06:单字写功能码使用0x06。
  1—0x10:单字写功能码使用0x10。
  注意:
  (1)。 “解码顺序”及“校验方式”设置:主要是针对非标准 ModbusRTU 协议的不同解码及校验顺序。当用户通过本驱动软件与设备通讯时,如果出现解析数据值不对,或者通讯校验错误(通讯状态为3),可与厂家咨询后对以上两项进行设置。而对于ModiconPLC及支持标准ModbusRTU 的 PLC 及控制器等设备,一般需将“32位整数解码顺序”和“32位浮点数解码顺序”设置为“2-3412” 。 另外,在使用本驱动与“Modbus 串口数据转发设备”构件通讯时, “解码顺序”及“校验方式”均需按默认值设置,否则会导致通讯失败或解析数据错误。
  (2)。 “分块采集方式”设置:主要是针对非标准 ModbusRTU协议设备。当用户通过本驱动软件与设备通讯时,如果按默认“0-按最大长度分块”时,出现读取连续地址正常,而不连续地址不正常时,可与厂家咨询,并设置为“1-按连续地址分块方式”尝试是否可正常通讯。 而对于 Modicon PLC 及支持标准 ModbusRTU 的 PLC 及控制器等设备,直接使用默认设置即可,这样可以提高采集效率。
  5、lrc和crc校验算法
  //--------------------------------------------------------------------------
  // Constants
  //--------------------------------------------------------------------------
  static const uint16_t modbus_crc_table[256] = {
  0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
  0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
  0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
  0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
  0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
  0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
  0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
  0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
  0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
  0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
  0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
  0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
  0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
  0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
  0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
  0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
  0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
  0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
  0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
  0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
  0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
  0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
  0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
  0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
  0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
  0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
  0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
  0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
  0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
  0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
  0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
  0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
  };
  //--------------------------------------------------------------------------
  // Modbus functions
  //--------------------------------------------------------------------------
  uint8_t modbus_lrc_calc(uint8_t *data, uint16_t len)
  {
  uint8_t lrc = 0U;
  int i;
  for (i = 0U; i 《 len; i++)
  {
  lrc += data;
  }
  lrc = (0xFFU - lrc)+1U;
  return(lrc);
  }
  uint16_t modbus_crc_calc(uint8_t *buffer, uint16_t size)
  {
  uint16_t crc = 0xFFFFU;
  uint8_t nTemp;
  while (size--)
  {
  nTemp = *buffer++ ^ crc;
  crc 》》= 8;
  crc ^= modbus_crc_table[(nTemp & 0xFFU)];
  }
  return(crc);
  }
  LRC算出来的是16进制的字节值,若发送的数据是字符串形式,不需任何转换,直接放在字符串最后位置就行了。若发送的数据是数组形式,则需要转换为ASCII值,即由1字节的16进制转换为2字节的ASCII值,4D应该转换为34 44。
  如果你的发送指令是:com.outpu,就是字符串形式;
  如果发送指令形式是:com.output=A[],就是数组形式。
  但不管是哪种形式,ModBus ASCII模式最终在数据线上的数据都是ASCII值。字符串形式的数据,编译软件会自动转换。
  6、调试工具
  Modbus Poll是一个Modbus管理模拟器软件,帮助开发人员进行管理和监控的Mod bus数据区在同一时间和模拟Mod bus协议。支持Modbus RTU / ASCII和Modbus TCP / IP协议。
  下载地址:http://www.downcc.com/soft/25945.html
  modbus slave是一款功能强大的modbus子设备模拟工具,可以帮助modbus通讯设备开发人员进行modbus通讯协议的模拟和测试,用于模拟、测试、调试modbus通讯设备。
  7、modbus开源项目
举报

更多回帖

发帖
×
20
完善资料,
赚取积分