Modbus通讯协议学习 - 认识篇

MODBUS协议整理——汇总
https://blog.csdn.net/xukai871105/article/details/16368615

ModBus
https://baike.sogou.com/v7562947.htm?fromTitle=ModBus

Modbus通讯协议学习 - 认识篇
https://blog.csdn.net/xiaoluoshan/article/details/73233955

Modbus协议———常用功能码详解
http://www.cnblogs.com/luomingui/archive/2013/06/14/Modbus.html

Modbus协议详解与实战分析
https://mp.weixin.qq.com/s?src=11&timestamp=1538828737&ver=1166&signature=j-bas9TJSbHnOe5UFqlOKCU4NmPoFIN8Bxfrn1FmNMyG0Msjcf861LsxprN-KFMCR*OZ96p6y2IIuCKhGu4yAPTDjm94tmbqjO66RPoXjIaJEsWilULnJ5NTSXmhhhLj&new=1

其实Modbus通讯很简单手把手教你
http://www.360doc.com/content/17/1204/04/49530759_709669949.shtml

//======================================================================//

01H-读线圈状态
1)描述:读从机线圈寄存器,位操作,可读单个或者多个;
2)发送指令:
假设从机地址位0x01,寄存器开始地址0x0023,寄存器结束抵制0x0038,总共读取21个线圈。协议图如下:
在这里插入图片描述

3)响应:
返回数据的每一位对应线圈状态,1-ON,0-OFF,如下图;
在这里插入图片描述
上表中data1表示0x0023-0x002a的线圈状态,data1的最低位代表最低地址的线圈状态,可以理解为小端模式;
在这里插入图片描述

data2表示地址0x002b-0x0033的线圈状态,如下表:
在这里插入图片描述

data3表示地址0x0034-0x0038的线圈状态,不够8位,字节高位填充为0,如下表:
在这里插入图片描述

02H-读离散输入状态
1):读离散输入寄存器,位操作,可读单个或多个,协议类似功能码0X01协议,此处省;

03H-读保持寄存器
1)描述:读保持寄存器,字节指令操作,可读单个或者多个;
2)发送指令:
从机地址0x01,保持寄存器起始地址0x0032,读2个保持寄存器
在这里插入图片描述

3)响应:
在这里插入图片描述

数据存储顺序
在这里插入图片描述

04H-读输入寄存器

1)描述:读输入寄存器,字节指令操作,可读单个或者多个;
2)发送指令:同03H;
3)响应:同03H;

05H-写单个线圈

1)描述:写单个线圈,位操作,只能写一个,写0xff00表示设置线圈状态为ON,写0x0000表示设置线圈状态为OFF
2)发送指令:
设置0x0032线圈为ON;
在这里插入图片描述

3)响应:
同发送指令;

06H-写单个保持寄存器

1)描述:写单个保持寄存器,字节指令操作,只能写一个;
2)发送指令:
写0x0032保持寄存器为0x1232;
在这里插入图片描述
3)响应:同发送指令;

0FH-写多个线圈

1)描述:写多个线圈寄存器。若数据区的某位值为“1”表示被请求的相应线圈状态为ON,若某位值为“0”,则为状态为OFF。
2)发送指令:
线圈地址为0x04a5,写12个线圈,
在这里插入图片描述
上图中DATA1为0x0c,表示:
在这里插入图片描述
DATA2为0x02,不够8位,字节高位填充0:
在这里插入图片描述
3)响应:
在这里插入图片描述

10H-写多个保持寄存器

1)描述:写多个保持寄存器,字节指令操作,可写多个;
2)发送指令:
保持寄存器起始地址为0x0034,写2个寄存器4个字节的数据;
在这里插入图片描述
3)响应:
在这里插入图片描述

//================================================================================//

什么是Modbus?
Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。Modbus 协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。

Modbus 是一个请求/应答协议

Modbus

以下是要分解的Modbus热图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Modbus消息帧
了解了它,会使你对串口通信有一个清晰的认识!
在这里插入图片描述

通用消息帧
在这里插入图片描述

ASCII消息帧 (在消息中的每个8Bit 字节都作为两个ASCII字符发送)

十六进制,ASCII字符0…9,A…F

消息中的每个ASCII字符都是一个十六进制字符组成

每个字节的位

1个起始位

n个数据位,最小的有效位先发送

1个奇偶校验位,无校验则无

1个停止位(有校验时),2个Bit(无校验时)

错误检测域

LRC(纵向冗长检测)

在这里插入图片描述
在这里插入图片描述

RTU消息帧

8位二进制,十六进制数0…9,A…F

消息中的每个8位域都是一个两个十六进制字符组成

每个字节的位

1个起始位

8个数据位,最小的有效位先发送

1个奇偶校验位,无校验则无

1个停止位(有校验时),2个Bit(无校验时)

错误检测域

CRC(循环冗长检测)
在这里插入图片描述
在这里插入图片描述

