当前位置: 代码迷 >> 综合 >> C# Modbus Rtu实现
  详细解决方案

C# Modbus Rtu实现

热度:44   发布时间:2024-02-04 17:05:43.0

Modbus Rtu的实现与Modbus Tcp的实现类似 C#ModBus Tcp的学习及Master的实现

我们还是需要借用一个开源库NModbus4,在vs中.打开NuGet管理器.安装NModbus4

具体实现,具体实现与之前的Modbus Tcp的实现类似 ,只是在实例化master时将TCPClient换为串行端口资源SerialPort,并在实例化时设置好端口所需参数(端口名,波特率,校验位,停止位,数据位)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Modbus.Device;
using System.Net.Sockets;
using System.Threading;
using System.IO.Ports;namespace ModbusRtu
{public partial class Form1 : Form{private static IModbusMaster master;private static SerialPort port;//写线圈或写寄存器数组private bool[] coilsBuffer;private ushort[] registerBuffer;//功能码private string functionCode;//参数(分别为站号,起始地址,长度)private byte slaveAddress;private ushort startAddress;private ushort numberOfPoints;//串口参数private string portName;private int baudRate;private Parity parity;private int dataBits;private StopBits stopBits;public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){cmb_portname.SelectedIndex = 0;cmb_baud.SelectedIndex = 5;cmb_parity.SelectedIndex = 2;cmb_databBits.SelectedIndex = 1;cmb_stopBits.SelectedIndex = 0;}private SerialPort InitSerialPortParameter(){if (cmb_portname.SelectedIndex < 0 || cmb_baud.SelectedIndex < 0 || cmb_parity.SelectedIndex < 0 || cmb_databBits.SelectedIndex < 0 || cmb_stopBits.SelectedIndex < 0){MessageBox.Show("请选择串口参数");return null;}else{portName = cmb_portname.SelectedItem.ToString();baudRate = int.Parse(cmb_baud.SelectedItem.ToString());switch (cmb_parity.SelectedItem.ToString()){case "奇":parity = Parity.Odd;break;case "偶":parity = Parity.Even;break;case "无":parity = Parity.None;break;default:break;}dataBits = int.Parse(cmb_databBits.SelectedItem.ToString());switch (cmb_stopBits.SelectedItem.ToString()){case "1":stopBits = StopBits.One;break;case "2":stopBits = StopBits.Two;break;default:break;}port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);return port;}}/// <summary>/// 读/写/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){try{//初始化串口参数InitSerialPortParameter();master = ModbusSerialMaster.CreateRtu(port);ExecuteFunction();}catch (Exception){MessageBox.Show("初始化异常");}}private async void ExecuteFunction(){try{//每次操作是要开启串口 操作完成后需要关闭串口//目的是为了slave更换连接是不报错if (port.IsOpen == false){port.Open();}if (functionCode != null){switch (functionCode){case "01 Read Coils"://读取单个线圈SetReadParameters();coilsBuffer = master.ReadCoils(slaveAddress, startAddress, numberOfPoints);for (int i = 0; i < coilsBuffer.Length; i++){SetMsg(coilsBuffer[i] + " ");}SetMsg("\r\n");break;case "02 Read DisCrete Inputs"://读取输入线圈/离散量线圈SetReadParameters();coilsBuffer = master.ReadInputs(slaveAddress, startAddress, numberOfPoints);for (int i = 0; i < coilsBuffer.Length; i++){SetMsg(coilsBuffer[i] + " ");}SetMsg("\r\n");break;case "03 Read Holding Registers"://读取保持寄存器SetReadParameters();registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);for (int i = 0; i < registerBuffer.Length; i++){SetMsg(registerBuffer[i] + " ");}SetMsg("\r\n");break;case "04 Read Input Registers"://读取输入寄存器SetReadParameters();registerBuffer = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);for (int i = 0; i < registerBuffer.Length; i++){SetMsg(registerBuffer[i] + " ");}SetMsg("\r\n");break;case "05 Write Single Coil"://写单个线圈SetWriteParametes();await master.WriteSingleCoilAsync(slaveAddress, startAddress, coilsBuffer[0]);break;case "06 Write Single Registers"://写单个输入线圈/离散量线圈SetWriteParametes();await master.WriteSingleRegisterAsync(slaveAddress, startAddress, registerBuffer[0]);break;case "0F Write Multiple Coils"://写一组线圈SetWriteParametes();await master.WriteMultipleCoilsAsync(slaveAddress, startAddress, coilsBuffer);break;case "10 Write Multiple Registers"://写一组保持寄存器SetWriteParametes();await master.WriteMultipleRegistersAsync(slaveAddress, startAddress, registerBuffer);break;default:break;}}else{MessageBox.Show("请选择功能码!");}port.Close();}catch (Exception ex){MessageBox.Show(ex.Message);}}private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){if (comboBox1.SelectedIndex >= 4){groupBox2.Enabled = true;groupBox1.Enabled = false;}else{groupBox1.Enabled = true;groupBox2.Enabled = false;}comboBox1.Invoke(new Action(() => { functionCode = comboBox1.SelectedItem.ToString(); }));}/// <summary>/// 初始化读参数/// </summary>private void SetReadParameters(){if (txt_startAddr1.Text == "" || txt_slave1.Text == "" || txt_length.Text == ""){MessageBox.Show("请填写读参数!");}else{slaveAddress = byte.Parse(txt_slave1.Text);startAddress = ushort.Parse(txt_startAddr1.Text);numberOfPoints = ushort.Parse(txt_length.Text);}}/// <summary>/// 初始化写参数/// </summary>private void SetWriteParametes(){if (txt_startAddr2.Text == "" || txt_slave2.Text == "" || txt_data.Text == ""){MessageBox.Show("请填写写参数!");}else{slaveAddress = byte.Parse(txt_slave2.Text);startAddress = ushort.Parse(txt_startAddr2.Text);//判断是否写线圈if (comboBox1.SelectedIndex == 4 || comboBox1.SelectedIndex == 6){string[] strarr = txt_data.Text.Split(' ');coilsBuffer = new bool[strarr.Length];//转化为bool数组for (int i = 0; i < strarr.Length; i++){// strarr[i] == "0" ? coilsBuffer[i] = true : coilsBuffer[i] = false;if (strarr[i] == "0"){coilsBuffer[i] = false;}else{coilsBuffer[i] = true;}}}else{//转化ushort数组string[] strarr = txt_data.Text.Split(' ');registerBuffer = new ushort[strarr.Length];for (int i = 0; i < strarr.Length; i++){registerBuffer[i] = ushort.Parse(strarr[i]);}}}}/// <summary>/// 清除文本/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button2_Click(object sender, EventArgs e){richTextBox1.Clear();}/// <summary>/// SetMessage/// </summary>/// <param name="msg"></param>public void SetMsg(string msg){richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg); }));}}
}

接下来开始测试

在这里 因为要用到串口,而我的笔记本没有串口,所以需要借助一个工具

Virtual Serial Port Dirver 虚拟串口工具

链接:https://pan.baidu.com/s/1opGre3GS-HWFoA_dP9qYYg

提取码:2afu 

借用这个工具我们添加两个虚拟串口 COM1和COM2 点击Add Virtual Pair 添加

设置Modbus Slave,选择连接方式为串口,选择对应端口,模式选择RTU,建立连接

 

接下来运行我们自己的Modbus RTU Master

设置串口参数(波特率,数据位,奇偶校验,停止位)要与Slave的串口参数一致

我们测试 功能码 0x01 读一组线圈

 

测试完成,数据正常,其他的功能码经测试数据正常,有兴趣的可以自行测试

到此为止,Modbus的学习到此告一段落

以上都为我自行学习并实现,如有错误之处,望大家不吝赐教,感谢(抱拳~)

 

程序源代码:

链接:https://pan.baidu.com/s/1mPAhRixLbsDb7h2ePENTRA
提取码:b5w6

作者: Peter.Pan

出处: https://www.cnblogs.com/pandefu/>

邮箱: defu_pan@163.com

关于作者:.Net,WindowsForm,工控软件,Modbus,OPC/Tcp/Ip,串口通讯

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接,否则保留追究法律责任的权利。 如有问题, 可邮件咨询。

https://www.cnblogs.com/pandefu/p/10849823.html 

  相关解决方案