前言
最近,编程遇到一个情况,使用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超时,如上述方法数据传输没有完全过来,可以调整上述超时时间(调大一点)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论