欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

modbus协议学习笔记

程序员文章站 2022-07-09 13:46:27
...

一.简介

1.介绍

  1. Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

  2. 有两种串行传输模式被定义: RTU(Remote Terminal Unit) 模式 和 ASCII 模式。
    它定义了报文域的位内容在线路上串行的传送。它确定了信息如何打包为报文和解码。
    Modbus 串行链路 上所有设备的传输式 模式 ( ( 和串行口参数) ) 必须 相同。
    尽管在特定的领域 ASCII 模式是要求的,但达到 Modbus 设备之间的互操作性只有每个设备都有
    相同的模式: 所有设备 必须现 必须实现 RTU 模式。 ASCII 传输模式是选项。
    设备应该由用户设成期望的模式, RTU 或 ASCII。 默认设置必须为 RTU 模式。

  3. 对于串行连接两个传输模式,它们在数值数据表示不同和协议细节上略有不同。Modbus RTU是一种紧凑的,采用二进制表示数据的方式由于我们进行通信的一般数据包为2进制形式,所以我们一般都采用RTU模式),Modbus ASCII是一种人类可读的,冗长的表示方式。这两个变种都使用串行通信(serial communication)方式。RTU(Remote Terminal Unit)格式后续的命令/数据带有循环冗余校验(CRC)的校验和,而ASCII格式采用纵向冗余校验的校验和。被配置为RTU变种的节点不会和设置为ASCII变种的节点通信,反之亦然。

  4. MODBUS 是一个请求/应答协议,并且提供功能码规定的服务MODBUS 功能码是 MODBUS
    请求/应答 PDU 的元素。

2.协议描述

MODBUS 协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网络上
的 MODBUS 协议映射能够在应用数据单元(ADU)上引入一些附加域。

modbus协议学习笔记
通过这个数据帧并对应相应功能实现数据包的传输

二.关键知识点

1.常用功能码寄存器的理解

modbus协议完整支持很多功能码,但是实际在应用的不多。具体如下:
0x01 :读线圈寄存器(注意:这些寄存器属于软件编写的给一段空间来定义相关参数)
0x02 :读离散输入寄存器
0x03 :读保持寄存器
0x04 :读输入寄存器
0x05 :写单个线圈寄存器
0x06 :写单个保持寄存器
0x0f :写多个线圈寄存器
0x10 :写多个保持寄存器
modbus协议学习笔记
分析:

  1. 线圈寄存器的读写实质上可以通过led来理解,对定义的每个bit进行读写,通过远程通信发送的数据帧,根据modbus协议定义的线圈寄存器,与调用led转换的代码,与之间接实现控制led的IO口,我的理解就是比如在我的有一篇博客中讲sx1278芯片寄存器的功能与读写,而这里就是用代码来实现寄存器的位数与其他功能模块调用挂钩,以实现控制的原理。
  2. 离散输入寄存器按照线圈寄存器理解一样的,只是离散输入寄存器规定只读模式,比如只能读取LED的IO口状态 。
  3. 对于保持寄存器则是对2byte数据进行读写,最基本的功能就是自定义数据类型,然后主机模块的测量温度的传感器的数据(16进制2byte)发送给从机接收,再由从机直接把数据通过串口显示出来。
  4. 输入寄存器与保持寄存器类似,但也是只支持读而不能写。

2.发送数据帧单元的理解

用主机模块利用功能码0x01写单个线圈寄存器的bit位来控制从机模块中3个led灯的亮灭举例:

从机地址 功能码 寄存器首地址 寄存器数量 crc 效验码
01 01 1001 0003 290B

分析:

  1. 从机地址,0x01这个我们在代码中设置即可。
  2. 功能码,0x01读线圈寄存器。
  3. 起始地址,0x1001也是在协议代码中定义。
  4. 数量,0x0003,3个led的状态。
  5. CRC效验码,根据一种crc算法计算,在确定了前面6个字节后自动加效验码(协议代码中体现)。

而在从机接收中,通过相应的函数对发送过来的数据帧一步一步通过判断并执行相应的功能,因为在协议标准代码中可以规定相应地址线圈的bit位相对应的功能并执行。

3.对modbus协议检错(异常相应)的理解

当客户机设备向服务器设备发送请求时,客户机希望一个正常响应。从主站询问中出现下列四种可能事件之一:

  1. 如果服务器设备接收到无通信错误的请求,并且可以正常地处理询问,那么服务器设备将
    返回一个正常响应。
  2. 如果由于通信错误,服务器没有接收到请求,那么不能返回响应。客户机程序将最终处理
    请求的超时状态。
  3. 如果服务器接收到请求,但是检测到一个通信错误(奇偶校验、LRC、CRC、…),那么不
    能返回响应。客户机程序将最终处理请求的超时状态。
  4. 如果服务器接收到无通信错误的请求,但不能处理这个请求(例如,如果请求读一个不存
    在的输出或寄存器),服务器将返回一个异常响应,通知用户错误的本质特性。

异常响应报文有两个与正常响应不同的域

  1. 功能码域 :在正常响应中,服务器利用响应功能码域来应答最初请求的功能码。所有功能码的
    最高有效位(MSB)都为 0(它们的值都低于十六进制 80)。在异常响应中,服务器设置功能码的MSB 为 1。这使得异常响应中的功能码值比正常响应中的功能码值高十六进制 80。
    通过设置功能码的 MSB,客户机的应用程序能够识别异常响应,并且能够检测异常码的数据域。
  2. 数据域 :在正常响应中,服务器可以返回数据域中数据或统计表(请求中要求的任何报文)。在
    异常响应中,服务器返回数据域中的异常码。这就定义了产生异常的服务器状态。

对应这两种不同域的异常相应的处理RTU应答代码(这些在从机检测并返回给主机):

/* RTU 应答代码 */

#define RSP_OK				0		/* 成功 */
#define RSP_ERR_CMD			0x01	/* 不支持的功能码 */
#define RSP_ERR_REG_ADDR	0x02	/* 寄存器地址错误 */
#define RSP_ERR_VALUE		0x03	/* 数据值域错误 */
#define RSP_ERR_WRITE		0x04	/* 写入失败 */

在接收时设置函数,分别判断接收缓冲区的每个字节位,判断跟我们设置的标准有无出入即可以判断是哪部分错误,比如如果我们判断功能域不为我们所规定的的那常见的几个,我们就判断功能域错误,也就是从机返回的数据帧中数据域代码为RSP_ERR_CMD 0x01/* 不支持的功能码 */。其他的类似判断。

对于这个协议标准,还得完全吃透,先了解总的构架,在对其各个突破。加油!