Modbus_RTU

本文主要记录串口通信,主要记录 modbus 的默认通信协议 modbus_RTU,当然modbus还包含 modbus_TCP(网口)和 modbus_ASCII(串口)。

一、基础知识

串口和网口
串口:串口是一种物理接口,通常用于连接计算机和外部设备,如打印机、鼠标等。它使用一根线缆进行数据传输,常见的接口有RS-232、RS-485等。串口通信是异步的,可以在一根线上同时发送和接收数据。
串口通信适用于需要简单、低成本通信的场景,如打印机、外部存储设备等。它也适用于需要长距离通信的应用,如工业控制系统中的远程监控。

网口:网口主要用于计算机网络连接,如以太网接口(Ethernet Port),通常通过RJ45接口连接,使用双绞线或光纤进行数据传输。网口通信支持网络协议,如TCP/IP,用于在局域网或广域网中进行数据传输。

 

基本名词
传输TX:TXD(Transmit Data)发送数据,用于数据的双向传输。
接收RX:RXD(Received Data)接收数据
地线GND:GND(Ground)代表地线或0线,公共端的意思,这个地并不是真正意义上的地,是出于应用而假设的地,对于电源来说,它就是一个电源的负极。用于建立电平参考,保证信号的稳定传输。

位:0或1,就是二进制里面的每一个数
字节:8个二进制组成一个字节
字符:一个汉字2个字节,一个英文字母1个字节

数据位:表示每次发送的数据长度,取值5~8
报文格式:起始位+报文(数据位)+停止位,其中0表示起始位,1和2表示结束位
大端序:大位在前,小位在后
波特率:1秒内能传输多少个高低电平,双方必须设置成一致
低电平:0~0.4V 
高电平:2.4~5V
RS232
RS232标准:转换高低电平的作用,减少感染,增加传输距离。把高电平位拉高到12V传输,把高电平转换成5V解析。传输距离最大15米,波特率2M,全双工通信(只能1对1)
RS485
RS485标准:通过转换芯片,把外部干扰的高低电平同时升高,大大增加了抗干扰的能力,增加传输距离。传输距离最大1200米,波特率50M,半双工通信(1主多从)

 

二、modbus

存储区
存储的数据类型分为 :布尔量 和 16位寄存器

布尔量:位操作。

16位寄存器: 字节操作,长度16位。2个字节,2的16次方。10进制最大值65536,因此值范围0~65535

区号

名称

读写

值类型

定义范围

实际范围

0区

输出线圈 Coil Status

可读写布尔量

布尔

00001~09999

00001~65535

1区

输入状态Input Status

只读布尔量

布尔

10001~19999

100001~165535

3区

输入寄存器Input Register

只读寄存器

2字节无符号

30001~39999

300001~365535

4区

保持(输出)寄存器 Holding Register

可读寄存器

2字节无符号

40001~49999

400001~465535

 
功能码
功能码 寄存器类型 读写状态 PLC地址 modbus地址 操作数据类型 操作数量
01H 读取线圈状态 可读可写 00001~09999 0000H~FFFFH 位操作 单个或多个
02H 读取输入状态 只读 10001~19999 0000H~FFFFH 位操作 单个或多个
03H 读取保持寄存器 可读可写 40001~49999 0000H~FFFFH 字节操作 单个或多个
04H 读取输入寄存器 只读 30001~39999 0000H~FFFFH 字节操作 单个或多个
05H 写入单线圈 可读可写 00001~09999 0000H~FFFFH 位操作   单个
06H 写入单寄存器 可读可写 40001~49999 0000H~FFFFH 字节操作 单个
0FH 写入多线圈 可读可写 00001~09999 0000H~FFFFH 位操作 单个
10H 写入多寄存器 可读可写 40001~49999 0000H~FFFFH 字节操作 多个

这里可以总结,输入都是只读,输出可读可写。

 

CRC

CRC全称循环冗余校验(Cyclic Redundancy Check, CRC),是通信领域数据传输技术中常用的检错方法,用于保证数据传输的可靠性。

CRC16算法过程

