关于Modbus协议
Modbus协议是MODICON(莫迪康)(现施耐德品牌)在1979年开发的,是全球第一个真正用于现场的总线协议;
Modbus协议是应用于电子控制器上的一种通用语言。通过此协议,可以实现控制器相互之间、控制器经由网络和其实设备之间的通信。
Modbus特点
-
标准开放、公开发表、无版税要求、无许可证费(没有费用)
-
支持多种电气接口(RS232、RS422、RS485、RJ45);各种介质传输(双绞线、网线)
-
格式简单、紧凑、通俗易懂,容易上手(好用)
Modbus总线通信环境
-
基本通信结构
-
从机编码
Modbus通信方式与分类
1. Modbus RTU (Remote Terminal Unit)
-
描述:Modbus RTU是一种在串行链路上使用二进制编码的消息传输模式。它通常用于点对点或一点对多点(星型拓扑)网络。
-
特点:
-
使用RS-232、RS-485等物理接口。
-
消息帧包含起始位、地址、功能码、数据域、校验和/循环冗余校验(CRC)以及停止位。
-
支持较长距离和较高的抗干扰能力。
-
适用于需要稳定性和可靠性的工业环境。
-
2. Modbus ASCII
-
描述:Modbus ASCII采用ASCII字符编码来表示消息帧中的每个字节,适合于不稳定的通信链路。
-
特点:
-
消息以冒号(:)开头,以回车换行符(CR/LF)结尾。
-
数据以十六进制形式发送,并且每两个字符代表一个字节。
-
校验方法为纵向冗余检查(LRC)。
-
速度较慢但易于调试和观察通信内容。
-
3. Modbus TCP/IP (Transmission Control Protocol/Internet Protocol)
-
描述:Modbus TCP/IP是基于以太网技术的标准Modbus协议扩展,它将Modbus请求封装到TCP/IP包中。
-
特点:
-
不需要定义物理地址,因为IP地址已经确定了目标设备。
-
在TCP/IP五层模型的应用层之上运行。
-
可以通过局域网、广域网甚至互联网实现远程监控和控制。
-
提供了更好的灵活性和可扩展性,适应现代工业自动化的需求。
-
Modbus协议下的数据存储
-
数据存储中的位bit (bool)、字节byte(8个位)、字Word(2个字节,16位)、双字DWord(2个字,4个字节,32位),C#中的数据显示:数据类型、显示格式
存储区 | 对象类型 | 访问类型(针对程序) | 存储区标识 | 说明 |
---|---|---|---|---|
线圈状态 Q | 单个bit | 读写 | 0XXXX | 通过应用程序改变这种类型数据 |
输入线圈 I | 单个bit | 只读 | 1XXXX | I/O系统提供这种类型数据 |
输入寄存器 | 16-位 字 | 只读 | 3XXXX | I/O系统提供这种类型数据 |
保持寄存器 | 16-位 字 2个字节 | 读写 | 4XXXX | 通过应用程序改变这种类型数据 |
操作存储器的命令
常用功能码:01、02、03、04、05、06、15、16
功能码 16进制 | 名称 | 功能 |
---|---|---|
01 | 读线圈状态 | 读位(读N个bit)---读从机线圈寄存器,位操作 |
02 | 读输入离散量 | 读位(读N个bit)---读离散输入寄存器,位操作 |
03 | 读多个保持型寄存器 | 读整型、字符型、状态字、浮点型(读N个words)---读保持寄存器,字节操作 |
04 | 读多个输入寄存器 | 读整型、状态字、浮点型(读N个words)---读输入寄存器,字节操作 |
05 | 写单个线圈 | 写位(写一个bit)---写线圈寄存器,位操作 |
06 | 写单个保持寄存器 | 写整型、字符型、状态字、浮点型(写一个word)---写保持寄存器,字节操作 |
07 | 读取异常状态 | 取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 |
08 | 回送诊断校验 | 把诊断校验报文送从机,以对通信处理进行评鉴 |
09 | 编程(只用于484) | 使主机模拟编程器作用,修改PC从机逻辑 |
0A | 控询(只用于484) | 可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 |
0B | 读取事件计数 | 可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 |
0C | 读取通讯事件记录 | 可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 |
0D | 编程(184/384/484/584) | 可使主机模拟编程器功能修改PC从机逻辑 |
0E | 探询(184/384/484/584) | 可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 |
0F | 写多个线圈 | 可以写多个线圈---强置一串连续逻辑线圈的通断 |
10 | 写多个保持寄存器 | 写多个保持寄存器---把具体的二进制值装入一串连续的保持寄存器 |
11 | 报告从机标识 | 可使主机判断编址从机的类型及该从机运行指示灯的状态 |
12 | (884和MICRO84) | 可使主机模拟编程功能,修改PC状态逻辑 |
13 | 重置通信链路 | 发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 |
14 | 读取通用参数(584L) | 显示扩展存储文件中的数据信息 |
15 | 写入通用参数(584L) | 把通用参数写入扩展存储文件 |
16~40 | 保留做扩展功能备用 | <font style="color:#595959;"></font> |
41~48 | 保留以备用户功能所用 | 留作用户功能的扩展编码 |
49~77 | 非法功能 | <font style="color:#595959;"></font> |
78~7F | 保留 | 留作内部作用 |
80~FF | 保留 | 用于异常应答 |
Modbus RTU 协议报文格式
读寄存器消息帧格式
功能码: 0x03、0x04
请求:
从站地址 | 功能码 | 起始地址 | 读取长度(2byte->16bit) | CRC | ||
---|---|---|---|---|---|---|
01 | 03 | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | C5 |
响应:
从站地址 | 功能码 | 字节数 | 寄存器值(1) | 寄存器值(2) | 寄存器值(...) | 寄存器值(20) | CRC | |||
---|---|---|---|---|---|---|---|---|---|---|
01 | 03 | 14 | 00(Hi) | 00(Lo) | 00(Hi) | 00(Lo) | …… | …… | 00(Hi) | 00(Lo) |
写单寄存器消息帧格式
功能码:0x06
请求、响应:
从站地址 | 功能码 | 写入地址 | 写入值(2) | CRC | ||
---|---|---|---|---|---|---|
01 | 06 | 00(Hi) | 00(Lo) | 00(Hi) | 00(Lo) | XX |
写多寄存器消息帧格式
功能码:0x10
请求:
从站地址 | 功能码 | 写入地址 | 写入数量 | 字节数 | 写入值 | CRC | ||
---|---|---|---|---|---|---|---|---|
01 | 10 | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | 04 | 0A | AB |
响应:
从站地址 | 功能码 | 写入地址 | 写入数量 | CRC | ||
---|---|---|---|---|---|---|
01 | 0F | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | XX |
读线圈消息帧格式
功能码:0x01、0x02
请求:
从站地址 | 功能码 | 起始地址 | 读取长度 | CRC | ||
---|---|---|---|---|---|---|
01 | 01 | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | XX |
响应:
从站地址 | 功能码 | 字节数 | 输出状态 7-0 | 输出状态 15-8 | CRC | |
---|---|---|---|---|---|---|
01 | 01 | 02 | 00 | 00 | XX | XX |
写单线圈消息帧格式
功能码:0x05
请求、响应:
从站地址 | 功能码 | 写入地址 | 写入值 | CRC | ||
---|---|---|---|---|---|---|
01 | 05 | 00(Hi) | 00(Lo) | FF(Hi)/00(Hi) | 0A(Lo) | XX |
写单线圈消息帧格式
功能码:0x0F
请求:
从站地址 | 功能码 | 写入地址 | 写入数量 | 字节数 | 写入值 | CRC | ||
---|---|---|---|---|---|---|---|---|
01 | 0F | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | 02 | 0A(Lo) | AB(Lo) |
响应:
从站地址 | 功能码 | 写入地址 | 写入数量 | CRC | ||
---|---|---|---|---|---|---|
01 | 0F | 00(Hi) | 00(Lo) | 00(Hi) | 0A(Lo) | XX |
CRC校验码计算:
void CRC16(List<byte> value, ushort poly = 0xA001, ushort crcInit = 0xFFFF)
{
if (value == null || !value.Any())
throw new ArgumentException("");
//运算
ushort crc = crcInit;
for (int i = 0; i < value.Count; i++)
{
crc = (ushort)(crc ^ (value[i]));
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
byte lo = (byte)(crc & 0x00FF); //低位置
List<byte> buffer = new List<byte>();
value.Add(lo);
value.Add(hi);
}
Modbus Ascii 协议处理
格式变化
-
将RTU基础报文(除CRC校验部分,PDU)可用字符转对应ASCII编码
-
利用转换后的ASCII编码数组进行LRC校验码计算
-
添加起始(: 3A)结束字符(换行回车 0D 0A)
传输示例
要读取地址 0x20c1的两个寄存器需要发送以下 ASCII 消息:
010420C1000218<CRLF>
请求:
-
: -消息开始-0x3A
-
01-从站地址-0x01
-
04-功能码 (读取输入寄存器)-0x04
-
20C1-要读取的寄存器地址-0x20C1
-
0002 -要读取的寄存器长度(必须是 2)-0x0002
-
18-LRC 校验码
-
<CRLF>-消息结束,回车换行-0x0D0A
LRC校验码:
public void LRC(List<byte> value)
{
if (value == null) return;
int sum = 0;
for (int i = 0; i < value.Count; i++)
{
sum += value[i];
}
sum = sum % 256;
sum = 256 - sum;
value.Add((byte)sum);// 16进制一个字节
}
Modbus TCP 协议处理
MBAP:Modbus Application Protocol (modbus报文头) - **固定7个字节**
-
事务标识符(Transaction Identifier) - 2 字节
这是一个唯一标识符,用于匹配请求和响应。在请求和响应中,这个字段的值是相同的。
-
协议标识符(Protocol Identifier) - 2 字节
对于 Modbus TCP,这个字段的值始终是 0x0000。
-
长度字段(Length Field) - 2 字节
表示接下来的单元标识符、功能码和数据字段的字节数。不包括事务标识符、协议标识符和长度字段本身。
-
单元标识符(Unit Identifier) - 1 字节
也称为设备地址或从站地址。在 Modbus TCP 中,这个字段用于标识网络上的 Modbus 从设备。