一、核心前置知识点(必考)
1.1 NModbus 库介绍
NModbus / NModbus4 是 .NET 生态工业标准 Modbus 第三方开源库,封装了底层串口、CRC16、LRC、帧拼接、帧解析所有底层逻辑,开发者无需手写校验、组帧、解帧代码,直接调用API即可完成Modbus读写,大幅简化开发。
支持模式:Modbus-RTU(串口)、Modbus-TCP(网络),本课重点为串口RTU主站开发。
1.2 库安装方式
NuGet 安装包:NModbus或NModbus4
控制台安装命令:Install-Package NModbus4
1.3 核心命名空间
using System.IO.Ports; using Modbus.Device;1.4 与原生手写Modbus的区别(简答题)
原生手写:需手动组帧、手写CRC16、手动解析高低字节、处理粘包分包、跨线程UI
NModbus库:底层全部封装,只需传「从站地址、起始地址、个数」三个参数,直接读写数据
二、核心对象与初始化知识点(带代码)
2.1 两大核心对象
SerialPort:系统原生串口对象,负责底层串口通信
IModbusMaster:NModbus主站接口对象,所有读写API都由该对象调用,依赖串口对象创建
2.2 全局参数定义(规范写法)
// 串口基础配置参数 public SerialPort port; public IModbusMaster _masterModbus; public string portName = "COM1"; public int botelv = 9600; public int dataBit = 8; public StopBits stopbit = StopBits.One; public Parity jiou = Parity.None; public int timeout = 10000; // 读写超时时间2.3 方式一:工厂模式初始化(推荐、项目常用)
通过ModbusFactory 工厂类创建主站对象,结构规范、便于统一管理。
public void InitPort() { // 1.初始化串口对象并赋值参数 port = new SerialPort(portName) { BaudRate = botelv, StopBits = stopbit, Parity = jiou, DataBits = dataBit }; // 2.工厂创建RTU主站对象 ModbusFactory fac = new ModbusFactory(); _masterModbus = fac.CreateRtuMaster(port); // 3.设置读写超时 _masterModbus.Transport.ReadTimeout = timeout; _masterModbus.Transport.WriteTimeout = timeout; }2.4 方式二:静态方法快速创建(临时测试常用)
无需工厂类,直接通过ModbusSerialMaster静态方法创建,适合简单测试场景。
SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); port.ReadTimeout = 1000; port.WriteTimeout = 1000; // 静态创建RTU主站 IModbusMaster master = ModbusSerialMaster.CreateRtu(port);三、串口开关功能知识点(带异常处理)
3.1 打开串口(容错处理)
private void button1_Click(object sender, EventArgs e) { try { if (!port.IsOpen) { port.Open(); MessageBox.Show("打开串口成功"); } } catch (Exception) { MessageBox.Show("打开串口失败"); } }考点:加入try-catch容错,避免串口被占用、端口不存在导致程序崩溃。
3.2 关闭串口
private void button2_Click(object sender, EventArgs e) { if (port.IsOpen) { port.Close(); } }四、Modbus 全部读写API知识点(同步+异步、全覆盖)
参数统一说明: 参数1:从站地址(SlaveID,默认1) 参数2:寄存器/线圈起始地址 参数3:读取个数/写入数据
4.1 同步读取保持寄存器(03功能码)
读取可读可写保持寄存器,返回 ushort 数组,无需手动高低位换算,库内部自动解析。
private void button3_Click(object sender, EventArgs e) { try { if (!port.IsOpen) { MessageBox.Show("串口未打开"); return; } // 从站1号、起始地址0、读取4个寄存器 ushort[] bs = _masterModbus.ReadHoldingRegisters(1, 0, 4); label1.Text = bs[2].ToString(); } catch (Exception) { MessageBox.Show("读取数据失败"); } }4.2 异步读取保持寄存器(Async/Await)
异步方法不阻塞UI界面,适合高频轮询场景,方法名带Async后缀。
private async void button4_Click(object sender, EventArgs e) { // 异步读取,无需阻塞主线程 ushort[] bs = await _masterModbus.ReadHoldingRegistersAsync(1, 0, 4); foreach (ushort s in bs) { Console.WriteLine(s); } }4.3 写入多个寄存器(16功能码)
private void button5_Click(object sender, EventArgs e) { // 定义批量写入数据 ushort[] data = new ushort[] { 100, 200, 300, 400 }; // 从站1、起始地址30、批量写入数组数据 _masterModbus.WriteMultipleRegisters(1, 30, data); }4.4 异步写入单个寄存器(06功能码)
private void button6_Click(object sender, EventArgs e) { // 异步写入:从站1、地址11、写入值100 _masterModbus.WriteSingleRegisterAsync(1, 11, 100); }4.5 读取线圈状态(01功能码)
线圈存储开关量 0/1,返回bool数组,true=1、false=0。
private void button7_Click(object sender, EventArgs e) { // 读取4个线圈状态 bool[] status = _masterModbus.ReadCoils(1, 0, 4); foreach (var item in status) { Console.WriteLine(item); } }4.6 读取输入寄存器(04功能码)
输入寄存器为只读,存储下位机采集的传感器原始数据。
private void button8_Click(object sender, EventArgs e) { // 从站1、起始地址2、读取2个输入寄存器 ushort[] bs = _masterModbus.ReadInputRegisters(1, 2, 2); foreach (var item in bs) { Console.WriteLine(item); } }五、两种初始化方式对比考点
5.1 工厂模式(ModbusFactory)
优势:全局初始化一次,全程复用主站对象,性能高
适用:正式项目、长期通信、轮询采集
5.2 静态创建(ModbusSerialMaster)
优势:代码简洁、无需全局变量
劣势:每次点击重新创建串口对象,频繁开关端口,易报错
适用:临时测试、单次读写调试
// 静态创建完整测试代码 private void button1_Click(object sender, EventArgs e) { SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); port.ReadTimeout = 1000; port.WriteTimeout = 1000; IModbusMaster master = ModbusSerialMaster.CreateRtu(port); if (!port.IsOpen) port.Open(); ushort[] bs = master.ReadHoldingRegisters(1, 0, 4); port.Close(); }六、高频易错考点(考试必背)
无需手动CRC、组帧、解帧:NModbus库底层封装所有校验和帧逻辑
数据类型区分:寄存器返回 ushort[],线圈返回 bool[]
异步方法必须加async:调用xxxAsync方法,方法修饰符必须为async
串口判空:所有读写操作前必须判断串口是否打开
超时配置:必须配置读写超时,防止设备无响应导致程序卡死
功能码对应API:03-ReadHoldingRegisters、01-ReadCoils、04-ReadInputRegisters、06/16-Write寄存器
七、原生Modbus VS NModbus库 终极对比
对比项 | 原生手写Modbus | NModbus第三方库 |
|---|---|---|
校验算法 | 手写CRC16/LRC | 底层封装,无需手写 |
帧处理 | 手动组帧、高低位拆分、解析 | 自动组帧、自动解析 |
代码量 | 大、繁琐 | 极简、仅调用API |
适用场景 | 考试原理考察 | 工业项目开发 |