Modbus_Ascii协议

发布于:2024-08-02 ⋅ 阅读:(41) ⋅ 点赞:(0)

设备必须要有RTU协议!这是Modbus协议上规定的,且默认模式必须是RTU,ASCII作为选项。(也就是说,一般的设备只有RTU这个协议,ASCII一般很少)所以说,一般学习Modbus协议,只需要了解RTU的协议,ASCII作为学习的了解就足够了。

modbus- TCP 和  modbus-RTU的区别?

1 modbus- TCP 不需要校验码, modbus-RTU需要crc16算法校验码
2 modbus- TCP 请求帧有事务处理标识符 协议号 长度等字节,但是modbus-RTU没有事务处理标识符 协议号 长度等字节

modbus- TCP 请求帧:00  03 |  00 00 |  00 06 |  01 | 03 | 00 01 | 00 01 

modbus-RTU 请求帧: 01| 03 | 00 01  |00 01 |D5 CA

 modbus- ASCII 和 modbus-RTU的区别?

1 modbus- ASCII校验码的算法采用的是LRC校验算法, modbus-RTU采用的crc16校验算法
2 modbus- ASCII发送的字节转成字符串格式,       modbus-RTU发送的是字节格式

modbus - ASCII 请求帧 :010300000001FB

1设备地址 01
2功能码 读取03
3寄存器地址 00 00
4 寄存器个数 00 01
5 lrc校验码 F B

string s2 =  Tools.GetRequestFrame(new byte[] { 01,03,00,00,00,10});

封装 modbus-ascii使用的方法 

internal static class Tools
{

    /// <summary>
    /// 把传递过来字节数组 通过lrc算法生成校验码
    /// </summary>
    /// <param name="data"> 字节数组</param>
    /// <returns>校验码</returns>
    /// Tools.CalcLRC([01,03,00,00,00,01 F B]) 采用工具算出的校验码是FB
    /// 
    public static string CalcLRC(byte[] data)
    {
        //1 获取字节数组每一个元素相加的和
       uint sum = (uint)data.Sum(x => x);//计算每个元素的和

        //2 把sum进行取反操作,再加1,再和0xff进行与运算 ,
        //~110 = 001 取反操作
        uint res = (~sum + 1) & 0xff;

        return res.ToString("X2");
    }
    /// <summary>
    /// [01,03,00,00,00,01 ] 转成 ":01030000001FB\r\n"
    /// </summary>
    /// <param name="data">转换的字节数组</param>
    /// <returns> 转成ascii字符串</returns>
    public static string GetRequestFrame(byte[] data)
    {
        //1 算lrc校验码
        string jym = CalcLRC(data);

        string requestData = "";
        // 2 遍历字节数组
        foreach (byte item in data)
        {
            requestData += item.ToString("X2");
        }
        //3 拼接校验码
        string value = ":" + requestData + jym + "\r\n";
        return value;
    }


    //:0103025AC0E0
    /// <summary>
    /// :0103025AC0E0 转成对应的ushort数组 [5A,C0]
    /// </summary>
    /// <param name="s"> ascii字符串</param>
    /// <param name="valueCount"> 寄存器个数  1个时候2字节,2个4字节</param>
    /// <param name="startIndex"> 从哪个位置开始截取</param>
    /// <returns></returns>
    public static ushort[] StringToUshort(string s,int valueCount,int startIndex=7)
    {
        //如果寄存器个数*4+开始截取数据的位置>整体字符串长度 证明没有数据部分
        if (valueCount * 4 + startIndex > s.Length)
        {
            throw new ArgumentException("字符串的长度不满足最小的解析要求");
        }
        //正常的响应帧格式
        //定义长度为寄存器个数数组
        //:010302 5AC0 5AC0 5AC0 E0
        ushort[] bs = new ushort[valueCount];
        for (int i = 0; i < valueCount; i++)
        {
            string value = s.Substring(startIndex, 4);
            startIndex += 4;
            bs[i] = Convert.ToUInt16(value,16);

        }
        return bs;

    }


}