Autor Beitrag
daywalker0086
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243


Delphi 2005 Architect
BeitragVerfasst: Di 09.07.13 11:03 
Hallo Leute ich hab mal wieder ein Problem und komm nicht weiter.
Es geht um eine Checksummenberechnung.
Hier erstmal ein wenig code:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
const
 // Table of CRC values for high–order byte */
 auchCRCHi : array [Byte] of Word =(
 $00$C1$81$40$01$C0$80$41$01$C0$80$41$00$C1$81,
 $40$01$C0$80$41$00$C1$81$40$00$C1$81$40$01$C0,
 $80$41$01$C0$80$41$00$C1$81$40$00$C1$81$40$01,
 $C0$80$41$00$C1$81$40$01$C0$80$41$01$C0$80$41,
 $00$C1$81$40$01$C0$80$41$00$C1$81$40$00$C1$81,
 $40$01$C0$80$41$00$C1$81$40$01$C0$80$41$01$C0,
 $80$41$00$C1$81$40$00$C1$81$40$01$C0$80$41$01,
 $C0$80$41$00$C1$81$40$01$C0$80$41$00$C1$81$40,
 $00$C1$81$40$01$C0$80$41$01$C0$80$41$00$C1$81,
 $40$00$C1$81$40$01$C0$80$41$00$C1$81$40$01$C0,
 $80$41$01$C0$80$41$00$C1$81$40$00$C1$81$40$01,
 $C0$80$41$01$C0$80$41$00$C1$81$40$01$C0$80$41,
 $00$C1$81$40$00$C1$81$40$01$C0$80$41$00$C1$81,
 $40$01$C0$80$41$01$C0$80$41$00$C1$81$40$01$C0,
 $80$41$00$C1$81$40$00$C1$81$40$01$C0$80$41$01,
 $C0$80$41$00$C1$81$40$00$C1$81$40$01$C0$80$41,
 $00$C1$81$40$01$C0$80$41$01$C0$80$41$00$C1$81,
 $40
 ) ;
 
// Table of CRC values for low–order byte */
 auchCRCLo: array [Byte] of Word =(
 $00$C0$C1$01$C3$03$02$C2$C6$06$07$C7$05$C5$C4,
 $04$CC$0C$0D$CD$0F$CF$CE$0E$0A$CA$CB$0B$C9$09,
 $08$C8$D8$18$19$D9$1B$DB$DA$1A$1E$DE$DF$1F$DD,
 $1D$1C$DC$14$D4$D5$15$D7$17$16$D6$D2$12$13$D3,
 $11$D1$D0$10$F0$30$31$F1$33$F3$F2$32$36$F6$F7,
 $37$F5$35$34$F4$3C$FC$FD$3D$FF$3F$3E$FE$FA$3A,
 $3B$FB$39$F9$F8$38$28$E8$E9$29$EB$2B$2A$EA$EE,
 $2E$2F$EF$2D$ED$EC$2C$E4$24$25$E5$27$E7$E6$26,
 $22$E2$E3$23$E1$21$20$E0$A0$60$61$A1$63$A3$A2,
 $62$66$A6$A7$67$A5$65$64$A4$6C$AC$AD$6D$AF$6F,
 $6E$AE$AA$6A$6B$AB$69$A9$A8$68$78$B8$B9$79$BB,
 $7B$7A$BA$BE$7E$7F$BF$7D$BD$BC$7C$B4$74$75$B5,
 $77$B7$B6$76$72$B2$B3$73$B1$71$70$B0$50$90$91,
 $51$93$53$52$92$96$56$57$97$55$95$94$54$9C$5C,
 $5D$9D$5F$9F$9E$5E$5A$9A$9B$5B$99$59$58$98$88,
 $48$49$89$4B$8B$8A$4A$4E$8E$8F$4F$8D$4D$4C$8C,
 $44$84$85$45$87$47$46$86$82$42$43$83$41$81$80,
 $40
 );

 var

 leistung1,leistung2,kaskade: integer;



function ModbusCRC16(const s: String): Word;
 var
     uchCRCHi: Byte;
     uchCRCLo: Byte;
     uIndex: Byte;
     I: Integer;
 begin
     uchCRCHi := $FF;
     uchCRCLo := $FF;
     for I := 1 to Length(s) do
     begin
         uIndex := uchCRCLo xor ord(s[i]);
         uchCRCLo := uchCRCHi xor auchCRCHi[uIndex];
         uchCRCHi := auchCRCLo[uIndex];
     end;
     Result:= uchCRCHi or uchCRCLo shl 8;
 end;

procedure TForm1.write_typ_c_btnClick(Sender: TObject);
var
sendstr, crc:string;
begin
sendstr:= ('01'+'10'+'05'+Inttohex(kaskade,2)+Inttohex(leistung1,4)+Inttohex(leistung2,4)) ;
serialp1.SendString(hextostr(sendstr));
memo1.Text:=sendstr;
//crc:= hextostr(inttohex(modbusCRC16(hexstrtostr(sendstring)),2));
memo1.Lines.Add(crc);

