Modbus RTU - Calcul du CRC 16

Résolu
GingermaN Messages postés 8 Date d'inscription mardi 20 novembre 2007 Statut Membre Dernière intervention 12 mai 2009 - 11 mai 2009 à 13:56
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 - 17 août 2019 à 11:21
Bonjour a vous tous,

Je suis entrain de développer une petite appli Delphi qui permet d'aller écrire dans un registre d'un régulateur West 6100+ la valeur d'une alarme.
J'utilise les composants de Tcomport pour la liaison série puis un convertisseur RS232/RS485 pour faire la liaison avec le regulateur.

Le regulateur utilise le protocole modbus soit RTU soit ASCII, j'ai donc choisi de communiquer en RTU. Mais le soucis vient du polynome imposé par le regulateur pour le calcul du CRC16, qui est le suivant: 216+215+22+1=98309

Voici ce que j'ai codé pour l'envoi de la trame:



procedure TForm1.ButEnvoiClick(Sender: TObject);
var
  CRC:string;
begin
{ouverture du port COM}
comport1.Connected:=true;
ButEnvoi.Enabled:=false;
{Conversion de la valeur de l'alarme en Hexa}
ConcatenerValAlarme:=PanChiffre1.caption+PanChiffre2.caption+PanChiffre3.caption;
ValeurAlarme:=StrToInt(ConcatenerValAlarme);
Data:=inttohex(ValeurAlarme,4);
Label1.Caption:=Data;
{Construction de l'entete de la trame}
Esclave:=IntToHex(StrToInt(CbxEsclave.Text),2);
CodeFonction:=IntToHex(StrToInt(EditCdFct.Text),2);
NumPara6100:=IntToHex(StrToInt(EditNumPara6100.Text),4);
{Assemblage de la trame sans CRC16}
TrameModbus:=Esclave+CodeFonction+NumPara6100+Data;
Label2.Caption:=TrameModbus;
{Calcul de CRC16}
CRC:=CalcCRC16(TrameModbus);
Label3.Caption:=CRC;
TrameModbusCRC16:=TrameModbus+CRC;
Label4.Caption:=TrameModbusCRC16;
{écriture de la trame sur le port COM}
comport1.WriteStr(TrameModbusCRC16);
{{Reception trame reponse (Facultatif, si 1 seul esclave, envoie de la trame
 à l'adresse esclave 0 en broadcast car pas de reponse de l'esclave dans ce cas)}
Comport1.ClearBuffer(True,False); // Supression de ce qui traine dans le buffer d'entrée
long:=Comport1.ReadStr(TrameRecue,LongueurTrame);// Lecture bloquante de la longueur demandée}
Label5.Caption:=TrameRecue;
{fermeture de port COM}
comport1.Connected:=false;
ButEnvoi.Enabled:=true;
end;





et voici ma procedure de calcul du CRC




Function CalcCRC16(trame:string):string;
var
  octet:char;
  ajout:boolean;
  CRC16:WORD;
  i,j:integer;
begin
  CRC16:=$FFFF;
  for i:=0 to length(trame)-1 do
    begin
      octet:=trame[i];
      CRC16:=(CRC16) xor ord(octet);
      for j:=1 to 8 do
        begin
          if odd(CRC16) then
              ajout:=true
          else
              begin
              ajout:=false;
              CRC16:=CRC16 div 2;
              end;
          if ajout=true then
            begin
              CRC16:=CRC16+98309;
              result:= IntToHex(CRC16,4);
            end;
        end;
    end;
end;




Est ce que mon code de calcul du CRC vous semble correct, car lorsque je test mon appli, je visualise mon CRC dans un label, mais si je change ma valeur de 3 ou 4 unité, ma trame change bien mais le CRC non.
par exemple, je met ma valeur d'alarme a 185 ma trame sera: 01 06 000D 00B9 A8DA  // A8DA etant mon CRC.
Pour 189 ma trame sere 01 06 000D 00B9 A8DA // le CRC est toujours A8DA.
Il faut que je monte a 192 pour voir mon CRC changer, pour cette valeur il est a : E9B0.

cela me parait bizarre car la trame changeant, le CRC devrait aussi changer, non?

En vous remerciant par avance.

Cordialement,

Jonathan

PS: Au besoin je peux vous zipper et vous envoyer l'appli complete.

14 réponses

cs_Kenavo Messages postés 702 Date d'inscription vendredi 21 mars 2003 Statut Membre Dernière intervention 1 octobre 2009 5
12 mai 2009 à 15:16
Salut,

Et salut à toi JulioDelphi !

Exact pour ajout ...
Ce qui donne au propre :


begin
  CRC16 := $FFFF;
  for i := 1 to length(trame) do








  begin
    octet := trame[i];
    CRC16 := (CRC16) xor ord(octet);
    for j := 1 to 8 do
    begin



     


Ajout :=



odd
(CRC16)

;


      CRC16 := CRC16 div 2;


      if ajout then


        CRC16 := CRC16 xor $A001;   






    end;

  end;

  result := IntToHex(CRC16, 4);


end;




Pour le polynôme générateur

:

Each message is followed by CRC (Cyclic Redundancy Check) with two byte. The CRC identify the
incongruity situations of the message, in this case the receiver ignore the message.
The CRC is calculated in accordance with a formula that imply a recursive division of the data by a
polynomial.
The polynomial divisor is:
216 + 215 + 22 + 1(Hex 18005)
but is modified in two ways:
• Since the bits order are reversed, then the binary pattern is also reversed, and the most significant
bit (MSB) is the right-most bit.
• Since interest only the remainder, the right-most bit could be discarded.
Therefore, the polynomial divisor has value: Hex A001

Ken@vo

Code, Code, Codec !
1
Rejoignez-nous