2013-08-28 61 views
6

我正在使用C#通過modbus rs485 rs232與2個相位表進行通信,其中包括其他日誌中的電源電壓。Modbus通信

我必須通過總線發送數據,以便我可以收到讀數。
我已經連接了一條正常的電線並且短接了發送和接收。

的數據被收到,該事件被觸發:

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

有一個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); 
} 
+0

是否有在線資源有一個典型的通信會話的記錄,與CRC完成?然後,您至少可以將您的算法應用於這些示例消息並查看是否提供了相同的CRC。 –

+1

什麼是「緩衝區」變量?它是一個列表?你確定你的「味精」變量是否大於6?爲什麼不直接使用串口的緩衝區而不是逐字節地將其分解,在List中重構,然後將其轉換回全局字節數組?爲什麼在讀取緩衝區的內容後,您還要立即在串行端口上調用ReadExisting? –

+0

CRC似乎是正確的@AndyzSmith。 你是什麼意思打電話redexisting realy我在哪裏可以稱它?是的緩衝區是一個列表 – Combinu

回答

2

問題是使用ReadExisting()。它不會以這種方式使用,因爲緩衝區中充斥着來自串行端口的無用數據。這個問題在評論中被@glace識別!

1

您首先需要通過一些現有的MODBUS主應用像MODPOLL建立與您的溝通米。然後,一旦您的通訊工作正常並從您的設備獲得有效答覆,那麼只有開始測試您的代碼。這樣你就可以確保這個問題只能在你的代碼中,而不是其他的。

例如,要同時連接兩個從屬設備,必須使用RS485而不是RS232,這就要求PC端有不同的接線和RS485到RS232轉換器。

爲了模擬目的而將RX和TX連接到RS232中並不是一個好主意,因爲來自主設備的每條MODBUS消息(廣播消息除外)都需要一個不同於消息迴應的回覆。另外,來自主站的每條MODBUS消息都嵌入了MODBUS客戶端地址,並且只有單個客戶端應答(MODBUS是單主多從站協議)。

對於CRC計算,這可能幫助MODBUS RTU協議(ASCII是不同的):

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; 

這是從實現的MODBUS RTU從協議工作的嵌入式AVR器件的報價。

+0

感謝您的幫助我實際上已經得到了這個問題的工作...似乎我從串口讀錯了......但仍幫助很多appriciated! – Combinu

+0

@MysticJay是否可以添加您的解決方案作爲答案(您可以回答自己的問題)並接受它?我認爲如果他們遇到類似的問題,這將有助於未來的讀者。 (我參考[xkcd](http://xkcd.com/979/)作爲參考) – Default

+0

我不認爲這是c# – mrid