serialp1.SendString(inttohex(modbusCRC16(sendstr),4));


Der erste Teil funktioniert, es sollen die direkten Hexweerte gesendet werden, das wird hier gemacht:
ausblenden Delphi-Quelltext
1:
2:
sendstr:= ('01'+'10'+'05'+Inttohex(kaskade,2)+Inttohex(leistung1,4)+Inttohex(leistung2,4)) ;
serialp1.SendString(hextostr(sendstr));

und läuft ich bekomme auf der Schnittstelle 01 10 05 00 C3 50 00 00 gesendet.

JEtzt muss noch der CRC bestimmt werden:
ausblenden Delphi-Quelltext
1:
serialp1.SendString(inttohex(modbusCRC16(sendstr),4));					

Jetzt kommt das Problem mit der Typumwandlung:
Als Vergleich lasse ich den Wert auf folgender Seite berechnen:
www.lammertbies.nl/c...crc-calculation.html
Wenn ich dort 01100500C3500000 eingebe C91F raus(er stellt erst Low und dann Highbyte dar)
Dies macht auch meine Routine, sie rechnet also prinzipiell richtig.
ABER: auf der seite muss "input type: HEX" angeklickt werden, damit ich den zugehörigen CRC bekomme da es ja Hexwerte sind.
Und dann kommt ein anderes Ergebnis raus. Aber dieses brauch ich eigentlich.
Also meine routine greift die Werte als ASCII auf und nicht als Hex, das ist falsch.
Hab schon wild umgewandelt aber aber kommt nie das raus was ich brache.
Kann mir da jemand helfen ?
Ich weis nichtmehr weiter.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Di 09.07.13 11:18 
Ich bin nicht sicher, ob ich Dich richtig verstanden habe, aber wie ist es so?
ausblenden Delphi-Quelltext
1:
uIndex := uchCRCLo xor IntToStr('$' + s[i]);					
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Di 09.07.13 12:04 
Ich weiß, was das Problem ist. Der TE hat den Unterschied zwischen Bytes und einer Zeichenkette noch nicht realisiert.

Nehmen wir die Bytefolge "01 10 05 00 C3 50 00 00". Das sind zunächst einmal lauter Bytes, also $01, $10, $05, $00, $C3, $50, $00 und $00. Durch die Umwandlung in eine Zeichenkette (s:='01 10 05 00 C3 50 00 00') sind das aber keine Bytes mehr. Das erste Byte $01 wird nun in eine zweistellige Zeichenkette, bestehend aus den Zeichen "0" und "1" umgewandelt. Es ist nun kein Byte mehr.

Ergo wird eine Iteration über die einzelnen Zeichen der Zeichenkette nicht mehr die ehemaligen Bytes erwischen, sondern die einzelnen Zeichen der Zeichenkette. Ergo: Ord(s[1]) holt sich das Zeichen "0", und ermittelt dessen ASCII-Code, in diesem Falle $30. Dann kommt das nächste Zeichen "1", dessen ASCII-Code $31 ist.

Deine CRC-Berechnung läuft also nicht über $01, $10, $05 und so weiter. Sie läuft über $30, $31, $20 (Leerzeichen) $30, $35 usw.

Um das ganze korrekt zu machen, würde ich die zu sendenden Bytes als allererstes in ein Array of Byte stopfen. Über dieses Array (!) würde ich dann die CRC-Checksumme bilden und an das Array anhängen. Anschließend die Bytes des Arrays in eine Zeichenkette wandeln und rüber über die SIO damit.

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Di 09.07.13 12:09 
Du must halt Deinen String auch nach Hex wandeln (bzw. je nach Quelle nicht nach String wandeln). Also in Deinem Beispiel
ausblenden Quelltext
1:
2:
01100500C3500000 ->  $01, $10, $05, $00, $C3, $50, $00, $00 
-> ansichar($01) + ansichar($10) ansichar($05) + ansichar($00) + ansichar($C3) + ansichar($50) + ansichar($00)+ ansichar($00)

Im übrigen ist es eine Unart, CRC- und Hash-Routinen mit Strings zu füttern, denn diese Algorithmen arbeiten auf Bit/Byte-Basis. Deshalb ist es auch wichtig (wenn es denn sein muß, Zeichen zu benutzen), mit ansichar/string an Stelle von char/string zu arbeiten, um möglich Unicodeproblemen vorzubeugen.
daywalker0086 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243


