C# Modbus

应用程序配置的保存

1 右键应用设置 → 属性 → 添加键值对和用户范围,应用程序和用户范围 

2 获取配置参数:Properties.Settings.Default.参数名

3 修改修改参数 roperties.Settings.Default["A"] = 10 最后调用 save进行保存

1.什么是modbus? 包含的内容

包含的三种协议: modbus-RTU:  modbus-ASCII:   modbus-TCP: 

大部分的硬件都支持modbus-RTU(远程终端设备)协议,对数据在传输过程和接收过程格式的规定

2.modbus作用?

用来通信;  如果没有协议:发送数据,接收方不知道这些数据的作用

 协议方就是指定数据的准则的

3 modbus-RTU: 协议是一种开放的串行协议,广泛应用于当今的工业监控设备中。该协议使用 RS-232 或 RS-485 串行接口进行通信,并得到市场上几乎所有商业 SCADA、HMI、OPC 服务器和数据采集软件程序的支持。因此,很容易将 Modbus 兼容设备集成到新的或现有的监控应用程序中,并具有即时的软件支持。

4 modbus 的主从技术()?

Modbus RTU 协议使用主/从技术在设备之间进行通信。这意味着,任何使用 Modbus RTU 协议的应用程序都将有一个 Modbus 主站和至少一个 Modbus 从站。Modbus Master 通常是一台运行软件的主机监控计算机,它将与一个或多个 Modbus Slave 设备进行通信。Modbus 从设备是执行系统参数测量和控制系统中的开/关设备的设备。为了执行这些任务,主站向 Modbus 从站发送消息,请求执行特定任务。

modbus中的帧结构?
帧就是系统 Master和Slave 设备之间发送的信息,master发送的叫做命令帧,Slave返回的叫做应答帧
帧的结构 = 地址位 + 功能码 + 数据 + CRC校验

地址: 占用一个字节,范围0-255,其中有效范围是1-247,其他有特殊用途

功能码:占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,
所以不同功能码对应不同功能。

数据:根据功能码不同,有不同结构,在下面的实例中有说明。
校验:为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;
如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了

Modbus-RTU协议一般我们用的最多功能码就是`03`​和`06`​,大部分都是用modbus来查询传感器上的信息用`03`​查询功能码(读寄存器),

如果需要修改传感器寄存器的值就用`06`​修改功能码(写寄存器),其他的不需要过多关注

6 上位机需要选择合适的 Modbus 协议栈、配置 Modbus 通信参数、建立 Modbus 通信连接、发送 Modbus 命令帧、接收 Modbus 响应帧和关闭 Modbus 通信连接等步骤。

应用程序配置的读取,保存

  #region ① 读取,修改 
  // 读取setting配置的参数
  // Properties.Settings.Default.参数名
  this.Text = Properties.Settings.Default.A; // 修改窗体标题
  this.BackColor = Properties.Settings.Default.B;// 修改窗体背景

  // 修改参数 应用程序范围参数 不能在运行的时候修改:用户范围的参数可以在运行时候修改
  Properties.Settings.Default["A"] = "31马赫的威慑力,奥特之王都追不上";
  Properties.Settings.Default["B"] = Color.PaleGreen;
  Properties.Settings.Default.Save(); // 保存修改
  #endregion

Modbus协议

            // 初始化串口参数配置
            serialPort1.PortName = "COM5"; // 串口名字
            serialPort1.BaudRate = 4800; // 波特率
            serialPort1.Parity = System.IO.Ports.Parity.None;// 无奇偶校检
            serialPort1.DataBits = 8; // 数据位置
            serialPort1.StopBits = System.IO.Ports.StopBits.One; // 停止位
            serialPort1.Open(); // 开放
            serialPort1.DataReceived += serialPort1_DataReceived;

        }
        // 接收到数据的方法 
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] body = new byte[20];
            serialPort1.Read(body, 0, body.Length);// 读取应答帧
            // 01 03 02 06 C3 FB B5 
            // 01 地址码
            // 03 功能码
            // 02 长度
            // 06 C3 数据位
            // FB B5 校检码



            // 将数据位解析为10进制
            Console.WriteLine(body[3] * 256 + body[4]);

            // 06C3     真正的值
            // 06 C3, C#是小端模式,这个数组被传递进去之后会被认为06为低位,C3为高位,按照C306进行计算
            // 先获取中间的两个字节, 再反转,使低位在前,高位在后
            byte[] value = body.Skip(3).Take(2).Reverse().ToArray();
            Console.WriteLine(BitConverter.ToUInt16(value, 0));

            BeginInvoke(new Action(() =>
            {
                label1.Text = BitConverter.ToUInt16(value, 0).ToString();
            }));
        }

        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;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 组织回答帧CO2
            byte[] bs = new byte[]
            {
                0x01,// 地址码 占1个字节
                0x03,// 功能码 读取 查询功能 占1个字节
                0x00,0x08,  // 起始地址,占2个字节
                0x00,0x01,// 数据长度 1个长度 占2个字节
                            // 校检码 2个字节
            };
            // 2 通过crc进行计算校检码
            byte[] abc = CRCCalc(bs); //  0x05,0xc8
            bs = bs.Concat(abc).ToArray();// 把bs和校检码进行合并

            //3 发送
            serialPort1.Write(bs, 0, bs.Length);
        }
    }
}

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 23:48:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 23:48:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 23:48:02       58 阅读
  4. Python语言-面向对象

    2024-07-12 23:48:02       69 阅读

热门阅读

  1. 安卓热门面试题一

    2024-07-12 23:48:02       19 阅读
  2. React组件间通信的几种方式

    2024-07-12 23:48:02       18 阅读
  3. TCP/IP模型和OSI模型的区别(面试题)

    2024-07-12 23:48:02       20 阅读
  4. opencv--把cv::Mat数据转为二进制数据的保存和读取

    2024-07-12 23:48:02       19 阅读
  5. 扫地机器人如何进行MTBF测试

    2024-07-12 23:48:02       18 阅读
  6. ffmpeg和imagemagick制作gif动图

    2024-07-12 23:48:02       22 阅读
  7. 基于深度学习的PID

    2024-07-12 23:48:02       19 阅读
  8. 【C++】C++中struct结构体和class类的区别

    2024-07-12 23:48:02       15 阅读
  9. CAS详解

    CAS详解

    2024-07-12 23:48:02      16 阅读