尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C# ModBus

C# ModBus 简单汇总一下不使用NModBus包的C#上位机侧的ModBus实现;1. ModBus Tcppublic class ModBusSerer { private int _index; private int _slaveIndex 1; private TcpClient _tcpClient; public NetworkStream _stream; public void Start(string ip, int port) { _tcpClient new TcpClient(); // 上位机作为主站要主动给从站发送连接请求因此作为Tcp的客户端 _tcpClient.Connect(ip, port); _stream _tcpClient.GetStream(); } public async Taskushort[] ReadSingleRegister(ushort startAddress, ushort count) { byte[] content new byte[5]; // ModBus实际发送内容 content[0] 0x03; // 功能码0x03 读单个保持寄存器0x06 写单个寄存器0x10 写多个寄存器 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); // 寄存器地址ModBus所用地址和实际寄存器地址之间的映射见表格 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), count); // 读取寄存器的数量 byte[] trans new byte[12]; // Tcp发送内容MBAP头内容 ModBus实际内容 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); // ModBus通信索引用于校验 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); // ModBus规定这两位为0 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); // 实际发送内容长度 1 trans[6] _slaveIndex; // 从站索引 content.CopyTo(trans, 7); // 拷贝实际发送内容 await _stream.WriteAsync(trans); // ModBus写 byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); // 自增索引 ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); // ModBus规定这两位为0 ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); // 结果长度 1(从站Id) if (responseId ! _index || responseId ! 0 || length 2) return null; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); // ModBus读响应 if((response[0] 0x80) ! 0) // response第一个字节是功能码如果出现异常则功能码最低位(最低地址)的位会被赋值1既0x03变成0x83 return response[1]; // 返回错误码到时候的response长度问题 else if(response.Length ! 2 count * 2 || response[0] ! 0x03 || response[1] ! count * 2) // 响应字节数组成功能码一字节 后续数据长度一字节 每个寄存器各两字节 return null; ushort[] ans new ushort[count]; for(int i 0; i ans.Length; i) ans[0] BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(2 i * 2, 2)); // 将某个寄存器读上来的两字节转换为一个short return response; } public async Taskbool WriteSingleRegister(ushort startAddress, float value) { byte[] content new byte[5]; content[0] 0x06; BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), value); // 写入寄存器的值 // 下面内容基本和ReadSingleRegister一样 byte[] trans new byte[12]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); trans[6] _slaveIndex; content.CopyTo(trans, 7); await _stream.WriteAsync(trans); byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); if (responseId ! _index || responseId ! 0 || length 2) return false; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); if((response[0] 0x80) ! 0) return false; else if(response ! trans) // 写单个寄存器时只要写入字节数组与相应字节数组一致就算成功 return false; return true; } public async Taskbool WriteMultiRegister(ushort startAddress, ushort[] values) { int count values.Length; // 写入寄存器的数量 byte[] content new byte[6 values.Length * 2]; content[0] 0x10; BinaryPrimitives.WriteUIInt16BigEndian(content.AsSpan(1, 2), startAddress); BinaryPrimitives.WriteUIInt16BigEndian(content.AsSpan(3, 2), count); // 写入寄存器的数量 content[5] (byte(count * 2)); for (int i 0; i count; i) BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(6 i * 2, 2), values[i]); // 写入各个寄存器的值 // 也差不多 byte[] trans new byte[7 content.Length]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); trans[6] _slaveIndex; content.CopyTo(trans, content.Length); await _stream.WriteAsync(trans); byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); if (responseId ! _index || responseId ! 0 || length 2) return false; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); if((response[0] 0x80) ! 0) return false; else if(response.Length ! 5 | response[0]| ! 0x10) // response.Length为5第一个字节功能码二三字节起始地址四五字节寄存器长度和写入的content前五个字节一致 return false; } }2. ModBus SerialPortpublic class ModBusSerer { private int _index; private int _slaveIndex 1; public void Start() { _serialPort new new SerialPort(COM1, 9600, Parity.None, 8, StopBits.One); // 串口对象 } public async Taskushort[] ReadSingleRegister(ushort startAddress, ushort count) { byte[] content new byte[5]; // ModBus实际发送内容 content[0] 0x03; // 功能码0x03 读单个保持寄存器0x06 写单个寄存器0x10 写多个寄存器 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); // 寄存器地址ModBus所用地址和实际寄存器地址之间的映射见表格 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), count); // 读取寄存器的数量 byte[] trans new byte[1 content.Length 2]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _slaveIndex); // 从站索引 content.CopyTo(trans, 1); // 拷贝实际发送内容 ushort sendCrc CalculateCrc16(trans.AsSpan(0, 1 content.Length)); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(1 content.Length, 2), sendCrc); _serialPort.Write(trans, 0, trans.Length); // 串口写 byte[] ans; byte[] buffer new byte[2]; _serialPort.Read(buffer, 0, 2); // 先从串口读取功能码和长度这两个字节 ushort code (ushort)buffer[1]; // 功能码 if ((code 0x80) ! 0) ans _serialPort.Read(ans, 0, 3); // 出现异常读取一字节异常码 2字节CRC else { byte[] count new byte[1]; _serialPort.Read(count, 0, 1); // 读取长度 ushort count (ushort)count[0]; _serialPort.Read(ans, 0, count 2); // 读取所有数据内容 2字节CRC } ushort receiveCrc CalculateCrc16(ans.AsSpan(0, 5)) if (receiveCrc ! sendCrc) return null; else return ans; } private ushort CalculateCrc16(byte[] data) { ushort crc 0xFFFF; foreach(var b in data) { crc ^ b; for(var i 0; i 8; i) { if((crc 0x0001) ! 0) crc (ushort)((crc 1) ^ 0xA001); else crc 1; } } return crc; } }
返回列表