前言
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
Modbus允许多个 (大约240个) 设备连接在同一个网络上进行通信,举个例子,一个测量温度和湿度的装置,并且将结果发送给计算机。在数据采集与监视控制系统(SCADA)中,Modbus通常用来连接监控计算机和远程终端控制系统(RTU)。
常用用法
通讯格式
以功能码03为例
C#实验常用的03,06,10
public class Modbus
{
protected SerialPort port;
public Modbus(SerialPort serialPort)
{
this.port = serialPort;
}
public void OpenPort()
{
try
{
if (!port.IsOpen)
{
port.Open();
}
port.DiscardInBuffer();
port.DiscardOutBuffer();
}
catch (Exception ex)
{
}
}
public void ClosePort()
{
try
{
if (port != null && port.IsOpen)
{
port.Close();
port = null;
}
}
catch (Exception ex)
{
}
}
protected int ReadData(byte[] buffer, int expectCount = 0)
{
int r = port.Read(buffer, 0, buffer.Length);
while (port.BytesToRead > 0)
{
int n = port.Read(buffer, r, buffer.Length - r);
r += n;
}
while (r < expectCount)
{
r += port.Read(buffer, r, buffer.Length - r);
}
return r;
}
/// <summary>
/// 读取保持型寄存器 功能码03
/// </summary>
/// <param name="stationID">站号</param>
/// <param name="addr">寄存器地址</param>
/// <param name="length">寄存器数量</param>
/// <param name="buffer">接收数据的缓存</param>
/// <returns>收到的字节数</returns>
public int ReadRegister(int stationID, ushort addr, int length, byte[] buffer)
{
OpenPort();
byte[] cmdHead = new byte[6];
cmdHead[0] = (byte)stationID;
cmdHead[1] = 0x03;
cmdHead[2] = (byte)((addr >> 8) & 0xFF);
cmdHead[3] = (byte)(addr & 0xFF);
cmdHead[4] = (byte)((length >> 8) & 0xFF);
cmdHead[5] = (byte)(length & 0xFF);
byte[] command = CRC.AppendCRC16(cmdHead);
//发送指令
port.Write(command, 0, command.Length);
int bytes = ReadData(buffer, 5 + 2 * length);
return bytes;
}
/// <summary>
/// 写入保持型寄存器 功能码06
/// </summary>
/// <param name="stationID">站号</param>
/// <param name="addr">寄存器地址</param>
/// <param name="value">写入值</param>
/// <returns></returns>
public int WriteRegister(int stationID, ushort addr, int value, byte[] buffer = null)
{
OpenPort();
byte[] cmdHead = new byte[6];
cmdHead[0] = (byte)stationID;
cmdHead[1] = 0x06;
cmdHead[2] = (byte)((addr >> 8) & 0xFF);
cmdHead[3] = (byte)(addr & 0xFF);
cmdHead[4] = (byte)((value >> 8) & 0xFF);
cmdHead[5] = (byte)(value & 0xFF);
byte[] command = CRC.AppendCRC16(cmdHead);
//发送指令。
port.Write(command, 0, command.Length);
if (buffer != null)
{
int bytes = ReadData(buffer, 8);
return bytes;
}
return 0;
}
/// <summary>
/// 写入一段连续的寄存器 功能码10
/// </summary>
/// <param name="stationID">站号</param>
/// <param name="addr">寄存器地址</param>
/// <param name="value">写入值</param>
/// <returns></returns>
public int WriteRegisters(int stationID, ushort addr, byte[] value, byte[] buffer = null)
{
OpenPort();
int registerCount = value.Length / 2;
byte[] cmdHead = new byte[7 + value.Length];
cmdHead[0] = (byte)stationID;
cmdHead[1] = 0x10;
cmdHead[2] = (byte)((addr >> 8) & 0xFF);
cmdHead[3] = (byte)(addr & 0xFF);
cmdHead[4] = (byte)((registerCount >> 8) & 0xFF);
cmdHead[5] = (byte)(registerCount & 0xFF);
cmdHead[6] = (byte)(value.Length & 0xFF);
for (int i = 0; i < value.Length; i++)
{
cmdHead[7 + i] = value[i];
}
byte[] command = CRC.AppendCRC16(cmdHead);
//发送指令。
port.Write(command, 0, command.Length);
if (buffer != null)
{
int bytes = ReadData(buffer, 8);
return bytes;
}
return 0;
}
长期分享工控干货,喜欢的话可以点赞关注,谢谢!