ModbusRTU协议帧详解
- 一.协议帧表格
- 二.读取发送帧和返回帧格式
- 三.写数据发送帧和返回帧格式
- 0x10(16功能码,写多个寄存器)写数据格式
- 0x06(06功能码,写单个寄存器)写数据格式
- 0x05(05功能码,写开关位)写数据格式
- 四.功能码定义
- 五.注意事项,必看
- 六.CRC校验计算C/C++
- 七.字节和浮点型相互转换C/C++
- 八.Qt实现modbusRTU/TCP主站demo
一.协议帧表格
二.读取发送帧和返回帧格式
读取帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 读取寄存个数高字节 | 读取寄存个数低字节 | CRC低字节 | CRC高字节 |
读取帧格式: | 0X01 | 0x03 | 0x00 | 0x00 | 0x00 | 0x02 | xx | xx |
返回帧格式: | 地址 | 功能码 | 数据长度(读取寄存器个数乘以2) | 数据部分 | CRC低字节 | CRC高字节 |
返回帧格式: | 0X01 | 0x03 | 0x04 | xxxxx | xx | xx |
三.写数据发送帧和返回帧格式
0x10(16功能码,写多个寄存器)写数据格式
写入帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入寄存个数高字节 | 写入寄存个数低字节 | 写入数据长度 | 写入数据 | CRC低字节 | CRC高字节 |
写入帧格式: | 0X01 | 0x10 | 0x00 | 0x00 | 0x00 | 0x02 | 0x04 | xxxx | xx | xx |
返回帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入寄存个数高字节 | 写入寄存个数低字节 | CRC低字节 | CRC高字节 |
返回帧格式: | 0X01 | 0x10 | 0x00 | 0x00 | 0x00 | 0x02 | xx | xx |
0x06(06功能码,写单个寄存器)写数据格式
写入帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入数据高字节 | 写入数据低字节 | CRC低字节 | CRC高字节 |
写入帧格式: | 0X01 | 0x06 | 0x00 | 0x00 | xx | xx | xx | xx |
返回格式个写入格式一致
返回帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入数据高字节 | 写入数据低字节 | CRC低字节 | CRC高字节 |
返回帧格式: | 0X01 | 0x06 | 0x00 | 0x00 | xx | xx | xx | xx |
0x05(05功能码,写开关位)写数据格式
写入帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入数据高字节 | 写入数据低字节 | CRC低字节 | CRC高字节 |
写入帧格式(置1): | 0X01 | 0x05 | 0x00 | 0x00 | 0xFF | 0x00 | xx | xx |
写入帧格式(置0): | 0X01 | 0x05 | 0x00 | 0x00 | 0x00 | 0x00 | xx | xx |
返回格式个写入格式一致
写入帧格式: | 地址 | 功能码 | 起始地址高字节 | 起始地址低字节 | 写入数据高字节 | 写入数据低字节 | CRC低字节 | CRC高字节 |
返回帧格式(置1): | 0X01 | 0x05 | 0x00 | 0x00 | 0xFF | 0x00 | xx | xx |
返回帧格式(置0): | 0X01 | 0x05 | 0x00 | 0x00 | 0x00 | 0x00 | xx | xx |
四.功能码定义
功能码 | 描述 |
0x01 | 读取开关量输出,可读写(开关量读取是按位读取的(不是按寄存数目读取的),所以返回的字节数目并不是读取的寄存器个数乘以2,是读取的开关量个数除以8的商值加1(加不加1看余数是不是0,余数非零就加1)),可以被05和0F(15)功能码写 |
0x02 | 读取开关量输入,只读(开关量读取是按位读取的(不是按寄存数目读取的),所以返回的字节数目并不是读取的寄存器个数乘以2,是读取的开关量个数除以8的商值加1(加不加1看余数是不是0,余数非零就加1)) |
0x03 | 读取数值量,定义上能被03的读取的可以被06和10写 |
0x04 | 读取数值量,定义只能读不能写,实际需要根据实际情况来(如果开放可以被06和10写) |
0x05 | 写开关量,只对01可以读取的数据写 |
0x06 | 写单个寄存,对能被03功能码读取的寄存器进行写数据操作 |
0x10 | 写多个寄存器,对能被03功能码读取的寄存器进行写数据操作 |
五.注意事项,必看
1.一个寄存器等于两个字节
2.(01和02功能码)开关量读取是按位读取的(不是按寄存数目读取的),所以返回的字节数目并不是读取的寄存器个数乘以2,是读取的开关量个数除以8的商值加1(加不加1看余数是不是0,余数非零就加1),比如读取7个开关量,返回数据长度就是(7/8),商是0余数是1,所以数据部分返回1个字节的长度,比如读取10个开关量,返回数据长度就是(10/8),商是1余数是2,所以数据部分返回2个字节的长度
六.CRC校验计算C/C++
xxx.h
unsigned short int CheckCRC_16(const unsigned char* data,const unsigned int sIndex=0,const unsigned int length=0);
xxx.c
unsigned short int CheckCRC_16(const unsigned char *data, const unsigned int sIndex, const unsigned int length)
{
unsigned int CRCreg=0xFFFF;
int len=length+sIndex;
for(int i=sIndex;i<len;i++)
{
CRCreg=CRCreg^data[i];
for(int j=0;j<=7;j++)
{
if(CRCreg&0x01)
{
CRCreg=(CRCreg>>1)^0xa001;
}
else
{
CRCreg=CRCreg>>1;
}
}
}
return CRCreg;
}
七.字节和浮点型相互转换C/C++
//以C语言转换为例:转换30.96(16进制41 F7 AE 14)
#include<stdio.h>
//使用联合体来实现
typedef union _HexFloat
{
char hex[4];
float fval;
}_HexFloat;
int main()
{
_HexFloat HexFloat;
//将16进制值分别放进去 ,如果得出的值不对就交换位置
HexFloat.hex[3]=0x41;
HexFloat.hex[2]=0xf7;
HexFloat.hex[1]=0xae;
HexFloat.hex[0]=0x14;
printf("fval十进制的值为=%f\n",HexFloat.fval);
return 0;
}
八.Qt实现modbusRTU/TCP主站demo
程序只做了modbusRTU的串口demo,但是封装的类可以选择摩登busTCP协议