基于C#和NModbus4库实现的Modbus RTU串口通信,包含完整的界面设计和功能实现:
一、项目依赖配置
-
NuGet包安装:
powershellInstall-Package NModbus4 Install-Package System.IO.Ports
-
窗体控件布局:
xml<!-- 基础控件配置 --> <ComboBox x:Name="cmbPort" Margin="5" Width="120"/> <Button x:Name="btnConnect" Content="连接" Margin="5"/> <Button x:Name="btnRead" Content="读取寄存器" Margin="5"/> <TextBox x:Name="txtLog" Height="200" Margin="5" TextWrapping="Wrap"/> <DataGrid x:Name="dgData" AutoGenerateColumns="False" Margin="5"> <DataGrid.Columns> <DataGridTextColumn Header="地址" Binding="{Binding Address}"/> <DataGridTextColumn Header="值" Binding="{Binding Value}"/> </DataGrid.Columns> </DataGrid>
二、核心代码实现
csharp
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Windows;
using Modbus.Device;
namespace ModbusRTUDemo
{
public partial class MainWindow : Window
{
#region 成员变量
private IModbusSerialMaster _master;
private SerialPort _serialPort;
private const ushort START_ADDR = 40001; // 起始寄存器地址
private const ushort READ_COUNT = 10; // 读取数量
#endregion
public MainWindow()
{
InitializeComponent();
InitializeSerialPorts();
btnConnect.Click += BtnConnect_Click;
btnRead.Click += BtnRead_Click;
}
#region 串口初始化
private void InitializeSerialPorts()
{
var ports = SerialPort.GetPortNames();
cmbPort.ItemsSource = ports;
cmbPort.SelectedIndex = 0;
}
#endregion
#region 连接控制
private void BtnConnect_Click(object sender, RoutedEventArgs e)
{
try
{
if (_master != null && _master.IsOpen)
{
Disconnect();
btnConnect.Content = "连接";
txtLog.AppendText("已断开连接\n");
return;
}
_serialPort = new SerialPort(cmbPort.Text, 9600, Parity.None, 8, StopBits.One)
{
ReadTimeout = 3000,
WriteTimeout = 3000
};
_master = ModbusSerialMaster.CreateRtu(_serialPort);
_master.Transport.Retries = 3; // 重试次数
_master.Transport.WriteTimeout = 2000;
_master.Transport.ReadTimeout = 2000;
_master.Open();
btnConnect.Content = "断开";
txtLog.AppendText($"已连接到 {_serialPort.PortName}\n");
}
catch (Exception ex)
{
txtLog.AppendText($"连接失败: {ex.Message}\n");
}
}
#endregion
#region 数据读取
private async void BtnRead_Click(object sender, RoutedEventArgs e)
{
try
{
var result = await Task.Run(() =>
_master.ReadHoldingRegisters(1, START_ADDR, READ_COUNT));
dgData.ItemsSource = result.Select((value, index) => new
{
Address = START_ADDR + index,
Value = value.ToString("0.00")
}).ToList();
txtLog.AppendText($"读取成功: {result.Length} 个寄存器\n");
}
catch (Exception ex)
{
txtLog.AppendText($"读取失败: {ex.Message}\n");
}
}
#endregion
#region 连接管理
private void Disconnect()
{
_master?.Close();
_master = null;
_serialPort?.Close();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Disconnect();
}
#endregion
}
}
参考代码 C# 写的串口通信程序源码 youwenfan.com/contentcsb/111840.html
三、关键功能说明
- 串口配置
- 支持自动检测可用串口(通过
SerialPort.GetPortNames()
) - 默认参数:9600波特率、无校验、8数据位、1停止位
- 支持自动检测可用串口(通过
- Modbus操作
- 读取保持寄存器 :
ReadHoldingRegisters
方法实现 - 写单个寄存器 :扩展方法
WriteSingleRegister
- 批量写线圈 :
WriteMultipleCoils
方法
- 读取保持寄存器 :
- 异常处理
- 自动重试机制(默认3次重试)
- 超时设置(读写各2秒)
四、扩展功能实现
-
定时数据采集
csharpprivate System.Timers.Timer _pollTimer = new System.Timers.Timer(5000); private void StartPolling() { _pollTimer.Elapsed += (s,e) => { var data = _master.ReadHoldingRegisters(1, START_ADDR, READ_COUNT); Dispatcher.Invoke(() => UpdateDataGrid(data)); }; _pollTimer.Start(); }
-
CRC校验实现
csharpprivate byte[] CalculateCRC(byte[] data) { ushort crc = 0xFFFF; for (int i = 0; i < data.Length; i++) { crc ^= (ushort)data[i]; for (int j = 0; j < 8; j++) { if ((crc & 0x0001) != 0) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return new byte[] { (byte)crc, (byte)(crc >> 8) }; }
五、调试技巧
-
串口监控
使用虚拟串口工具(如VSPD)进行通信调试
-
数据验证
csharp// 校验从站响应 if (response.SlaveId != slaveId) throw new InvalidDataException("从站ID不匹配");
-
协议分析
通过Wireshark抓包分析Modbus RTU帧结构
完整项目源码可通过NuGet部署NModbus4库后导入Visual Studio运行。实际应用中需根据设备手册调整功能码和寄存器地址。