当前位置:   article > 正文

通信协议_C#实现自定义ModbusRTU主站

通信协议_C#实现自定义ModbusRTU主站

背景知识:modbus协议介绍

相关工具

  • mbslave:充当从站。
  • 虚拟串口工具:虚拟出一对串口。
  • VS2022。

实现过程以及Demo

  1. 打开虚拟串口工具:
  2. 打开mbslave:
    从站设置
    在这里插入图片描述
    此处从站连接COM1口。

Demo实现

  1. 创建DLL库,创建ModbusRTU类,进行实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace modBusDLL
{
    public class ModBusRTU
    {

        private SerialPort serialPort = null;

        public ModBusRTU()
        {
            serialPort = new SerialPort();
        }

        //连接方法

        //端口号,波特率,数据位,校验位,停止位
        public void Connect(string portName,int baudRate=9600,int dataBits=8,
                            Parity parity=Parity.None,StopBits stopBits=StopBits.One)
        { 
        if (serialPort.IsOpen) {
            
                serialPort.Close();
            }
        serialPort.BaudRate = baudRate;
        serialPort.PortName = portName;
        serialPort.Parity = parity;
        serialPort.StopBits = stopBits;
        serialPort.DataBits = dataBits;

            serialPort.Open();

            
        }

        //断开连接方法
        public void Disconnect()
        {
            if (serialPort.IsOpen)
            {

                serialPort.Close();
            }
        }

        //读消息方法
        public byte[] ReadKeepRegisters(byte devAdd,ushort start,ushort length)
        { 
        //拼接报文,发送报文,接受报文,校验报文,解析报文

            //创建一个字节集合
            List<byte> ret = new List<byte>();

            //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+CRC

            //站地址
            ret.Add(devAdd);

            //功能码
            ret.Add(0x03);
 

            //起始寄存器地址
            //高位地址
            ret.Add((byte)(start / 256));
            //低位地址
            ret.Add((byte)(start % 256));


            //寄存器数量
            //高位地址
            ret.Add((byte)(length / 256));
            //低位地址
            ret.Add((byte)(length % 256));





            byte[] crc= CRCCalc(ret.ToArray());
            ret.AddRange(crc);





            //发送报文
            serialPort.Write(ret.ToArray(), 0, ret.Count);

            Thread.Sleep(50);
            
            //接受长度
            int byteCount = serialPort.BytesToRead;

            byte[] data = new byte[byteCount];

            //读入data
            serialPort.Read(data, 0, data.Length);

            byte[] result = new byte[length*2]; 
        
            Array.Copy(data,3,result, 0,length*2);

            return result;
        
        }

        #region 16位CRC校验
        /// <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);
        }

        #endregion
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  1. 在窗体代码中进行调用:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using modBusDLL;

namespace easyProjectPractice
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            this.comboBox1.DataSource=SerialPort.GetPortNames();
        }

        private ModBusRTU modBusRTU = new ModBusRTU();



        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            modBusRTU.Connect(this.comboBox1.Text);

            MessageBox.Show("连接成功");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            modBusRTU.Disconnect();
            MessageBox.Show("断开连接");
        }

        private void button3_Click(object sender, EventArgs e)
        {

            byte[] data = modBusRTU.ReadKeepRegisters(1, 2, 1);

       

            textBox1.Text = (data[0] * 256 + data[1]).ToString();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  1. 最终可实现通过mdbusRTU协议进行主从站通信:
    在这里插入图片描述
    在这里插入图片描述

总结

简单的modbusRTU主从通信自定义。
  • 1

接触过的所有通信协议Demo代码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/810982
推荐阅读
相关标签
  

闽ICP备14008679号