我已经尝试过使用RS-485中的Modbus RTU协议进行PC到MiCOM P127中继(主从)通信我使用Visual Studio C#制作程序来读取设备的保存寄存器。我想读0080-0081,=> 0089地址from this manual
这是Visual Studio上的一些代码
public class ModbusRTUProtocol
{
// Declares variables
private byte slaveAddress = 1;
private byte function = 3;//
private ushort startAddress = 128;//0080 hex => 128 dec
private uint _NumberOfPoints = 3;
private SerialPort serialPort1 = null;
private List<Register> _Registers = new List<Register>();
public ModbusRTUProtocol(uint pNumberOfPoints)
{
this.NumberOfPoints = pNumberOfPoints;
for (int i = 0; i < this.NumberOfPoints; i++)
{
_Registers.Add(new Register() { Address = (ushort)(startAddress + i) }); // cast type to ushort.
}
}
/// <summary>
/// Starts Modbus RTU Service.
/// </summary>
public void Start()
{
try
{
serialPort1 = new SerialPort("COM7", 19200, Parity.None, 8, StopBits.One);
if (serialPort1.IsOpen) serialPort1.Close();
serialPort1.Open();
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
while (true)
{
if (serialPort1.IsOpen)
{
byte[] frame = ReadHoldingRegistersMsg(slaveAddress, startAddress, function, NumberOfPoints);
serialPort1.Write(frame, 0, frame.Length);
Thread.Sleep(100); // Delay 100ms
if (serialPort1.BytesToRead >= 5)
{
byte[] bufferReceiver = new byte[this.serialPort1.BytesToRead];
serialPort1.Read(bufferReceiver, 0, serialPort1.BytesToRead);
serialPort1.DiscardInBuffer();
// Process data.
byte[] data = new byte[bufferReceiver.Length - 5];
Array.Copy(bufferReceiver, 3, data, 0, data.Length);
UInt16[] result = Word.ByteToUInt16(data);
for (int i = 0; i < result.Length; i++)
{
Registers[i].Value = result[i];
}
}
}
Thread.Sleep(20); // Delay 20ms
}
}));
}
catch (Exception ex)
{
throw ex;
}
}
private byte[] ReadHoldingRegistersMsg(byte slaveAddress, ushort startAddress, byte function, uint numberOfPoints)
{
byte[] frame = new byte[8];
frame[0] = slaveAddress; // Slave Address
frame[1] = function; // Function
frame[2] = (byte)(startAddress >> 8); // Starting Address High
frame[3] = (byte)startAddress; // Starting Address Low
frame[4] = (byte)(numberOfPoints >> 8); // Quantity of Registers High
frame[5] = (byte)numberOfPoints; // Quantity of Registers Low
byte[] crc = this.CalculateCRC(frame); // Calculate CRC.
frame[frame.Length - 2] = crc[0]; // Error Check Low
frame[frame.Length - 1] = crc[1]; // Error Check High
return frame;
}
private byte[] CalculateCRC(byte[] data)
{
ushort CRCFull = 0xFFFF; // Set the 16-bit register (CRC register) = FFFFH.
byte CRCHigh = 0xFF, CRCLow = 0xFF;
char CRCLSB;
byte[] CRC = new byte[2];
for (int i = 0; i < (data.Length) - 2; i++)
{
CRCFull = (ushort)(CRCFull ^ data[i]); //
for (int j = 0; j < 8; j++)
{
CRCLSB = (char)(CRCFull & 0x0001);
CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
if (CRCLSB == 1)
CRCFull = (ushort)(CRCFull ^ 0xA001);
}
}
CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
return CRC;
}
public uint NumberOfPoints
{
get
{
return _NumberOfPoints;
}
set
{
_NumberOfPoints = value;
}
}
public List<Register> Registers
{
get
{
return _Registers;
}
set
{
_Registers = value;
}
}
}
请帮助我。我应该如何编辑该代码以获取正确的数据?谢谢
从您链接的手册中:
F18A无符号长整数:数字数据:0至(2E32 -1)
因此,我们正在处理存储在两个寄存器中的32位数字。这意味着我们需要考虑编码。 modbus standard说:
MODBUS使用'big-Endian'表示地址和数据项。
因此设置了寄存器内的字节顺序(这在ReadHoldingRegistersMsg
函数中实现)。但是,它没有说明如何组合多个寄存器,因此可以处理更大数量的寄存器。我浏览了您提供的手册,发现没有提及此内容,但是尝试将两个选项指向little-endian顺序(这意味着第二个寄存器是数字的高位)。
[从屏幕快照中获取数据,寄存器0080 = 21210和寄存器0081 =5。假定一个小的字节序格式等于(5 * 65536)+ 21210 = 348890(注意:这在十六进制hi时更容易可视化= 0x05,低= 0x52DA,结果= 0x552DA)。如文档所述,该数字为10mv,我们除以100,000得到kv,即3.4889kv(预期为3.5kV)。
检查下两个; 0082 = 21789; 0083 =5。(5 * 65536)+ 21789 = 349,469。转换为3.49469kV(预期为3.5kV)。为了完整起见,UC计算为3.49007kV(预期为3.5kV)。
现在让我们看一下寄存器88-6.06kv;从手动格式F1是
无符号整数:数字数据0至65535
所以这些应该是直流电压:
0088-6351 = 6.351kV(预期6.06)0089-6358 = 6.358kV(预期6.07)0090-6343 = 6.343kV(预计6.06)
以上内容与您提供的值不完全匹配,但我怀疑这可能取决于时机(您需要执行一些测试)。希望这可以为您提供一个起点。