• 通信协议_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 ret = new List();
    
                //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+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校验
            /// 
            /// CRC校验,参数data为byte数组
            /// 
            /// 校验数据,字节数组
            /// 字节0是高8位,字节1是低8位
            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;
            }
    
            /// 
            /// CRC校验,参数为空格或逗号间隔的字符串
            /// 
            /// 校验数据,逗号或空格间隔的16进制字符串(带有0x或0X也可以),逗号与空格不能混用
            /// 字节0是高8位,字节1是低8位
            public static byte[] CRCCalc(string data)
            {
                //分隔符是空格还是逗号进行分类,并去除输入字符串中的多余空格
                IEnumerable datac = data.Contains(",") ? data.Replace(" ", "").Replace("0x", "").Replace("0X", "").Trim().Split(',') : data.Replace("0x", "").Replace("0X", "").Split(' ').ToList().Where(u => u != "");
                List bytedata = new List();
                foreach (string str in datac)
                {
                    bytedata.Add(byte.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier));
                }
                byte[] crcbuf = bytedata.ToArray();
                //crc计算赋初始值
                return CRCCalc(crcbuf);
            }
    
    
            /// 
            ///  CRC校验,截取data中的一段进行CRC16校验
            /// 
            /// 校验数据,字节数组
            /// 从头开始偏移几个byte
            /// 偏移后取几个字节byte
            /// 字节0是高8位,字节1是低8位
            public static byte[] CRCCalc(byte[] data, int offset, int length)
            {
                byte[] Tdata = data.Skip(offset).Take(length).ToArray();
                return CRCCalc(Tdata);
            }
    
            #endregion
        }
    }
    
    
    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. 最终可实现通过mdbusRTU协议进行主从站通信:
      在这里插入图片描述
      在这里插入图片描述

    总结

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

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

  • 相关阅读:
    MySQL 事务
    SDH设备
    git使用经验
    35张图,直观理解Stable Diffusion
    一文教你理解Kafka offset
    前端学习路线(一)
    一网打尽——线粒体基因组高级分析
    Leetcode808. 分汤
    java-net-php-python-jsp班导师助理业务管理信息系统计算机毕业设计程序
    全球与中国亚麻布行业市场现状分析及投资可行性研究报告2022-2028年
  • 原文地址:https://blog.csdn.net/weixin_63243357/article/details/140253948