C# Modbus通信

C# Modbus通信,c#,crc,modbus,C#,Crc,Modbus,我使用C#通过modbus rs485 rs232与记录电源电压的2相仪表进行通信 我必须通过总线发送数据,以便接收读数。 我已经连接了一条正常的电线,并且短接了发送和接收 接收数据并触发此事件: private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; byte[] buff = new byte[sp.By

我使用C#通过
modbus rs485 rs232
与记录电源电压的2相仪表进行通信

我必须通过总线发送数据,以便接收读数。
我已经连接了一条正常的电线,并且短接了发送和接收

接收数据并触发此事件:

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    byte[] buff = new byte[sp.BytesToRead];

    //Read the Serial Buffer
    sp.Read(buff, 0, buff.Length);
    string data= sp.ReadExisting();

    foreach (byte b in buff)
    {
        AddBuffer(b); //Add byte to buffer
    }
}
然后将该缓冲区发送到另一个函数,即:

private void AddBuffer(byte b)
{
    buffer.Add(b);

    byte[] msg =  buffer.ToArray();

    //Make sure that the message integrity is correct
    if (this.CheckDataIntegrity(msg))
    {
        if (DataReceived != null)
        {
            ModbusEventArgs args = new ModbusEventArgs();
            GetValue(msg, args);
            DataReceived(this, args);
        }
        buffer.RemoveRange(0, buffer.Count);

    }
}
我认为问题在于数据完整性检查:

public bool CheckDataIntegrity(byte[] data)
{
    if (data.Length < 6)
        return false;
    //Perform a basic CRC check:
    byte[] CRC = new byte[2];
    GetCRC(data, ref CRC);
    if (CRC[0] == data[data.Length - 2] && CRC[1] == data[data.Length - 1])
        return true;
    else
        return false;
}
public bool CheckDataIntegrity(字节[]数据)
{
如果(数据长度<6)
返回false;
//执行基本的CRC检查:
字节[]CRC=新字节[2];
GetCRC(数据,参考CRC);
如果(CRC[0]==data[data.Length-2]&&CRC[1]==data[data.Length-1])
返回true;
其他的
返回false;
}
有一个CRC校验,奇怪的是它从来没有变成真的。CRC计算:

private void GetCRC(byte[] message, ref byte[] CRC)
{

    ushort CRCFull = 0xFFFF;
    byte CRCHigh = 0xFF, CRCLow = 0xFF;
    char CRCLSB;

    for (int i = 0; i < (message.Length) - 2; i++)
    {
        CRCFull = (ushort)(CRCFull ^ message[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);
}
private void GetCRC(字节[]消息,参考字节[]CRC)
{
ushort CRCFull=0xFFFF;
字节CRCHigh=0xFF,CRCLow=0xFF;
char-CRCLSB;
对于(int i=0;i<(message.Length)-2;i++)
{
CRCFull=(ushort)(CRCFull^消息[i]);
对于(int j=0;j<8;j++)
{
CRCLSB=(字符)(CRCFull&0x0001);
CRCFull=(ushort)((CRCFull>>1)和0x7FFF);
如果(CRCLSB==1)
CRCFull=(ushort)(CRCFull^0xA001);
}
}
CRC[1]=CRCHigh=(字节)((CRCFull>>8)和0xFF);
CRC[0]=CRCLow=(字节)(CRCFull&0xFF);
}

您首先需要通过一些现有的MODBUS主应用程序(如)与电表建立通信。然后,一旦通信正常工作并从设备获得有效回复,然后才开始测试代码。通过这种方式,您可以确保问题只存在于您的代码中,而不存在其他问题

例如,要同时连接到两个从设备,必须使用RS485而不是RS232,这要求不同的接线和RS485到PC端的RS232转换器

在RS232中连接RX和TX用于模拟不是一个好主意,因为来自主设备的每个MODBUS消息(广播消息除外)都需要一个不同于消息回显的回复。此外,来自主设备的每条MODBUS消息都嵌入了MODBUS客户机地址,只有一个客户机应回复该消息(MODBUS是单主多从设备协议)

对于CRC计算,这可能有助于MODBUS RTU协议(ASCII不同):


这是一段引用自一个使用MODBUS RTU从协议的嵌入式AVR设备的话。

问题在于ReadExisting()的使用。它不能以这种方式使用,因为缓冲区中充满了来自串行端口的无用数据。这个问题是由@glace在评论中发现的

是否有在线资源具有典型通信会话的抄本,并附有CRC?然后,您至少可以将您的算法应用于这些示例消息,看看是否得到相同的CRC?这是一张单子吗?您确定您的“msg”变量曾经大于6吗?为什么不直接从串行端口使用缓冲区,而不是将其逐个字节分解成循环,在列表中重建它,然后将其转换回全局字节数组?在读取缓冲区的内容后,您还立即调用串行端口上的ReadExisting,为什么?CRC似乎是正确的@AndyzSmith。你叫readexisting realy是什么意思我可以叫它到哪里?是的,buffer是一个列表,或者更好,是否有某种终止字符可以用来表示有效数据传输的结束,而不是无限累积字节并检查长度?在sp.Read()行之后,立即调用sp.ReadExisting()。这是不必要的,甚至可能是你的问题,或者至少是问题的一部分。通过检查BytesToRead属性并构造该大小的缓冲区,您已经正确阅读了。感谢您的帮助,我实际上已经解决了这个问题。。。看来我从串口读错了!。。。但还是帮了不少忙@您是否可以将您的解决方案添加为答案(您可以回答自己的问题)并接受它?我认为这将有助于未来的读者,如果他们遇到类似的问题。(我引用以供参考)我认为这不是c#
function mb_CalcCRC16(ptr: pointer to byte; ByteCount: byte): word;
var
  crc: word;
  b, i, n: byte;
begin
  crc := $FFFF;
  for i := 0 to ByteCount do
    if i = 0 then           // device id is 1st byte in message, and it is not in the buffer
      b := mb_GetMessageID; // so we have to calculate it and put it as 1st crc byte
    else
      b := ptr^;
      Inc(ptr);
    endif;
    crc := crc xor word(b);
    for n := 1 to 8 do
      if (crc and 1) = 1 then
        crc := (crc shr 1) xor $A001;
      else
        crc := crc shr 1;
      endif;
    endfor;
  endfor;
  Return(crc);
end;

function mb_CalcCRC: word; // Calculate CRC for message in mb_pdu
begin // this message can be one that is just received, or in a reply we have just composed
  Return(mb_CalcCRC16(@mb_pdu[1], mb_GetEndOfData));
end;