目录
- 前言
- 一、IO操作中止可能原因分析
- 1.物理连接问题(传输线问题)
- 2.串口配置不一致
- 3.收发方向控制(RS485特有)
- 4.超时或数据冲突
- 二、具体实现
- 1.使用NModbus4库实现串口连接(使用NModbus2.1.0版本)
- 2.控制台Main函数实现
- 总结
前言
最近,编程遇到一个情况,使用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 RS485Serviceandroid()
{
}
public void Connect()
{
if (serialPort == null)
{
try
{
//Serial port
string portName = "COM6";
int baudRahttp://www.devze.comte = 9600;
Parity parity = Parity.None;
int dataBits = 8;
StopBits stopBits = StopBits.One;
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
//serialPort.DataReceived += SerialPort_DataRecei编程客栈ved;
serialPort.RepythonadTimeout = 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
{
javascript 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超时,如上述方法数据传输没有完全过来,可以调整上述超时时间(调大一点)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
加载中,请稍侯......
精彩评论