如何使用C#(通过RS-485通讯的ModBus RTU)读取MiCOM继电器的保持寄存器

问题描述 投票:0回答:1

我已经尝试过使用RS-485中的Modbus RTU协议进行PC到MiCOM P127中继(主从)通信我使用Visual Studio C#制作程序来读取设备的保存寄存器。我想读0080-0081,=> 0089地址from this manual

enter image description hereenter image description here

这是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;
        }
    }
}

Source here

此应用上的价值不同于设备上的实际价值enter image description here

请帮助我。我应该如何编辑该代码以获取正确的数据?谢谢

c# modbus
1个回答
1
投票

从您链接的手册中:

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)

以上内容与您提供的值不完全匹配,但我怀疑这可能取决于时机(您需要执行一些测试)。希望这可以为您提供一个起点。

© www.soinside.com 2019 - 2024. All rights reserved.