基于Modbus协议的C#串口工具开发

2023-05-16

(Tips:本人纯萌新小白一枚,文章只为记录自己的编程历史,温故而知新,能帮助到大家倍感荣幸,有错误的地方劳烦指点!)

不多废话直接进入主题!

本文旨在基于Modbus协议、C#开发语言进行串口工具的开发工作:

首先是界面的设计:

初次设计,界面略显花哨,各位按需进行颜色的设置。

用到的控件有:label(文本)、textBox(文本框)、comboBox(下拉框)、button(按键)、groupBox(组合框)、timer(时钟)、serialPort(串口) 。

由于是初次设计,所以内容较为单薄,且有未实现的功能(例如10H指令并未实现)

首先是Form(主函数)的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using Modbus.Data;
using Modbus.Device;
using Modbus.Message;
using Modbus调试工具_demo_.Properties;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace Modbus调试工具_demo_
{
    public partial class Form1 : Form
    {
        #region 一些定义
        /// <summary>
        /// 私有串口实例
        /// </summary>
        private SerialPort serialPort = new SerialPort();   
        //定义接收字节数组
        byte[] Data = new byte[1024];
        byte ReceiveByte;
        int ReceiveByteCount = 0;
        //定义设备地址
        int CurrentAddr = 1;
        int Word;
        //定义返回报文
        string ReceiveData;
        #endregion

        public Form1()
        {
            InitializeComponent();

            Control.CheckForIllegalCrossThreadCalls = false;
        }

        #region 打开窗口触发事件(存在问题需要修改!!!)
        private void Form1_Load(object sender, EventArgs e)
        {
            //(待程序调整好后启用) MessageBox.Show("欢迎使用本软件!\r\n", "MB调试工具");
            this.MaximumSize = this.Size;
            this.MinimumSize = this.Size; //固定程序窗口大小;
            button1.Text = "打开串口";
            button1.BackColor = Color.Green;
            button9.BackColor = Color.Yellow;
            button3.Enabled = false;
            button4.Enabled = false;
            button5.Enabled = false;
            button6.Enabled = false;
            button7.Enabled = false;
            checkBox1.Enabled = false;
            timer3.Start();

            this.textBox3.Text = Settings.Default.One;
            this.textBox4.Text = Settings.Default.Two;
            this.textBox5.Text = Settings.Default.Three;
            this.textBox6.Text = Settings.Default.Four;
            this.textBox7.Text = Settings.Default.Five;
            this.textBox8.Text = Settings.Default.Six;
            this.textBox9.Text = Settings.Default.Seven;
            this.textBox10.Text = Settings.Default.Eight;
            this.textBox11.Text = Settings.Default.Nine;

            #region 保存输入数值代码段(存在错误)
            /*  string Data_Save = " ";
            string[] Data_Set = { " " };
            string Data_Path = Application.StartupPath + "\\config_data.ini";
            try
            {
                StreamReader streamReader = new StreamReader(Data_Path);
                Data_Save = streamReader.ReadToEnd();
                streamReader.Close();
                Data_Set = Regex.Split(Data_Save, "||");
            }
            catch { }

            //USART_Scan(serialPort1, comboBox1);
            if (Data_Set.Length > 12)
            {
                try
                {
                    if (Data_Set[0] == "1")
                    {
                        checkBox1.Checked = true;
                    }
                    else
                    {
                        checkBox1.Checked = false;
                    }
                    comboBox2.Text = Data_Set[1];
                    comboBox3.Text = Data_Set[2];
                    comboBox4.Text = Data_Set[3];
                    comboBox5.Text = Data_Set[4];
                    textBox3.Text = Data_Set[5];
                    textBox4.Text = Data_Set[6];
                    textBox5.Text = Data_Set[7];
                    textBox6.Text = Data_Set[8];
                    textBox7.Text = Data_Set[9];
                    textBox8.Text = Data_Set[10];
                    textBox9.Text = Data_Set[11];
                    textBox10.Text = Data_Set[12];
                    textBox11.Text = Data_Set[13];
                    //代码段暂时存在错误,无法使用;
                }
                catch
                {
                    checkBox1.Checked = false;
                    comboBox2.Text = "9600";
                    comboBox3.Text = "No Parity";
                    comboBox4.Text = "8";
                    comboBox5.Text = "1";
                    textBox3.Text = "";
                    textBox4.Text = "";
                    textBox5.Text = "";
                    textBox6.Text = "";
                    textBox7.Text = "";
                    textBox8.Text = "";
                    textBox9.Text = "";
                    textBox10.Text = "";
                    textBox11.Text = "";
                }
            }
            else
            {
                comboBox2.Text = "9600";
                comboBox3.Text = "No Parity";
                comboBox4.Text = "8";
                comboBox5.Text = "1";
            }          */
            #endregion

        }
        #endregion

        #region 开关串口
        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "打开串口")    //假设当前串口设备为关闭状态;
            {
                try
                {
                    serialPort1.PortName = comboBox1.Text;
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
                    serialPort1.DataBits = Convert.ToInt32(comboBox4.Text);
                    serialPort1.Open();

                    button1.Text = "关闭串口";
                    comboBox1.Enabled = false;
                    comboBox2.Enabled = false;
                    button1.BackColor = Color.Red;
                    button3.Enabled = true;
                    button4.Enabled = true;
                    button5.Enabled = true;
                    button6.Enabled = true;
                    button7.Enabled = true;
                    checkBox1.Enabled = true;
                }
                catch
                {
                    MessageBox.Show("打开串口失败,请检查串口!", "警告");
                }
            }
            else
            {
                try
                {
                    serialPort1.Close();

                    comboBox1.Enabled = true;
                    comboBox2.Enabled = true;
                    button1.Text = "打开串口";
                    button1.BackColor = Color.Green;
                    button3.Enabled = false;
                    button4.Enabled = false;
                    button5.Enabled = false;
                    button6.Enabled = false;
                    button7.Enabled = false;
                    checkBox1.Enabled = false;
                    textBox1.Text = "";
                    textBox2.Text = "";
                }
                catch
                {
                    MessageBox.Show("关闭串口失败,请检查串口!", "警告");
                }
            }
        }
        #endregion

        #region 自动获取可用的串口号
        private void comboBox1_DropDown(object sender, EventArgs e)
        {
            USART_Scan(comboBox1);
        }

        public void USART_Scan(System.Windows.Forms.ComboBox MyBox) //搜索串口号并放到下拉列表中
        {
            MyBox.Items.Clear(); //清空ComboBox显示内容
            string[] Serial_Name = System.IO.Ports.SerialPort.GetPortNames(); //获取串口名称 为字符串数组
            if (Serial_Name.Length > 0)
            {
                Array.Sort(Serial_Name);
                MyBox.Items.AddRange(Serial_Name);
            }

            if (MyBox.Items.Count != 0)
                MyBox.SelectedIndex = 0;

        }
        #endregion

        #region 清空textBox
        private void button6_Click(object sender, EventArgs e)
        {
            textBox1.Text=" ";     //一种清空textbox的方法;
        }

        private void button7_Click(object sender, EventArgs e)
        {
            textBox2.Clear();      //另一种清空textbox的方法;
        }
        #endregion

        #region 03H代码的功能实现
        /// <summary>
        /// 03H发送命令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                string send_byte = "";
                Modbus_data modbus_Data = new Modbus_data();
                byte[] data_re = modbus_Data.Order03H(Convert.ToByte(textBox3.Text), 
                Convert.ToUInt16(textBox4.Text), Convert.ToUInt16(textBox5.Text));
                serialPort1.Write(data_re, 0, 8);
                Word = Convert.ToUInt16(textBox5.Text);
                for (int i = 0; i < data_re.Length; i++)
                   {
                       send_byte += data_re[i].ToString("X2") + " ";
                   }
                textBox1.Text += send_byte + "\t";
                Thread.Sleep(100);
                textBox2.Text += ReceiveData + "\n";
                if(ReceiveData != null)
                {
                    ReceiveData = ReceiveData.Replace(" ", "");
                    ReceiveData = ReceiveData.Substring(6, Word * 4);
                    ReceiveData = string.Join(" ",
                System.Text.RegularExpressions.Regex.Split(ReceiveData, "(?<=\\G.{4})(?!$)"));
                }
                
                }
            catch (System.Exception ex)
            {
                MessageBox.Show("错误:" + ex.Message, "Modbus 错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion

        #region 06H代码的功能实现
        /// <summary>
        ///  06H写入命令发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            try
            {
                string send_byte = "";
                Modbus_data modbus_Data = new Modbus_data();
                byte[] data_se = modbus_Data.Order06H(Convert.ToByte(textBox6.Text), 
                Convert.ToUInt16(textBox7.Text), Convert.ToUInt16(textBox8.Text));
                serialPort1.Write(data_se, 0, 8);
                for (int i = 0; i < data_se.Length; i++)
                   {
                      send_byte += data_se[i].ToString("X2") + " ";
                   }
                textBox1.Text += send_byte + "\t";
                Thread.Sleep(100);
                textBox2.Text += ReceiveData + "\n";
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("错误:" + ex.Message, "Modbus 错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion

        #region 10H代码的功能实现(存在问题需要修改!!!)
        /// <summary>
        ///  10H写入命令发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click(object sender, EventArgs e)
        {
            try
            {
                string send_byte = "";
                Modbus_data modbus_Data = new Modbus_data();
                byte[] data_re = modbus_Data.Order10H(Convert.ToByte(textBox9.Text),
                Convert.ToUInt16(textBox10.Text), Convert.ToUInt16(textBox11.Text));
                serialPort1.Write(data_re, 0, 8);
                for (int i = 0; i < data_re.Length; i++)
                {
                    send_byte += data_re[i].ToString("X2") + " ";
                }
                textBox1.Text += send_byte + "\t";
                Thread.Sleep(100);
                textBox2.Text += ReceiveData + "\n";
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("错误:" + ex.Message, "Modbus 错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        //try
        //{
        //    string send_byte = "";
        //    Modbus_data modbus_Data = new Modbus_data();
        //    byte[] data_re = modbus_Data.Order10H(Convert.ToByte(textBox9.Text), 
        //    Convert.ToUInt16(textBox10.Text), Convert.ToUInt16(textBox11.Text));
        //    serialPort1.Write(data_re, 0, 8);
        //    for (int i = 0; i < data_re.Length; i++)
        //    {
        //        send_byte += data_re[i].ToString("X2") + " ";
        //    }
        //    textBox1.Text += send_byte + "\t";
        //    Thread.Sleep(100);
        //    textBox2.Text += ReceiveData + "\n";
        //}
        //catch (System.Exception ex)
        //{
        //    MessageBox.Show("错误:" + ex.Message, "Modbus 错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        //}
        #endregion

        #region 接口接收到数据时的事件(10H代码需要在功能实现代码修改完后进行相应的修改!!!)
        private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(SerialPort1_DataReceived);
            //对serialPort1 注册一个事件委托

            this.textBox2.Invoke(new MethodInvoker(delegate ()
            {
                textBox2.AppendText(serialPort1.ReadExisting());//这里用多行文本进行显示
                textBox2.ScrollToCaret();
            }));

            if (serialPort1.IsOpen)
            {
                while (serialPort1.BytesToRead > 0)
                {
                    ReceiveByte = (byte)serialPort1.ReadByte();
                    Data[ReceiveByteCount] = ReceiveByte;
                    ReceiveByteCount += 1;
                    if (ReceiveByteCount >= 1024)
                    {
                        ReceiveByteCount = 0;
                        //清除输入缓存区
                        serialPort1.DiscardInBuffer();
                        return;
                    }
                }
            }
            //读取保持型寄存器 功能码0x03
            if (Data[0] == (byte)CurrentAddr && Data[1] == 0x03 && ReceiveByteCount >= (Word * 2 + 5))
            {
                ReceiveData = "";
                for (int i = 0; i < Word * 2 + 5; i++)
                {
                    ReceiveData = ReceiveData + " " + Data[i].ToString("X2");
                }
                serialPort1.DiscardInBuffer();
            }
            //预置单字保持型寄存器  功能码0x06
            if (Data[0] == (byte)CurrentAddr && Data[1] == 0x06 && ReceiveByteCount >= 8)
            {
                ReceiveData = "";
                for (int i = 0; i < 8; i++)
                {
                    ReceiveData = ReceiveData + " " + Data[i].ToString("X2");
                }
                serialPort1.DiscardInBuffer();
            }
            Array.Clear(Data, 0, Data.Length);
            ReceiveByteCount = 0;
            //预置多字保持型寄存器  功能码0x10
            if (Data[0] == (byte)CurrentAddr && Data[1] == 0x06 && ReceiveByteCount >= 8)
            {
                ReceiveData = "";
                for (int i = 0; i < 8; i++)
                {
                    ReceiveData = ReceiveData + " " + Data[i].ToString("X2");
                }
                serialPort1.DiscardInBuffer();
            }
            Array.Clear(Data, 0, Data.Length);
            ReceiveByteCount = 0;

            //string dataRecive = serialPort1.ReadExisting();
            //textBox2.AppendText(dataRecive);
        }
        #endregion

        #region 清除收发缓存区数据的实现
        private void button8_Click(object sender, EventArgs e)
        {
            textBox1.Clear();
            textBox2.Clear();
        }
        #endregion

        #region 03H指令自动发送的实现
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if(checkBox1.Checked)
            {
                checkBox1.BackColor = Color.Red;
                comboBox6.Enabled = false;         //界面改变;
                try
                {
                    timer1.Interval = Convert.ToUInt16(comboBox6.Text, 10);
                }
                catch
                {
                    MessageBox.Show("输入时间有误,设定为默认值", "提示");
                    comboBox6.Text = "1000";
                    timer1.Interval = 1000;
                }
                timer1.Start();
            }
            else
            {
                checkBox1.BackColor = Color.DarkGray;
                timer1.Stop();
                comboBox6.Enabled = true;
            }
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            button3.PerformClick();
        }
        #endregion

        #region 结束程序按键
        private void button9_Click(object sender, EventArgs e)
        {
            System.Environment.Exit(0);
        }
        #endregion

        #region 06H指令自动发送的实现
        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox2.Checked)
            {
                checkBox2.BackColor = Color.Red;
                comboBox7.Enabled = false;         //界面改变;
                try
                {
                    timer2.Interval = Convert.ToUInt16(comboBox7.Text, 10);
                }
                catch
                {
                    MessageBox.Show("输入时间有误,设定为默认值", "提示");
                    comboBox6.Text = "1000";
                    timer1.Interval = 1000;
                }
                timer2.Start();
            }
            else
            {
                checkBox2.BackColor = Color.DarkGray;
                timer2.Stop();
                comboBox7.Enabled = true;
            }
        }
        private void timer2_Tick(object sender, EventArgs e)
        {
            button4.PerformClick();
        }
        #endregion

        #region 窗口关闭时触发事件
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Settings.Default.One = this.textBox3.Text;
            Settings.Default.Two = this.textBox4.Text;
            Settings.Default.Three= this.textBox5.Text;
            Settings.Default.Four = this.textBox6.Text;
            Settings.Default.Five = this.textBox7.Text;
            Settings.Default.Six = this.textBox8.Text;
            Settings.Default.Seven = this.textBox9.Text;
            Settings.Default.Eight = this.textBox10.Text;
            Settings.Default.Nine = this.textBox11.Text;
            Settings.Default.Save();

            #region 存在问题待修复代码段
            /*   string Data_Save = "";
               StreamWriter streamWriter = new StreamWriter("config_data.ini", false);
               if(checkBox3.Checked)
               {
                   Data_Save += "1" + "||";
               }
               else
               {
                   Data_Save += "0" + "||";
               }
               Data_Save += comboBox1.Text.ToString() + "||";
               Data_Save += comboBox2.Text.ToString() + "||";
               Data_Save += comboBox3.Text.ToString() + "||";
               Data_Save += comboBox4.Text.ToString() + "||";
               Data_Save += comboBox5.Text.ToString() + "||";
               Data_Save += textBox3.Text.ToString() + "||";
               Data_Save += textBox4.Text.ToString() + "||";
               Data_Save += textBox5.Text.ToString() + "||";
               Data_Save += textBox6.Text.ToString() + "||";
               Data_Save += textBox7.Text.ToString() + "||";
               Data_Save += textBox8.Text.ToString() + "||";
               Data_Save += textBox9.Text.ToString() + "||";
               Data_Save += textBox10.Text.ToString() + "||";
               Data_Save += textBox11.Text.ToString();

               streamWriter.Write(Data_Save);
               streamWriter.Close();                              */
            #endregion
        }
        #endregion

        #region 发送缓存区数据保存
        private void button10_Click(object sender, EventArgs e)
        {
            FileStream fs = new FileStream(@"Send_data.txt",FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(this.textBox1.Text + "\r\n");
            sw.Close();
            fs.Close();
            MessageBox.Show("数据已保存!\r\n请查看根目录\r\nSend_data文档!", "提示");
        }
        #endregion

        #region 接收缓存区数据保存
        private void button11_Click(object sender, EventArgs e)
        {
            FileStream fs = new FileStream(@"Receive_data.txt", FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(this.textBox2.Text + "\r\n");
            sw.Close();
            fs.Close();
            MessageBox.Show("数据已保存!\r\n请查看根目录\r\nReceive_data文档!", "提示");
        }
        #endregion

        #region 时钟模块
        private void timer3_Tick(object sender, EventArgs e)
        {
            label21.Text = System.DateTime.Now.ToString();
        }
        #endregion

    }
}

其次是Program.cs的代码段:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Modbus调试工具_demo_
{
    internal class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

最后是Modbus_data.cs的代码段(10H指令代码段貌似有错误):

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;



namespace Modbus调试工具_demo_
{
    internal class Modbus_data
    {

        #region 整形、字节、位互相转化

        //整形转字节数组
        private Byte[] intToBytes(int inInt, int outNum)
        {
            Byte[] outByte = new byte[outNum];
            for (int i = 0; i < outNum; i++)
            {
                outByte[outNum - 1 - i] = (byte)(inInt & 0xFF);
                inInt >>= 8;
            }
            return outByte;
        }
        //字节数组转整形
        private int bytesToInt(byte[] inBytes)
        {
            int len = inBytes.Length;
            if (len > 4) len = 4;
            int outInt = 0;
            for (int i = 0; i < len; i++)
            {
                outInt = (outInt << 8) | inBytes[i];
            }
            return outInt;
        }

        //Bool数组转Byte
        private byte boolsToByte(bool[] bools)
        {
            if (bools != null && bools.Length > 0)
            {
                byte b = 0;
                int len = bools.Length;
                if (len > 8) len = 8;
                for (int i = 0; i < len; i++)
                {
                    if (bools[i]) b = (Byte)(b | (1 << i));
                }
                return b;
            }
            return 0;
        }

        //Byte转Bool数组
        private bool[] byteToBools(byte inByte)
        {
            Boolean[] array = new Boolean[8];
            for (int i = 0; i < 8; i++)
            { //对于byte的每bit进行判定  
                array[i] = (inByte & 1) == 1;   //判定byte的最后一位是否为1,若为1,则是true;否则是false  
                inByte = (byte)(inByte >> 1);       //将byte右移一位  
            }
            return array;
        }
        #endregion

        #region CRC表
        private static readonly byte[] auchCRCHi = new byte[]//crc高位表
{
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};

        private static readonly byte[] auchCRCLo = new byte[]//crc低位表
        {
            0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
            0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
            0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
            0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
            0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
            0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
            0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
            0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
            0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
            0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
            0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
            0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
            0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
            0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
            0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
            0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
            0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
            0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
            0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
            0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
            0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
            0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
            0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
            0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
            0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
            0x43, 0x83, 0x41, 0x81, 0x80, 0x40
        };
        #endregion

        #region CRC查表
        public byte[] CRC16(byte[] buffer)
        {
            byte crcHi = 0xff;  // 高位初始化

            byte crcLo = 0xff;  // 低位初始化

            for (int i = 0; i < buffer.Length; i++)
            {
                int crcIndex = crcHi ^ buffer[i];

                //查找crc表值
                crcHi = (byte)(crcLo ^ auchCRCHi[crcIndex]);
                crcLo = auchCRCLo[crcIndex];
            }
            return new byte[] { crcHi, crcLo };
        }
        #endregion

        #region 03H功能码  //按照协议格式化 03 指令  返回值 :格式化后数据
        public byte[] Order03H(byte Station, ushort inAddr, ushort inNum)  
        {

            byte[] subuffer = new byte[6];
            byte[] SendData = new byte[8];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x03;    //功能码
            intToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            intToBytes(inNum, 2).CopyTo(subuffer, 4);    //数据个数

            subuffer.CopyTo(SendData, 0);
            CRC16(subuffer).CopyTo(SendData, 6);

            return SendData;

        }
        #endregion

        #region 06H功能码   //按照协议格式化 06 指令  返回值 :格式化后数据
        public byte[] Order06H(byte Station, ushort inAddr, ushort inNum)
        {

            byte[] subuffer = new byte[6];
            byte[] SendData = new byte[8];
            subuffer[0] = Station;
            subuffer[1] = 0x06;
            intToBytes(inAddr, 2).CopyTo(subuffer, 2);    
            intToBytes(inNum, 2).CopyTo(subuffer, 4);

            subuffer.CopyTo(SendData, 0);
            CRC16(subuffer).CopyTo(SendData, 6);

            return SendData;

        }
        #endregion

        #region 10H功能码
        public bool Order10H(byte Station, ushort inAddr, short[] inShorts)
        {
            int len = inShorts.Length;
            byte[] sendByte = new byte[len * 2];
            for (int i = 0; i < len; i++)
            {
                intToBytes(inShorts[i], 2).CopyTo(sendByte, i * 2);
            }

            Byte[] subuffer = new Byte[len * 2 + 7];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x10;    //功能码
            intToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            intToBytes(len, 2).CopyTo(subuffer, 4);    //数据个数
            subuffer[6] = (byte)(len * 2);   //字节长度
            sendByte.CopyTo(subuffer, 7);

            byte[] ob = SendData(subuffer);

            if (ob == null)
            {
                return false;
            }
            else
            {
                byte[] send_data = new byte[4];
                byte[] rec_data = new byte[4];
                Array.Copy(subuffer, 2, send_data, 0, 4);
                Array.Copy(ob, 2, rec_data, 0, 4);
                return CompareArray(send_data, rec_data);
            }
        }

        private byte[] SendData(byte[] subuffer)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region 判断两个数组是否相等
        public bool CompareArray(byte[] bt1, byte[] bt2)
        {
            if (bt1 == null && bt2 == null)
            {
                return true;
            }

            if (bt1 == null || bt2 == null)
            {
                return false;
            }

            int len = bt1.Length;
            if (len != bt2.Length)
            {
                return false;
            }
            for (var i = 0; i < len; i++)
            {
                if (bt1[i] != bt2[i])
                    return false;
            }
            return true;
        }

        internal byte[] Order10H(byte v1, ushort v2, ushort v3)
        {
            throw new NotImplementedException();
        }
        #endregion

    }
}

第一次发文,加之是一个古早的程序了,有许多存在纰漏之处,敬请谅解!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Modbus协议的C#串口工具开发 的相关文章

  • 基于RS485通信的Modbus通信协议

    通信可以分为两个方面 xff1a 硬件层 xff1a RS485解决的是数据传输问题 xff0c 也就是说如何将一个 0 或 1 传输到另外一端 xff08 保证了数据可以转移到另一端 xff09 软件层 xff1a modbus是在硬件基
  • Modbus超时时间设置

    很多同学在做Modbus通讯时 xff0c 需要连续读取多个现场设备的数据 xff0c 虽然也编写了Modbus轮询的程序 xff0c 但是有时还是无法正常通讯 有时虽然能够保证通讯 xff0c 但是所有现场设备的轮询周期很长 本文将从MB
  • Modbus通信工具学习记录

    Modbus通信工具学习记录 Modbus通信方式Serial PortSerial Port调试助手虚拟Serial Port驱动 TCPTCP调试助手 Modbus模拟工具Modbus PollModbus Slave通信测试后记 Mo
  • MODBUS RTU

    Modbus xff1a 是一种单主 从通信协议 MODBUS网络上只有一个主站 xff0c 主站在MODBUS网络没有地址 xff0c 从站的地址范围为0 247 xff0c 其中0为广播地址 xff0c 从站的实际地址为1 247 MO
  • 【STM32】RS485 Modbus协议 采集传感器数据

    源码链接 xff1a Modbus传感器采集协议 一 硬件 1 传感器 xff1a 为液压传感器 xff0c 12vDC xff0c RS485数据输出 xff0c 采用Modbus协议通信 2 电路 xff1a 根据传感器属性 xff0c
  • 【VSPD虚拟串口】【Modbus Poll】【Modbus Slave】仿真工具的学习过程

    学习想法 xff1a 通信是工控行业内采集仪器仪表等设备信息的重要途径 xff0c 同时可以通过通信访问设备的工作状况对设备进行监控 xff0c 也可以通过通信对设备进行参数修改以及控制设备运行 xff0c 所以掌握通信是工控行业人员比不可
  • 西门子PLC S7-200SMART Modbus TCP通讯的步骤和要点

    Modbus TCP是一个非常传统 xff0c 应用广泛的通讯协议 xff0c 很多智能设备都支持该协议 西门子S7 200SMART及1200 1500系列都免费支持 xff08 300和400还是要高昂收费 xff09 xff0c 并且
  • MODBUS MASTER RTU在STM32上的实现

    MODBUS MASTER RTU在STM32上的实现 1 概述 最近需要将几个信号采集模块通过总线串联起来 xff0c 这样便于系统模块化 故将目光关注到了工业上经常使用的modbus协议 modbus协议是一种一主多从的拓扑结构 xff
  • Modbus设备在Modbus scan上面的使用方法

    操作教程 参数 xff1a DeviceID xff1a 485从站 寄存器地址 xff1a 查询设备地址表 北醒雷达Dist在0x0000开始 读取寄存器长度 xff1a 雷达数据长度值 格式 xff1a MODBUS RTU 串口协议
  • DSP28335 高速modbus代码实现

    程序特点 不使用while循环速度尽可能快速除去程序运行时间 xff0c 没有多余等待时间优化CRC校验方式 头文件modbus h span class token macro property span class token dire
  • Modbus CRC16算法

    Modbus CRC16算法 直接运算 unsigned short modbus crc16 unsigned char pdata unsigned short len unsigned short i j crc crc 0xFFFF
  • modbus poll

    刚刚发布Modbus测试工具 Modbus Slave调试工具 不过已经是昨天了 今天开始发布Modbus测试工具 Modbus Master调试工具 做为Modbus主站 发送命令包 采集从站的数据 典型工具如 Modbus Poll M
  • Modbus网关的 四种类型

    概述 Modbus网关是一种能够将Modubs TCP协议转化为Modbus RTU协议的设备 Modbus广泛应用于仪表和传感器领域 可以获得仪表和传感器的数据 但是传统的基于RS485的Modbus RTU 或ASCII 速度和扩展性较
  • modbus总线协议(一)modbus rtu

    一 介绍 Modbus协议由Modicon公司开发出来 现在Modbus是工业领域全球最流行的协议 硬件支持RS 232 RS 422 RS 485和以太网设备 应用在PLC DCS 智能仪表等工控领域 图片来源于网络 二 modbus协议
  • MFC 中的 libmodbus

    我正在尝试将 libmodbus 提供的 modbus 库集成到 Visual Studio 2010 中的自定义 MFC 项目中 到目前为止我所遵循的步骤如下 1 我在 Visual Studio 2010 中创建了项目 如下面的快照所示
  • 需要 modbus Java 库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要带有源代码的简单 modbus Java 库 我在谷歌上找到了 但有 jar 文件 并且没有强大的
  • 计算 Modbus RTU CRC 16

    我正在实现一个软件 可以通过串行方式读取和写入 Modbus RTU 协议中的数据 为此 我需要计算字节字符串末尾的两个 CRC 字节 但我无法执行此操作 在网上搜索 我发现两个函数似乎可以正确计算 CRC WORD CRC16 const
  • 我可以在输入寄存器中写入吗? MODBUS

    我已经在一家公司工作了两个月MODBUS项目现在我发现了一个问题 我的客户要求我写入输入寄存器 地址 30001 到 40000 我认为这对我来说不是什么问题 因为每个modbus文档说 30001 到 40000 寄存器是只读的 甚至可以
  • Modbus 错误:[无效消息] 收到的消息不完整,预计至少 2 个字节(收到 0 个字节)

    Problem pymodbus 主站 客户端可以向从站 服务器发送请求 从属 服务器准备好返回的东西 并等待主控 客户端来接收它们 尽管服务器 从站已准备就绪 但主站 客户端仅返回错误 Modbus 错误 输入 输出 Modbus 错误
  • 如何在 iPhone 应用程序中与 Modbus 设备通信?

    我希望能够开发一个可以与 Modbus 设备通信的 iPhone 应用程序 但我不知道如何继续 有没有人有这方面的经验 或者是否有用于此目的的现有图书馆 您首先需要知道如何在您的 iPhone 上创建一个简单的 TCP 客户端 以及如何在您

随机推荐

  • 【RV1126/RV1109学习笔记】#RKNN篇#(2)RKNN环境安装与测试

    文章目录 一 环境要求 二 Python pip 安装 python相关 问题 三 通过 docker 镜像安装 四 验证 五 测试 参考 一 环境要求 项 版本要求 操作系统版本 Ubuntu16 04 x64 及以上 Windows 7
  • 【RV1126/RV1109学习笔记】#基础篇#(1)资料与简介

    文章目录 一 官方资料 二 芯片差异表 三 芯片框架 四 IPC典型应用及EVB系统框图 参考 一 官方资料 瑞芯微智能视觉芯片RV1126是瑞芯微新一代智能视觉芯片 基于四核ARM Cortex A7内核 内置2T算力 NPU 支持4K3
  • 【RV1126/RV1109学习笔记】#RKNN篇#(3)RKNN模型转换示例

    文章目录 一 概述 二 模型转换流程 三 模型转换参考示例 四 执行模型转换 一 概述 RKNN Toolkit 支持 Caffe TensorFlow TensorFlow Lite ONNX Darknet Pytorch MXNet
  • 【RV1126/RV1109学习笔记】#RKNN篇#(4)RKNN模型推理示例

    文章目录 一 概述 二 模型推理流程 三 模型推理参考示例 四 执行模型推理 问题解决 一 概述 RKNN Toolkit 能够在 PC 上模拟 Rockchip NPU 运行 RKNN 模型并获取推理结果 也可以将RKNN 模型分发到指定
  • 【目录】瑞芯微RV1126/RV1109学习笔记

    目录 瑞芯微RV1126 RV1109学习笔记 基础篇 RV1126 RV1109学习笔记 基础篇 xff08 1 xff09 资料与简介 RV1126 RV1109学习笔记 基础篇 xff08 2 xff09 基础入门使用 RV1126
  • word文档转html利器

    有需求 xff0c 就会有相应的技术出现 xff0c 以前知道word可以用来书写html网页 xff0c 那自然就可以转成html页面 xff0c 难道我们要用word开发html页面吗 xff1f 你可能会问 xff0c 我为什么要把w
  • 【RV1126/RV1109学习笔记】#RockX篇#(3)人脸检测

    文章目录 一 概述 二 流程 三 API说明 四 实现代码 一 概述 人脸检测是人脸识别 人脸属性分类 人脸编辑 人脸跟踪等任务必不可少的早期步骤 其性能直接影响到人脸识别等任务的有效性 尽管在过去的几十年里 不受控制的人脸检测取得了巨大的
  • 【RV1126/RV1109学习笔记】#RockX篇#(2)API说明

    文章目录 基础API 人脸相关API 基础API span class token comment brief 创建Rockx模块 param handle out 创建的模块的句柄 param m in RockX模块的枚举 ref Ro
  • 【问题解决】python安装bs4后,仍然报错 ModuleNotFoundError: No module named ‘bs4‘

    问题 我这里是windows上使用出现的问题 xff1a 代码中使用了 from bs4 span class token function import span BeautifulSoup span class token commen
  • 【问题解决】python报错 IndexError: list index out of range

    问题 list 遍历时报错 xff1a IndexError list index out of range 分析 可能是 xff1a list 下标 index 超出范围list 为空 解决 try span class token pu
  • 机械+固态双硬盘时机械硬盘卡顿问题解决

    机械 43 固态双硬盘时机械硬盘卡顿问题解决 参考文章 xff1a xff08 1 xff09 机械 43 固态双硬盘时机械硬盘卡顿问题解决 xff08 2 xff09 https www cnblogs com xia weiwen p
  • IDM下载百度资源出现403的解决方法

    IDM下载百度资源出现403的解决方法 参考文章 xff1a xff08 1 xff09 IDM下载百度资源出现403的解决方法 xff08 2 xff09 https www cnblogs com aucy p 9567375 html
  • _tcsrchr 的用法

    tcsrchr 查找字符串中某个字符最后一次出现的位置 两个参数 第一个参数 xff1a 字符串 第二个参数 xff1a 查找的字符 返回值 xff1a 指向最后一次在字符串中出现的该字符的指针 xff0c 如果要查找的字符再串中没有出现
  • 航模无人机中PWM信号频率浅谈

    航模中执行机构 xff1a 动力电调 xff0c 舵机等基本都是通过PWM信号来驱动的 xff0c 但不同的电机或电调输入的信号频率和占空比又是不一样的 xff0c 这个是由什么决定的呢 xff1f 今天就以我的理解来简单讲一讲 最早的航模
  • 公网IP TCP服务器调试

    在MCU上调试4G等模块时 xff0c 为了验证和平台TCP通信是否顺畅 xff0c 很多时候需要一个公网的IP环境能够直接和4G模块通信 xff0c 一些网络调试工具只能提供局域网内的TCP Server 博主找到一个好的网站提供这个功能
  • windows环境下安装opendr

    pip install opendr会报下面的错误 应该先安装glfw glfw是opengl的一个框架 xff0c pip install glfw 即可安装 下载 https github com polmorenoc opendr 后
  • DockerFile创建及案例

    DockerFile dockerfile是用来构建docker镜像的文件 xff0c 命令脚本参数脚本 xff01 构建步骤 编写一个dockerfile文件docker build 构建成为一个对象docker run 运行镜像dock
  • 结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程

    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程 一 xff0c 实验目标 以fork和execve系统调用为例分析中断上下文的切换分析execve系统调用中断上下文的特殊之处分析fork子进程启动执行时进程上下文的特殊之
  • SPI、UART、RS232、RS485、IIC 5种嵌入式经典通信总线协议

    文章目录 一 UART1 通信基础 并行和串行并行通信串行通信 2 通信基础 单工和双工3 通信基础 比特率4 通讯基础 异步和同步5 UART硬件连接6 UART控制器串口控制器工作原理介绍 xff1a FIFO模式及其作用 7 IO操作
  • 基于Modbus协议的C#串口工具开发

    xff08 Tips 本人纯萌新小白一枚 xff0c 文章只为记录自己的编程历史 xff0c 温故而知新 xff0c 能帮助到大家倍感荣幸 xff0c 有错误的地方劳烦指点 xff01 xff09 不多废话直接进入主题 xff01 本文旨在