modbus rtu通讯发送数据优化方式

 更新时间:2025年07月23日 14:36:09   作者:老农民编程  
使用NModbus4库发送数据时出现IO操作中止,可能因串口参数不一致、RS485收发切换未正确配置或超时设置不当导致,建议检查通信参数一致性、添加收发控制代码并适当调整超时时间以解决冲突

前言

最近,编程遇到一个情况,使用NModbus4库实现通讯并发送数据给别一个设备,报IO操作已中止

具体情况:使用Modbus Poll发送,在另一个设备的Modbus Poll可以接收到。使用NModbus4库的通讯程序发送,在同一个设备上能接收,在不同的设备上的Modbus Poll不可以接收到。

一、IO操作中止可能原因分析

IO操作已中止,可能原因有如下几点:

1.物理连接问题(传输线问题)

  • 因为使用调试工具能够成功连接并发送数据,排除其原因。

2.串口配置不一致

(两台电脑的串口参数(波特率、数据位、停止位、校验位)必须完全一致。)

  • 检查两台电脑串口参数是否一致。

3.收发方向控制(RS485特有)

  • 1)问题根源:

RS485是半双工,需通过RTS或DE/RE引脚控制收发切换。若未正确切换,会导致冲突或数据丢失。

  • 2)解决方案:
// 启用RTS控制(部分USB转485芯片需要)
serialPort.RtsEnable = true;
// 或手动控制(根据转换器文档)
serialPort.Handshake = Handshake.RequestToSend;

添加上述代码的其中一条。

4.超时或数据冲突

1)调整超时时间(避免无限等待):

serialPort.ReadTimeout = 1000; // 毫秒(1秒超时)
serialPort.WriteTimeout = 1000;

目的:确保两台设备不会同时发送数据(RS485同一时刻只能一个发送方)。

2)单独设置Modbus超时

master = ModbusSerialMaster.CreateRtu(serialPort);
master.Transport.ReadTimeout = 1000; // 单独设置 Modbus 超时 1秒

二、具体实现

1.使用NModbus4库实现串口连接(使用NModbus2.1.0版本)

使用NModbus4库,创建RS485通讯类,代码如下:

using Modbus.Device;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole.Serivces
{
    class RS485Service
    {
        //从站地址
        byte slaveAddress = 1;

        public IModbusMaster master;
        public SerialPort serialPort = null;

        public RS485Service()
        {

        }

        public void Connect()
        {
            if (serialPort == null)
            {
                try
                {
                    //Serial port
                    string portName = "COM6";
                    int baudRate = 9600;
                    Parity parity = Parity.None;
                    int dataBits = 8;
                    StopBits stopBits = StopBits.One;
                    serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
                    //serialPort.DataReceived += SerialPort_DataReceived;

                    serialPort.ReadTimeout = 1000; // 1秒超时
                    serialPort.WriteTimeout = 1000;
                    // 启用RTS控制(部分USB转485芯片需要)
                    serialPort.RtsEnable = true;

                    serialPort.Open();

                    if (serialPort.IsOpen)
                    {
                        master = ModbusSerialMaster.CreateRtu(serialPort);
                        master.Transport.ReadTimeout = 1000; // 单独设置 Modbus 超时

                        Console.WriteLine("串口通讯连接成功!");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("串口通讯未连接!" + ex.Message);
                }
            }
        }

        #region 地址写值

        //写单个线圈(功能码 0x05)
        //如果需要控制单个 IO 点(例如一个继电器),可以使用 WriteSingleCoil 方法
        public void WriteCoil(ushort coilAddress, bool value)
        {
            master.WriteSingleCoil(slaveAddress, coilAddress, value);
        }

        //写多个线圈(功能码 0x0F)
        //如果需要同时控制多个 IO 点,可以使用 WriteMultipleCoils 方法
        public void WriteMultCoils(ushort startAddress, bool[] values)
        {
            master.WriteMultipleCoils(slaveAddress, startAddress, values);
        }

        //写单个保持寄存器(功能码 0x06)
        //如果需要写入单个寄存器(例如一个数字量输出),可以使用 WriteSingleRegister 方法
        public void WriteRegister(ushort registerAddress, ushort value)
        {
            master.WriteSingleRegister(slaveAddress, registerAddress, value);
        }

        //写多个保持寄存器(功能码 0x10)
        //如果需要写入多个寄存器,可以使用 WriteMultipleRegisters 方法
        public void WriteMultRegisters(ushort startAddress, ushort[] values)
        {
            master.WriteMultipleRegisters(slaveAddress, startAddress, values);
        }

        #endregion
    }
}

2.控制台Main函数实现

在Main函数中实现通讯并发送数据,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TestConsole.Serivces;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                RS485Service rs485Trans = new RS485Service();
                rs485Trans.Connect();

                ushort[] tempList = {1,2,3,4};
                // 写入操作
                rs485Trans.WriteMultRegisters(0, tempList);
                Console.WriteLine("写入成功");
            }
            catch (Exception ex)
            {
                Console.WriteLine("发生错误" + ex.Message);
            }
    
            Console.ReadLine();
        }
    }
}

总结

IO操作已中止问题,大概率就是IO超时,如上述方法数据传输没有完全过来,可以调整上述超时时间(调大一点)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C#快速实现拖放操作

    C#快速实现拖放操作

    这篇文章介绍了C#快速实现拖放操作的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • 在C#项目中调用C++编写的动态库的三种方式

    在C#项目中调用C++编写的动态库的三种方式

    这篇文章给大家介绍了三种方式详解如何在C#项目中调用C++编写的动态库,文中通过代码示例给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2024-01-01
  • c# 用ICSharpCode组件压缩文件

    c# 用ICSharpCode组件压缩文件

    今天,主要总结下,windows平台下,zip的压缩与解压的方法,用ICSharpCode组件,有此需求的朋友可以参考下本文
    2021-06-06
  • C#中异步回调函数用法实例

    C#中异步回调函数用法实例

    这篇文章主要介绍了C#中异步回调函数用法,实例分析了异步回调函数的定义及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-04-04
  • C#使用SQL DataAdapter数据适配代码实例

    C#使用SQL DataAdapter数据适配代码实例

    今天小编就为大家分享一篇关于C#使用SQL DataAdapter数据适配代码实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • C# 中闭包(Closure)详解

    C# 中闭包(Closure)详解

    这篇文章主要介绍了C# 中闭包(Closure)详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • C# PropertyInfo类案例详解

    C# PropertyInfo类案例详解

    这篇文章主要介绍了C# PropertyInfo类案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C#线性渐变画刷LinearGradientBrush用法实例

    C#线性渐变画刷LinearGradientBrush用法实例

    这篇文章主要介绍了C#线性渐变画刷LinearGradientBrush用法,实例分析了线性渐变画刷LinearGradientBrush的相关使用技巧,需要的朋友可以参考下
    2015-06-06
  • c# 匿名方法的小例子

    c# 匿名方法的小例子

    c# 匿名方法的小例子,需要的朋友可以参考一下
    2013-04-04
  • C#利用System.Threading.Thread.Sleep即时输出信息的详解

    C#利用System.Threading.Thread.Sleep即时输出信息的详解

    本篇文章是对C#利用System.Threading.Thread.Sleep即时输出信息进行了详细的分析介绍,需要的朋友参考下
    2013-06-06

最新评论