2013-08-28 21 views
6

Sto utilizzando C# per comunicare tramite modbus rs485 rs232 a 2 misuratori di fase che tra gli altri registrano la tensione di alimentazione.Comunicazione Modbus

Devo inviare i dati sul bus in modo da poter ricevere le letture.
Ho collegato un filo normale e ho messo in corto l'invio e la ricezione.

I dati è ricevuto e questo evento viene generato:

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

Poi questo buffer viene inviato ad un'altra funzione, che è questo:

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

    } 
} 

Penso che il problema sta alla dati controllo di integrità:

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

C'è un controllo CRC e ciò che è strano è che non è mai bec è vero. Il calcolo 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

Esistono risorse online con una trascrizione di una tipica sessione di comunicazione, completa di CRC? Quindi potresti almeno applicare il tuo algoritmo a quei messaggi di esempio e vedere se ti viene in mente lo stesso CRC. –

+1

Qual è la variabile "buffer"? È una lista ? Sei sicuro che la tua variabile "msg" sia mai più grande di 6? Perché non utilizzare il buffer solo dalla porta seriale invece di scomporlo in un ciclo per byte, ricostruendolo in un elenco, quindi riconvertendolo in un array di byte globale? Chiamate anche ReadExisting sulla porta seriale subito dopo aver letto il contenuto del buffer, perché? –

+0

Il CRC sembra essere corretto @AndyzSmith. cosa intendi con la chiamata a leggere esattamente dove posso chiamarlo?e sì il buffer è una lista – Combinu

risposta

2

Il problema è l'uso di ReadExisting(). Non doveva essere usato in quel modo poiché il buffer veniva riempito con dati inutili dalla porta seriale. Questo problema è stato identificato da @glace nei commenti!

1

Innanzitutto è necessario per stabilire la comunicazione con i vostri metri attraverso alcune applicazioni master MODBUS esistente come MODPOLL. Quindi, una volta che la comunicazione è funzionante e hai risposte valide dal tuo dispositivo, solo allora inizia a testare il tuo codice. In questo modo ti assicuri che il problema possa riguardare solo il tuo codice e nient'altro.

Ad esempio, per connettersi a due dispositivi slave contemporaneamente è necessario utilizzare RS485 anziché RS232, e ciò richiede cablaggio diverso e convertitore da RS485 a RS232 sul lato PC.

Avere RX e TX collegati in RS232 per scopi di simulazione non è una buona idea dato che ogni messaggio MODBUS da un master (eccetto i messaggi broadcast) ha bisogno di una risposta diversa da quella del messaggio echo. Inoltre, ogni messaggio MODBUS di un master ha un indirizzo client MODBUS incorporato in esso e solo un singolo client deve rispondere ad esso (MODBUS è un protocollo a slave multipli master singolo).

Per quanto riguarda il calcolo CRC, questo potrebbe aiutare per il protocollo Modbus RTU (ASCII è diverso):

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; 

Questa è una citazione da un dispositivo AVR incorporato lavorare con implementato il protocollo Modbus RTU slave.

+0

grazie per il tuo aiuto, in realtà ho avuto il problema di lavorare ... sembra che stavo leggendo male dalla porta seriale! ... ma aiuto ancora molto appriciato !! – Combinu

+0

@MysticJay è possibile aggiungere la soluzione come risposta (è possibile rispondere alla propria domanda) e accettarla? Penso che questo potrebbe aiutare i futuri lettori se si imbattono in problemi simili. (Mi riferisco a [xkcd] (http://xkcd.com/979/) per riferimento) – Default

+0

non penso che sia C# – mrid