2013-08-28 10 views
6

Używam C# do komunikowania się przez modbus rs485 rs232 do 2 fazomierzy, które między innymi rejestrują napięcie zasilania.Komunikacja Modbus

Muszę przesłać dane przez magistralę, aby móc odbierać odczyty.
Podłączyłem normalny przewód i zwróciłem wysyłkę i odbiór.

Dane otrzymałem i to zdarzenie jest opalane:

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

Wtedy ten bufor jest wysyłany do innej funkcji, która jest taka:

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

    } 
} 

Myślę, że problem leży w danych kontrola integralności:

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

Istnieje kontrola CRC i co jest dziwne, to nigdy nie było omuje prawdę. Obliczenie 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

Czy istnieją zasoby online zawierające transkrypcję typowej sesji komunikacyjnej wraz z CRC? Następnie możesz przynajmniej zastosować algorytm do tych przykładowych wiadomości i sprawdzić, czy wymyślisz tę samą CRC. –

+1

Co to jest zmienna "bufor"? Czy to jest lista ? Czy jesteś pewien, że twoja "msg" zmienna jest zawsze większa niż 6? Dlaczego nie wystarczy użyć bufora z portu szeregowego, zamiast rozbić go w bajt po bajcie, zrekonstruować go na liście, a następnie przekonwertować z powrotem na globalną tablicę bajtów? Możesz także wywołać ReadExisting na porcie szeregowym natychmiast po odczytaniu zawartości bufora, dlaczego? –

+0

CRC wydaje się poprawne @AndyzSmith. co masz na myśli, nazywając czytanie naprawdę, gdzie mogę to nazwać?i tak bufor jest listą – Combinu

Odpowiedz

2

Problem polega na użyciu funkcji ReadExisting(). Nie miało być używane w ten sposób, ponieważ bufor był wypełniany bezużytecznymi danymi z portu szeregowego. Ten problem został zidentyfikowany przez @glace w komentarzach!

1

Najpierw należy nawiązać łączność ze swoimi metrów przez jakiś istniejącej aplikacji Modbus master jak MODPOLL. Następnie, po nawiązaniu komunikacji i otrzymaniu prawidłowych odpowiedzi z urządzenia, wtedy i tylko wtedy zacznij testować swój kod. W ten sposób upewnisz się, że problem może dotyczyć tylko Twojego kodu i nic więcej.

Na przykład, aby połączyć się z dwoma urządzeniami podrzędnymi w tym samym czasie, należy użyć RS485 zamiast RS232, a to wymaga innego okablowania i konwertera RS485 na RS232 po stronie PC.

Posiadanie RX i TX podłączonych w RS232 dla celów symulacji nie jest dobrym pomysłem, ponieważ każda wiadomość MODBUS od urządzenia nadrzędnego (z wyjątkiem komunikatów rozgłoszeniowych) wymaga odpowiedzi, która różni się od samego echa komunikatu. Ponadto, każda wiadomość MODBUS od urządzenia master ma wbudowany adres klienta MODBUS i tylko jeden klient powinien na nią odpowiedzieć (MODBUS jest protokołem dla pojedynczego wzorca wielu urządzeń podrzędnych).

Co do obliczeń CRC, to może pomóc w protokole Modbus RTU (ASCII jest inny):

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; 

To cytat z pracy wbudowanego urządzenia AVR z zaimplementowanym protokołem MODBUS RTU Slave.

+0

dziękuję za pomoc, ale mam problem z działaniem ... wydaje mi się, że czytałem źle z portu szeregowego! ... ale nadal pomagam bardzo docenić !! – Combinu

+0

@MysticJay czy możliwe jest dodanie rozwiązania jako odpowiedzi (możesz odpowiedzieć na własne pytanie) i zaakceptowanie go? Myślę, że pomogłoby to przyszłym czytelnikom, gdyby mieli podobne problemy. (Odnoszę się do [xkcd] (http://xkcd.com/979/) w celach informacyjnych) – Default

+0

Nie sądzę, że jest to C# – mrid