1 初始化一个16位的寄存器地址 用作初始值
2 遍历数据字节,从最高位到最低位, 
3 将数据字节与寄存器异或
4 对寄存器进行8次迭代,每一次迭代将寄存器右移一位
5 如果最低位位1,将寄存器与生成多项式0x8005异或,否则只进行右移操作
6 重复上述步骤直到遍历完所有的字节
7 最终寄存器的值就是crc16校验码
8 crc计算之后高低位进行互换

 

CRC16算法

    public static class CRC16
    {
        /// <summary>
        /// CRC校验,参数data为byte数组
        /// </summary>
        /// <param name="data">校验数据,字节数组</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(byte[] data)
        {
            //crc计算赋初始值
            int crc = 0xffff;
            for (int i = 0; i < data.Length; i++)
            {
                crc = crc ^ data[i];
                for (int j = 0; j < 8; j++)
                {
                    int temp;
                    temp = crc & 1;
                    crc = crc >> 1;
                    crc = crc & 0x7fff;
                    if (temp == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                    crc = crc & 0xffff;
                }
            }
            //CRC寄存器的高低位进行互换
            byte[] crc16 = new byte[2];
            //CRC寄存器的高8位变成低8位,
            crc16[1] = (byte)((crc >> 8) & 0xff);
            //CRC寄存器的低8位变成高8位
            crc16[0] = (byte)(crc & 0xff);
            return crc16;
        }
    
        /// <summary>
        /// CRC校验,参数为空格或逗号间隔的字符串
        /// </summary>
        /// <param name="data">校验数据,逗号或空格间隔的16进制字符串(带有0x或0X也可以),逗号与空格不能混用</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(string data)
        {
            //分隔符是空格还是逗号进行分类,并去除输入字符串中的多余空格
            IEnumerable<string> datac = data.Contains(",") ? data.Replace(" ", "").Replace("0x", "").Replace("0X", "").Trim().Split(',') : data.Replace("0x", "").Replace("0X", "").Split(' ').ToList().Where(u => u != "");
            List<byte> bytedata = new List<byte>();
            foreach (string str in datac)
            {
                bytedata.Add(byte.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier));
            }
            byte[] crcbuf = bytedata.ToArray();
            //crc计算赋初始值
            return CRCCalc(crcbuf);
        }
    
        /// <summary>
        ///  CRC校验,截取data中的一段进行CRC16校验
        /// </summary>
        /// <param name="data">校验数据,字节数组</param>
        /// <param name="offset">从头开始偏移几个byte</param>
        /// <param name="length">偏移后取几个字节byte</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(byte[] data, int offset, int length)
        {
            byte[] Tdata = data.Skip(offset).Take(length).ToArray();
            return CRCCalc(Tdata);
        }
}

 

三、modbus_RTU协议

01H 读取线圈状态(多个)

格式:

发送报文:从站地址+功能码+开始线圈地址+线圈数量+CRC

返回报文:从站地址+功能码+字节计数+数据+CRC

 

02H 读输入状态

格式:

发送报文:从站地址+功能码+起始地址+输入长度+CRC

返回报文:从站地址+功能码+数据长度+数据+CRC

 

03H读取保持寄存器

格式:

发送报文:从站地址+功能码+开始寄存器地址+寄存器数量+CRC

返回报文:从站地址+功能码+字节计数+数据+CRC

 

04H读取输入寄存器

格式:

发送报文:从站地址+功能码+开始寄存器地址+寄存器数量+CRC

返回报文:从站地址+功能码+字节计数+数据+CRC

 

05H写入单线圈

格式:

发送报文:从站地址+功能码+线圈地址+线圈值+CRC

返回报文:从站地址+功能码+线圈地址+线圈值+CRC

 

06H写入单寄存器

格式:

发送报文:从站地址+功能码+寄存器地址+寄存器值+CRC

返回报文:从站地址+功能码+寄存器地址+寄存器值+CRC

 

0FH写入多线圈

格式:

发送报文:从站地址+功能码+起始线圈地址+数量+字节技术+数据+CRC

返回报文:从站地址+功能码+起始线圈地址+数量+CRC

 

10H写入多寄存器

格式:

发送报文:从站地址+功能码+起始寄存器地址+数量+字节计数+数据+CRC

返回报文:从站地址+功能码+起始寄存器地址+数量+CRC

 

posted @ 2024-08-09 22:01  别动我的猫  阅读(98)  评论(0编辑  收藏  举报