Delphi 2005 Architect
BeitragVerfasst: Di 09.07.13 12:47 
So kurze Zwischenfrage:
habe in Array of Byte erstellt und wollte diesem jetzt zum testen feste Werte zuweisen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.write_typ_c_btnClick(Sender: TObject);
var
sendstr, crc:string;
daten: array[0..7of Byte;
begin
sendstr:= ('01'+'10'+'05'+Inttohex(kaskade,2)+Inttohex(leistung1,4)+Inttohex(leistung2,4)) ;
serialp1.SendString(hextostr(sendstr));
memo1.Text:=sendstr;
//crc:= hextostr(inttohex(modbusCRC16(hexstrtostr(sendstring)),2));

daten[0]:=01;
daten[1]:=10;
daten[2]:=05;
daten[3]:=00;
daten[4]:=195//C3
daten[5]:=50;
daten[6]:=00;
daten[7]:=00;

mach ich das jetzt so richtig? Wenn ja muss ich wohl noch die CRC Routine umstricken damit die Byte frisst oder?
Habe diese nur kopiert weil ich mir dachte warum das Rad neu erfinden und augenscheinlich funktionierte sie ja erstmal.
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Di 09.07.13 13:06 
user profile icondaywalker0086 hat folgendes geschrieben Zum zitierten Posting springen:

mach ich das jetzt so richtig? Wenn ja muss ich wohl noch die CRC Routine umstricken damit die Byte frisst oder?
Habe diese nur kopiert weil ich mir dachte warum das Rad neu erfinden und augenscheinlich funktionierte sie ja erstmal.
Nein, jetzt verwechselst Du teilweise (bis auf die 193) hex und dec, am besten die Hex-Werte (wie schon oben beschrieben) mit $ zuweisen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
daten[0]:=$01;
daten[1]:=$10;
daten[2]:=$05;
daten[3]:=$00;
daten[4]:=$C3;
daten[5]:=$50;
daten[6]:=$00;
daten[7]:=$00;

Hier mal ein halb-automatischer Code aus meinen CRC/Hashroutinen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
{---------------------------------------------------------------------------}
function CRC16_MODBUS(data: pointer; len: word): word;
const
  rpoly = $A001{Reflected $8005}
type
  tbyteptr = ^byte;
var
  j: integer;
  crc: word;
  ptr: tbyteptr;
begin
  crc := $FFFF;
  ptr := tbyteptr(data);
  while len>0 do begin
    crc := crc xor ptr^;
    for j:=1 to 8 do begin
      if odd(crc) then  crc := (crc shr 1xor rpoly
      else crc := crc shr 1;
    end;
    inc(ptr);
    dec(len);
  end;
  CRC16_MODBUS := crc;
end;

const
  tv: array[0..7of byte = ($01$10$05$00$C3$50$00$00);
begin
  writeln('CRC16_MODBUS Test daywalker0086:', CRC16_MODBUS(@tv,sizeof(tv))=$6ac);
end.

Die Ausgabe ist (via True/False, weil ich IntToHex nicht einbinden wollte):
ausblenden Quelltext
1:
CRC16_MODBUS Test daywalker0086:TRUE					

Oder mit array of byte:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
{---------------------------------------------------------------------------}
function CRC16_MODBUS(const b: array of byte): word;
const
  rpoly = $A001{Reflected $8005}
var
  i,j: integer;
  crc: word;
begin
  crc := $FFFF;
  for i:=low(b) to high(b) do begin
    crc := crc xor b[i];
    for j:=1 to 8 do begin
      if odd(crc) then  crc := (crc shr 1xor rpoly
      else crc := crc shr 1;
    end;
  end;
  CRC16_MODBUS := crc;
end;

const
  tv: array[0..7of byte = ($01$10$05$00$C3$50$00$00);
begin
  writeln('CRC16_MODBUS Test daywalker0086:', CRC16_MODBUS(tv)=$6ac);
end.

Edit: Ein paar Korrekturen :wink:
daywalker0086 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243


Delphi 2005 Architect
BeitragVerfasst: Di 09.07.13 13:56 
Danke für die Hilfe!
Hab das mit deiner Routine jetzt so gelöst:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
daten[0]:=$01;
daten[1]:=$10;
daten[2]:=$05;
daten[3]:=$00;
daten[4]:=$C3//C3
daten[5]:=$50;
daten[6]:=$00;
daten[7]:=$00;
daten[8]:=Lo(CRC16_MODBUS(@daten,8));
daten[9]:=Hi(CRC16_MODBUS(@daten,8));
for i:=0 to 9 do
begin
  sendstr:=sendstr+inttohex(daten[i],2);
end;
serialp1.SendString(hextostr(sendstr));


Ist zwar auch wieder ein wenig hextostr und inttohex aber funktioniert bis jetzt tadellos. oder kann man es noch stark verbessern?
Jetzt muss ich nur noch sehen das ich die festen Werte richtig mit meinen Eingaben ersetze...
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Di 09.07.13 14:08 
user profile icondaywalker0086 hat folgendes geschrieben Zum zitierten Posting springen:
Danke für die Hilfe!
Hab das mit deiner Routine jetzt so gelöst:
ausblenden Delphi-Quelltext
1:
2:
3:
//..
daten[8]:=Lo(CRC16_MODBUS(@daten,8));
daten[9]:=Hi(CRC16_MODBUS(@daten,8));

Ist zwar auch wieder ein wenig hextostr und inttohex aber funktioniert bis jetzt tadellos. oder kann man es noch stark verbessern?
Etwas kann man noch ein wenig verbessern: Du berechnest den CRC-Wert zweimal. Bei so wenigen Daten ist das nur unschön, bei Megabytes geht's auch in die Zeit: einfach eine Word-Variable zum Zwischenspeichern nehmen.