常见的功能码有01、02、03、04、05、06、15、16等,分别表示着读线圈状态、读离散输入状态、读保持寄存器、读输入寄存器、写单个线圈、写单个保持寄存器、写多个线圈、写多个保持寄存器的功能。
寄存器分区:
线圈,可以看作是一个可读可写的位变量,Modbus RTU支持对其的读写操作。允许多位操作。
离散输入寄存器,可以看作是一个只读的位变量,Modbus RTU支持对其的读操作。
保持寄存器,可以看作是一个可读可写的字节变量,Modbus RTU支持对其的读写操作。允许多字节操作。一个保持寄存器为2个字节。
输入寄存器,可以看作是一个只读的字节变量,Modbus RTU支持对其的读操作。一个输入寄存器为2个字节。
寄存器地址:Modbus RTU的寄存器地址有00001~09999(0区,表示线圈寄存器)、10001~19999(1区,表示离散输入寄存器)、30001~39999(3区,表示输入寄存器)、40001~49999(4区,表示保持寄存器),其中3区和4区,每个寄存器由2个字节组成。
注意:在Modbus二进制数据指令里,表示寄存器地址的指令数据是从0开始的,Modbus RTU的寄存器地址是从1开始,注意对应关系。
用功能码是可以识别到Modbus寄存器分区的,所以在Modbus二进制数据指令里,是不填写分区代码的,这在第四、节的案例里可以看出对应关系。
3.3 Modbus RTU的数据位
Modbus RTU的数据位根据不同的功能码有不同的长度。
3.4 Modbus RTU的数据校验
Modbus RTU采用CRC-16校验,对一个数据帧里校验数据前面所有的数据进行CRC校验,得出的校验结果为2个字节,低字节在前(先发),高字节在后(后发)。
一个参考的单片机CRC计算C程序如下:
#include "crc16.h"
unsigned short modbus_crc_16(unsigned char *adata,unsigned int asize) //CRC计算:计算结果为16位数据,CRC低字节在左,高字节在右
{
unsigned short crc_out=0xffff;
unsigned int i,j;
unsigned char crc_low,crc_high;
for(i=0;i>=1;
crc_out^=0xa001;
}
else
{
crc_out>>=1;
}
}
}
//exchange high and low 8 bits
//业务联系:3w点yonko-tech点com, Q:二五三一26三七二六。
crc_low=(unsigned char)crc_out;
crc_high=(unsigned char)(crc_out>>8);
crc_out=(unsigned int)((crc_low<<8)+crc_high);
return crc_out;
}
四、 不理解吗?来点例子,Modbus RTU数据帧案例详解(重点)
为了更清晰地理解,本节介绍Modbus RTU的通信例子。本章节大部分内容引用自网络文献。
4.1 读取输出线圈状态
01功能码的作用是读取从站里输出线圈的状态,主站发送指令后从站响应并返回数据,返回的线圈数据由低位线圈到高位线圈,注意这里的线圈数量是表示有多少个二进制位。
关于CRC:
上图中从站返回的除了校验码的数据是0x11 0x 01 0x 04 0x cd 0x 6b 0x b2 0x 05,那么计算出来的CRC结果为0x 11 0x C3,其中0x 11是低字节,0x C3是高字节,那么完整的数据帧是:0x11 0x 01 0x 04 0x cd 0x 6b 0x b2 0x 05 0x 11 0x C3。CRC可以通过3.4节中的程序计算,或者使用网络上的CRC在线计算工具。
4.2 读取离散输入状态
02功能码的作用是读取从站输入线圈的状态,主站发送指令后从站响应并返回数据,返回的线圈数据由低位线圈到高位线圈,注意这里的线圈数量也是表示有多少个二进制位。
4.3 读取保持寄存器
03功能码的作用是读取从站保持寄存器的状态,主站发送指令后从站响应并返回数据,返回的寄存器数据由低位寄存器到高位寄存器,注意这里的每个寄存器有2个字节组成,寄存器先发低的再发高的,每个寄存器先发高字节,再发低字节。
4.4 读取输入寄存器
04功能码的作用是读取从站输入寄存器的状态,主站发送指令后从站响应并返回数据,返回的寄存器数据由低位寄存器到高位寄存器,注意这里的每个寄存器有2个字节组成,寄存器先发低的再发高的,每个寄存器先发高字节,再发低字节。
4.5 强制单个线圈
05功能码的作用是设置从站的单个线圈值,主站发送指令后从站响应并返回数据。
4.6 强制多个线圈
0F功能码的作用是设置从站的多个线圈值,主站发送指令后从站响应并返回数据。
4.7 预置单个寄存器
06功能码的作用是设置从站的单个寄存器值,主站发送指令后从站响应并返回数据。
4.8 预置多个寄存器
10功能码的作用是设置从站的多个寄存器值,主站发送指令后从站响应并返回数据。
编程时,可以把Modbus RTU的线圈看作为位变量,寄存器看作为双字节变量(一个寄存器为2个字节,16位)。
可以看出,Modbus RTU是主从模式,是主站发出指令,从站响应,从站不能直接主动地向主站发出指令。
Modbus RTU基本可以在所有串行通信里面使用,但是Modbus RTU一般在RS485通信里使用得较多一些。
后续大可能会写单片机与昆仑通态触摸屏通信的实操,如有兴趣可以关注避免失误。
如有错误,感谢指正。本文有一部分资料来自网络资源,感谢其他大牛的分享,绿水青山,后会有期,全文暂时完。
沙鸥 成都 2024-6