CRC校验 (http://baike.baidu.com/view/1664507.htm)
public static string CRCCheck(string val)
{
val = val.TrimEnd(’ ‘);
string[] spva = val.Split(’ ‘);
byte[] bufData = new byte[spva.Length + 2];
bufData = ToBytesCRC(val);
ushort CRC = 0xffff;
ushort POLYNOMIAL = 0xa001;
for (int i = 0; i < bufData.Length - 2; i++)
{
CRC ^= bufData[i];
for (int j = 0; j < 8; j++)
{
if ((CRC & 0x0001) != 0)
{
CRC >>= 1;
CRC ^= POLYNOMIAL;
}
else
{
CRC >>= 1;
}
}
}
return Maticsoft.DBUtility.HLConvert.ToHex(System.BitConverter.GetBytes(CRC));
}
///
/// 例如把如下字符串转换成字节数组
/// AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB 转换为字节数组
///
/// 十六进制字符串
///
public static byte[] ToBytesCRC(string hex)
{
string[] temp = hex.Split(’ ');
byte[] b = new byte[temp.Length + 2];

        for (int i = 0; i < temp.Length; i++)
        {
            b[i] = Convert.ToByte(temp[i], 16);
        }

        return b;
    }
    /// <summary>
    /// 将字节数据转换为十六进制字符串,中间用 “ ”分割 如:AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB
    /// </summary>
    /// <param name="vars">要转换的字节数组</param>
    /// <returns></returns>
    public static String ToHex(byte[] vars)
    {
        return BitConverter.ToString(vars).Replace('-', ' ').Trim();
    }

CS校验(累加和)
public static string CSCheck(string str)
{
if (str.Length == 0) return “”;
else str = str.Trim();
byte[] sss = ToBytes(str);
int n = 0;
for (int i = 0; i < sss.Length; i++)
{
n += sss[i];
}
return ToHex(n);
}
///
/// AB CD 12 3B 转换为字节数组
///
/// 十六进制字符串
///
public static byte[] ToBytes(string hex)
{
string[] temp = hex.Split(’ ');
byte[] b = new byte[temp.Length];

        for (int i = 0; i < temp.Length; i++)
        {
            if (temp[i].Length > 0)
                b[i] = Convert.ToByte(temp[i], 16);
        }

        return b;
    }
    /// <summary>
    /// 转换为符合本程序的十六进制格式
    /// </summary>
    /// <param name="var">1 2 3 等。</param>
    /// <returns>返回十六进制字符串,如果是1-9的话,前面带零</returns>
    /// <example>例如: 5  ="05"  12 ="0C" 无论何时,都是两位数。  </example>
    public static string ToHex(int var)
    {
        int cs = var;
        string tmp = "";
        if (cs == 0) { tmp = "00"; }
        while (cs > 0)
        {
            int ys;
            cs = Math.DivRem(cs, 256, out ys);
            tmp = tmp.Insert(0, string.Format(" {0}", Right("00" + Convert.ToString(ys, 16), 2).ToUpper()));
        }
        return tmp.Trim();
    }
    public static string Right(string str, int Length)
    {
        if ((Length <= 0) || (str == null))
        {
            return "";
        }
        int length = str.Length;
        if (Length >= length)
        {
            return str;
        }
        return str.Substring(length - Length, Length);
    }

LRC校验(LRC错误校验用于ASCII模式)
///
/// 取模FF(255)
/// 取反+1
///
///
///
public static string LRCheck(string writeUncheck)
{
char[] hexArray = new char[writeUncheck.Length];
hexArray = writeUncheck.ToCharArray();
int decNum = 0, decNumMSB = 0, decNumLSB = 0;
int decByte, decByteTotal = 0;

        bool msb = true;

        for (int t = 0; t <= hexArray.GetUpperBound(0); t++)
        {
            if ((hexArray[t] >= 48) && (hexArray[t] <= 57))

                decNum = (hexArray[t] - 48);

            else if ((hexArray[t] >= 65) & (hexArray[t] <= 70))
                decNum = 10 + (hexArray[t] - 65);

            if (msb)
            {
                decNumMSB = decNum * 16;
                msb = false;
            }
            else
            {
                decNumLSB = decNum;
                msb = true;
            }
            if (msb)
            {
                decByte = decNumMSB + decNumLSB;
                decByteTotal += decByte;
            }
        }

        decByteTotal = (255 - decByteTotal) + 1;
        decByteTotal = decByteTotal & 255;

        int a, b = 0;

        string hexByte = "", hexTotal = "";
        double i;

        for (i = 0; decByteTotal > 0; i++)
        {
            b = Convert.ToInt32(System.Math.Pow(16.0, i));
            a = decByteTotal % 16;
            decByteTotal /= 16;
            if (a <= 9)
                hexByte = a.ToString();
            else
            {
                switch (a)
                {
                    case 10:
                        hexByte = "A";
                        break;
                    case 11:
                        hexByte = "B";
                        break;
                    case 12:
                        hexByte = "C";
                        break;
                    case 13:
                        hexByte = "D";
                        break;
                    case 14:
                        hexByte = "E";
                        break;
                    case 15:
                        hexByte = "F";
                        break;
                }
            }
            hexTotal = String.Concat(hexByte, hexTotal);
        }
        return hexTotal;
    }

    public void LRCheck(byte[] code)
    {
        int sum = 0;
        foreach (byte b in code)
        {
            sum += b;
        }
        sum = sum % 255;//取模FF(255)
        sum = ~sum + 1;//取反+1
        string lrc = Convert.ToString(sum, 16);
        return lrc;
    }

自定义Modbus数据表
自定义Modbus数据表例子:

设备相关读取信息:
在这里插入图片描述

命令报文信息解析:
在这里插入图片描述
自定义Modbus数据表定义注意

串口调试工具
串口调试工具的使用.
在这里插入图片描述

串口调试工具 + RS485 就可以读取硬件上的数据,和向硬件请求了,如何使用请看“调试篇”会有详细的说明。
在这里插入图片描述
在这里插入图片描述

网络调试助手:

   调试助手主要还是TCP协议通讯的一个调试工具
   ![在这里插入图片描述](https://img-blog.csdn.net/20181006152554131?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NTQ3ODk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

猜你喜欢

转载自blog.csdn.net/u011854789/article/details/82951282