crc-Berechnung Codezeile Erklärung

OP #6939311
Lesenswert?

Liebes Forum

ich habe hier eine Codezeilen die ich nicht ganz verstehe und würde mich 
über eine kurze Erklärung freuen:
1
#include "CRC16.h"
2
#include "CRC.h"
3
CRC16 crc;
4
void test1()
5
{
6
  static char buff[23];
7
  snprintf(buff, sizeof(buff),"[%s/%s/%d,%d,%d,]","A","Index",strlen(buff),buttonstate_index[0],buttonstate_index[1]);
8
  Serial.print("strlen(buff)= ");
9
  Serial.println(strlen(buff));
10
  Serial.print("buff=  ");          
11
  Serial.println(buff);                                                                                         // Daten senden  
12
  Serial.println("" );
13
  Serial.print("crc16=  ");  
14
  Serial.println(crc16((uint8_t *) buff, sizeof(buff), 0x1021, 0, 0, false, false), HEX);

"Serial.println(crc16((uint8_t *) buff, sizeof(buff), 0x1021, 0, 0, 
false, false), HEX);"

Um diese Zeile geht es. Wie bekomme ich das Ergebnis bzw. die Hex-Zahl 
in eine Variable, die ich dann weiter verarbeiten kann
? Und welche Größe müsste diese haben?

Vielen Dank für die Hilfe!
#6939316
Lesenswert?

Du brauchst eine 16Bit Variable.
uint16_t oder unsigned short.

Und dann nimmst du die crc16() Funktion einfach aus der println 
heraus...

Vorher:
1
Serial.println(crc16((uint8_t *) buff, sizeof(buff), 0x1021, 0, 0, false, false), HEX);

Nachher:
1
unsigned short my_crc16;
2

3
my_crc16 = crc16((uint8_t *) buff, sizeof(buff), 0x1021, 0, 0, false, false);
4
Serial.println(my_crc16, HEX);
Gast #6939321
Lesenswert?

Adam P. schrieb:
> Wie kommst du auf uint8_t?

Das ist falsch. Das Ergebnis muss natürlich eine 16 Bit Variable sein.

Ich möchte noch ergänzen: Um eine Integer Variable in einen 
Hexadezimalen String umzuwandeln, kann man sprintf() benutzen.
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#ga6017094d9fd800fa02600d35399f2a2a
1
char puffer[10];
2
sprintf(puffer, "%04X", variable);

0: die Zahl soll ggf mit Nullen beginnen
4: ist die Anzahl der gewünschten Hex-Ziffern
X: Hexadezimal mit großen Buchstaben (alternativ geht auch x für kleine 
Buchstaben)
OP #6939342
Lesenswert?

> Stefan ⛄ F. schrieb:
> Adam P. schrieb:
Super! VIELEN DANK.
Ich habe mir es gestern schon so gedacht, war mir leider nicht sicher, 
da es zu keinem vernüftigen Ergenis kam in meinem Code kam.
1
Serial.println(crc16((uint8_t *) buff, sizeof(buff), 0x1021, 0, 0, false, false), HEX);

Bedeutet also nur, dass "(uint8_t *) buff" uint8_t* ein Zeiger auf den 
Wert ist, der in buff steht?
Und was ist besser, sizeof(buff) oder strlen(buff) zur bestimmung der 
Länger der in buff befindlichen Zeichen zu ermitteln?
Gast #6939355
Lesenswert?

Matthias S. schrieb:
> Bedeutet also nur, dass "(uint8_t *) buff" uint8_t* ein Zeiger auf den
> Wert ist, der in buff steht?

Fast.

Es bedeutet, dass der Zeiger auf einen (oder mehrere) uint8 zeigt. Man 
braucht das, wenn der Zeiger in Wirklichkeit auf etwas anderes zeigt, 
als was die Funktion haben will.

Letztendlich zeigen alle Zeiger auf Zellen im RAM*, das man als eine 
Folge von Bytes verwenden kann.

*) Ja ich weiß, es gibt Ausnahmen.
OP #6939358
Lesenswert?

Jim M. schrieb:
> Nämlich ob die Eingabe ein String variabler Länge ist - und die
> Nullbytes nicht mitzählen - oder ein fester Puffer, wo auch Null Bytes
> wichtig sein könn(t)en.

Was würde das heißen? Wie kann ich dieses Nullbyte erkennen? :)
Nein schon klar, aber was geht nun bei
1. variabler Länge
2. fester Länge

Ich habe auch ein Verständnisproblem mit dem Nullbyte, bei einer 
seriellen Datenübertragung wird am Ende immer ein Nullbyte an den 
Datenstring mit angehängt? Und wenn ja, wie kann ich dies z.Bsp. in 
einem Terminalprogramm mir anzeigen lassen?
Wie ist es mit dem Nullbyte, wenn ich seriell Daten in einen Puffer 
einlese, muss ich ja die Puffergröße immer +1 festlegen. Wenn ich nun 
die Puffergröße ermitteln möchte, wie geh ich dann vor?
Gast #6939365
Lesenswert?

Matthias S. schrieb:
> was geht nun bei
> 1. variabler Länge
> 2. fester Länge

1: strlen(), welches die Anzahl der belegten Bytes zählt, wobei die 
abschließende 0 nicht mitgezählt wird
2: sizeof(), welches die Anzahl der Bytes von Argument schon beim 
Compilieren weis.
1
char variable[100]="Hallo";

strlen(variable) ist 5, weil der String 5 Zeichen lang ist. Tatsächlich 
sind 6 Zeichen belegt, wegen der abschließenden Null.

sizeof(variable) ist 10, weil das Array 10 bytes groß ist. Der Inhalt 
spielt keine Rolle.

Wenn du ein Array mit variablem Inhalt hast, der nicht mit einer 0 
terminiert ist, dann geht keins von beiden. Dann must du selbst wissen, 
wie groß der Inhalt ist, also eine eigene Variable dafür benutzen.
Gast #6939371
Lesenswert?

Jim M. schrieb:
> Nämlich ob die Eingabe ein String variabler Länge ist - und die
> Nullbytes nicht mitzählen - oder ein fester Puffer, wo auch Null Bytes
> wichtig sein könn(t)en.

Und ob buff ein Array oder ein Pointer ist (wozu Arrays speziell bei der 
Übergabe als Funktionsparameter gern degenerieren) und welchen Datentyp 
buff hat.

Bei char wird dank sizeof(char) = 1 (per Definition) das zweite Problem 
wohl nur auf eher exotischen Plattformen auftreten, auf denen char mehr 
als 8 Bits hat (z.B. ist bei 16-Bit-char sizeof(buff) vermutlich halb so 
groß wie die Anzahl der uint8_t, die die Funktion verarbeiten soll) und 
dann ist fraglich, ob eine solche Plattform überhaupt uint8_t kennt. Bei 
den diversen "wide character"-Datentypen wird das auch auf gängigen 
Plattformen potentiell lustig.

MfG, Arno
Gast #6939373
Lesenswert?

Matthias S. schrieb:
> Ich habe auch ein Verständnisproblem mit dem Nullbyte, bei einer
> seriellen Datenübertragung wird am Ende immer ein Nullbyte an den
> Datenstring mit angehängt?

Das kommt ganz drauf an, wie du die Kommunikation programmierst. Die 
Serielle Schnittstelle überträgt bytes, sie weiss nicht was ein String 
ist und die konkreten Werte sind ihr egal.

Üblicherweise terminiert man die Zeilen bei Klartext-Schnittstellen mit 
einem Zeilenumbruch im Unix Format (\n), traditionell ist auch das alte 
DOS Format \r\n noch relativ weit verbreitet.

> Und wenn ja, wie kann ich dies z.Bsp. in einem Terminalprogramm
> mir anzeigen lassen?

Kommt auf dein Programm an. Manche Zeigen Null-Zeichen gar nicht an. 
hammer Terminal zeigt sie als Kästchen mit Nullen drin an.

> Wie ist es mit dem Nullbyte, wenn ich seriell Daten in einen Puffer
> einlese, muss ich ja die Puffergröße immer +1 festlegen.

Ja

> Wenn ich nun die Puffergröße ermitteln möchte, wie geh ich dann vor?

Die Größe des Puffers ist eine Sache, wie viel davon belegt wurde ist 
eine andere. Siehe oben mein Beispiel mit dem "Hallo".
Gast #6939382
Lesenswert?

Niklas G. schrieb:
> Arno schrieb:
>> und
>> dann ist fraglich, ob eine solche Plattform überhaupt uint8_t kennt
>
> Den kann sie nicht kennen, denn was wäre dann sizeof(uint8_t)? sizeof
> arbeitet in Vielfachen von char, d.h. es kann keinen kleineren (in
> Sachen Speicherverbrauch) Datentyp als char geben.

Klingt logisch, danke für die Ergänzung. Dann hat mich mein Gefühl nicht 
getrogen :)

MfG, Arno
OP #6939607
Lesenswert?

Dann hab ich noch eine Frage:
wenn ich es richtig sehe, wird bei der crc Berechnung in der Funktion 
mit String/Char-Array gearbeitet (berichtigt mich bitte wenn ich es 
falsch interpretiere)
[c]
  crc.setPolynome(0x1021);
  crc.add((uint8_t*)str, 9);
  Serial.println(crc.getCRC(), HEX);
[\c]
ich aber z.Bsp. 2 Byte habe, wie kann ich von diesen den crc berechnen?
Muss ich erst die Byte zu char machen?
Gast #6939608
Lesenswert?

Matthias S. schrieb:
> bin morgen in Berlin - Spandau

Uh, das ist sehr weit weg von mir. Trotzdem danke für das Lob. Ich habe 
aber auch einen Rüffel für die beiden Flüchtigkeitsfehler 8 versus 16 
und 10 versus 100 verdient. C ist zwar nur mein Hobby, trotzdem sollte 
das nach so vielen Jahren nicht gleich doppelt in einem Thread 
passieren.
OP #6939618
Lesenswert?

Stefan ⛄ F. schrieb:
> Matthias S. schrieb:
>> bin morgen in Berlin - Spandau
>
> Uh, das ist sehr weit weg von mir. Trotzdem danke für das Lob. Ich habe
> aber auch einen Rüffel für die beiden Flüchtigkeitsfehler 8 versus 16
> und 10 versus 100 verdient. C ist zwar nur mein Hobby, trotzdem sollte
> das nach so vielen Jahren nicht gleich doppelt in einem Thread
> passieren.

Das ist nicht schlimm, zum Glück bin ich nicht soo blutfrischer Anfänger 
und wusste schon, wie es sein sollte.

> Uh, das ist sehr weit weg von mir.

Ohh da hab ich mich wohl vermacht, wenn ich mich nicht vermache gibt es 
auch einen fleißigen Helfer Stefan aus Berlin...

ok, dann bist Du aus Düsseldorf?! doch ein bissle weiter weg :)
OP #6939649
Lesenswert?

Matthias S. schrieb:
> Dann hab ich noch eine Frage:
> wenn ich es richtig sehe, wird bei der crc Berechnung in der Funktion
> mit String/Char-Array gearbeitet (berichtigt mich bitte wenn ich es
> falsch interpretiere)
> [c]
>   crc.setPolynome(0x1021);
>   crc.add((uint8_t*)str, 9);
>   Serial.println(crc.getCRC(), HEX);
> [\c]
> ich aber z.Bsp. 2 Byte habe, wie kann ich von diesen den crc berechnen?
> Muss ich erst die Byte zu char machen?

@ Stefan,

könntest Du etwas mir davon näherbringen?
Übrigens... ein schöner Schwalbenschwanz, hab ich nur einmal in meinem 
Leben in freier Natur gesehen :)
#6939985
Lesenswert?

Stefan ⛄ F. schrieb:
> Ich habe
> aber auch einen Rüffel für die beiden Flüchtigkeitsfehler 8 versus 16
> und 10 versus 100 verdient.

Ach was
...tippfehler, schneller getippt, als gedacht ;-) passiert jedem mal.

Matthias S. schrieb:
> Das ist nicht schlimm, zum Glück bin ich nicht soo blutfrischer Anfänger
> und wusste schon, wie es sein sollte.

Dann hattest du bestimmt einfach nur einen dieser "Den Wald vor lauter 
Bäumen nicht sehen" Momenten :) das passiert auch jedem mal.

Funktionsaufruf innerhalb der Parameterliste.
1
Serial.println( crc16(...), HEX );

Also ich schreibe so etwas sehr ungerne.

Ich bevorzuge eher die Schreibweise mit extra Variablen:
1
uint16_t foo;
2

3
foo = get_something(...);
4
print(foo, HEX);

Anstatt:
1
print(get_somoeting(...), HEX);

Da es sehr zum verlesen leitet und irgendwie einfach zu viel auf einmal 
ist (in einer Zeile).

Da gibt es noch viel schlimmere Bsp.

Evtl. ist es Geschmackssache?
Persönliche Seite #6940052
Lesenswert?

Adam P. schrieb:
> uint16_t foo;
> foo = get_something(...);

Genau das würde ich vermeiden, weil: foo ist hier zwischenzeitlich 
uninitialisiert. Wenn zwischen Definition und erster Zuweisung viel Code 
ist, kann es schnell mal passieren dass man zwischendurch versehentlich 
"foo" ausliest und dann undefiniertes passiert. Variablen sollte man 
immer erst dann definieren, wenn man einen Wert für sie hat, und sie 
direkt damit initialisieren:
1
uint16_t foo = get_something (...);

Das bedeutet insbesondere auch, dass man die Variable im 
innerst-möglichen Scope definiert, also z.B. in die innerste Schleife 
o.ä. So kann der Wert nicht versehentlich später noch genutzt werden, 
wenn er gar nicht mehr gültig ist o.ä.

So vermeidet man Fehler, und kürzer ist es auch. Seit C99 muss man 
Variablen auch nicht mehr alle am Anfang der Funktion definieren, und 
das sollte man nutzen.
OP #6940082
Lesenswert?

Adam P. schrieb:
> Evtl. ist es Geschmackssache?

Niklas G. schrieb:
> Genau das würde ich vermeiden, weil:

Ich als Anfänger kann beide Seiten verstehen!
Darum ist es für Anfänger auch schwierig, sich in bestehende Beispiele 
einzuarbeiten, da die Schreibweise manigfaltig sein kann und dennoch zum 
Gleichen führt.
Das hat mich an c++ so abgeschreckt, zu verstehen, was alles in einer 
Zeile bedeutet und wie es "auseinander" zu nehmen ist. Auch wenn wieder 
viele stöhnen, da geht es bei Bascom einfacher, übersichtlicher daher.
Langsam steige ich jedoch weiter in die Materie ein und sehe jedoch so 
einige Vorteile, die mir teilweise bei Bascom verwehrt blieben.

Ich find's jedoch gut, dass ich hier Hilfe bekomme. Habe auch "Der C++ 
PROGRAMMIERER" aber darin werden solche Beispiele nicht gleich sichtbar 
dargelegt.

Nun kämpfe ich ersteinmal mit Strings und Arrays und deren 
Möglichkeiten... Wenn jemand noch ein übersichtliches Deutsch-Tutorial 
hat...

Danke!
#6940346
Lesenswert?

Niklas G. schrieb:
> Adam P. schrieb:
>> uint16_t foo;
>> foo = get_something(...);
>
> Genau das würde ich vermeiden, weil: foo ist hier zwischenzeitlich
> uninitialisiert. Wenn zwischen Definition und erster Zuweisung viel Code
> ist, kann es schnell mal passieren dass man zwischendurch versehentlich
> "foo" ausliest und dann undefiniertes passiert.

Also möchtest du uns mitteilen, dass du in deinem Source, den Überblick 
über lokale und globale Variablen verlierst?

Also eine Funktion sollte nicht 1000 Zeilen lang sein, so das sie auch 
auf den Bildschirm passt.
Bei meiner Variante sieht man doch oben die Definitionen der Variablen 
die in dieser Funktion verwendet werden?!
Das muss man doch im Blick haben - das sind alles PUSH und POP Befehle.

Und wenn ne Funktion zu lang wird, dass man die Variablen aus den Augen 
verliert - dann ist sie einfach zu lang.

Stell dir vor hast ne Funktion die 3 Parameter erwartet,
willst du dann etwa sowas schreiben:
1
// Lieber nicht
2
draw("Hallo", get_element_pos("TITEL"), get_head_color(), get_head_font());
3

4
// schon eher
5
pos_t xy;
6
uint32_t rgb;
7
font head_font;
8

9
xy = get_element_pos("TITEL");
10
rgb = get_head_color();
11
head_font = get_head_font();
12

13
draw("Hallo", xy, rgb, head_font);

Na ich weis ja nicht ob das so toll ist, wenn du das dann noch auf 
andere Dinge beziehst, wo der Funktionsaufruf in Augenkrebs 
endet...siehe Win-API.

Aber wie gesagt, jeder wie er mag!

> Wenn zwischen Definition und erster Zuweisung viel Code
> ist, kann es schnell mal passieren dass man zwischendurch versehentlich
> "foo" ausliest und dann undefiniertes passiert.
Ich verstehe deinen Einwand, aber das würde man nur machen, wenn es 
keinen Ausweg mehr gibt, weil die SW oder FW so verpfuscht wurde...ich 
kenn es.
Gast #6940355
Lesenswert?

Adam P. schrieb:
> Also möchtest du uns mitteilen, dass du in deinem Source, den Überblick
> über lokale und globale Variablen verlierst?

Es ging ausschließlich um lokale Variablen. Globale Variablen sollte man 
sowieso auch nicht nutzen.

Adam P. schrieb:
> Also eine Funktion sollte nicht 1000 Zeilen lang sein, so das sie auch
> auf den Bildschirm passt.

Sollte nicht, passiert aber schon mal.

Adam P. schrieb:
> Bei meiner Variante sieht man doch oben die Definitionen der Variablen
> die in dieser Funktion verwendet werden?!

Trotzdem ist es schwerer lesbar und fehleranfällig.

Adam P. schrieb:
> Das muss man doch im Blick haben - das sind alles PUSH und POP Befehle.

Hm? Der Compiler allokiert sowieso ein großes Stack Frame für alle 
lokalen Variablen beim Funktionseintritt, egal wo sie definiert sind.

Adam P. schrieb:
> willst du dann etwa sowas schreiben

Natürlich, ist schön kompakt und lesbar.

Adam P. schrieb:
> // schon eher

So schon gar nicht. Uninitialisierte Variablen, die man versehentlich 
verwenden kann. So wäre es auch ok:
1
pos_t xy = get_element_pos("TITEL");
2
uint32_t rgb = get_head_color();
3
font head_font = get_head_font(); draw("Hallo", xy, rgb, head_font);
In manchen Programmiersprachen (z.B. Kotlin, Rust, Scala) geht es 
überhaupt auch nur so, weil dort diese große Fehlerquelle, Variablen 
uninitialisiert zu lassen, gleich ganz verboten wird. In C++ versucht 
man mittels Konstruktoren auch dafür zu sorgen, dass immer alles sofort 
initialisiert wird.

Adam P. schrieb:
> Na ich weis ja nicht ob das so toll ist, wenn du das dann noch auf
> andere Dinge beziehst

Auch da kann man Variablen direkt initialisieren, wenn man sie nicht 
gerade per Out-Parameter initialisiert.

Adam P. schrieb:
> Ich verstehe deinen Einwand, aber das würde man nur machen, wenn es
> keinen Ausweg mehr gibt,

Man verändert Code ständig, und da können schnell Fehler passieren. 
Daher ist es etablierte Regel, eben diese Fehlerquelle zu vermeiden. In 
den meisten Programmiersprachen ist das auch absoluter Standard (oder 
eben Pflicht, s.o.), nur in C ist diese Trennung von Definition und 
Initialisierung noch so verbreitet, weil es eben bis C99 nicht anders 
ging.
#6940363
Lesenswert?

Niklas Gürtler schrieb:
> So schon gar nicht. Uninitialisierte Variablen, die man versehentlich
> verwenden kann. So wäre es auch ok:
> pos_t xy = get_element_pos("TITEL");
> uint32_t rgb = get_head_color();
> font head_font = get_head_font(); draw("Hallo", xy, rgb, head_font);

Sorry, abar sowas ist bei mir ein NO-GO.

Deshalb benennt man ja Variablen auch demenstprechend.
Damit sie im Kontext zu erkennen sind...
Warum sollte ich z.b. "rgb" abfragen, wenn ich "rgb" noch nix
zugewiesen habe? Ergibt keinen Sinn.
Gast #6940470
Lesenswert?

Adam P. schrieb:
> Ähm, NEIN.
> Den gebe ich in meinen LinkerScripts an.

Bei welchem Compiler gibt man Stack-Frames im Linker-Script an? Alle mir 
bekannten Compiler summieren die Größen aller lokalen Variablen einer 
Funktion auf, und allokieren bei Funktionseintritt die entsprechende 
Menge auf dem Stack, z.B. mit einer SUB-Instruktion. Daher ist es für 
die Effizienz völlig unerheblich, wo in der Funktion die Variable 
definiert wird.

Adam P. schrieb:
> Sorry, abar sowas ist bei mir ein NO-GO.

Ist aber absolut üblich. Kompakter Code ist auch gut für die Lesbarkeit, 
denn schließlich gibt es einfach weniger zu überblicken. Natürlich 
sollte man es nicht übertreiben, wenn die Zeile zu lang wird kann man 
mit temporären Variablen anfangen, wie gezeigt.

Adam P. schrieb:
> Warum sollte ich z.b. "rgb" abfragen, wenn ich "rgb" noch nix
> zugewiesen habe? Ergibt keinen Sinn.

Weil Irren menschlich ist, und man bei nachträglicher Bearbeitung des 
Codes versehentlich die Variable zu früh auslesen könnte. 
Fehlervermeidung ist sehr wichtig.

Außerdem wird Code eben viel häufiger gelesen als geschrieben, und daher 
ist es wichtig, dass sofort zu erfassen ist, was da passiert. Wenn am 
Anfang der Funktion erstmal eine Liste an Variablen folgt, bringt das 
gar nichts - ich weiß nicht, was da rein geschrieben wird, wann sie 
gültig ist, wann nicht mehr... Wenn eine Variable z.B. nur in einer 
inneren Schleife benutzt wird, sollte man sie auch nur dort definieren 
und direkt initialisieren. Dann ist sofort ersichtlich, dass sie nur 
dort gültig ist, und man kann sie nicht versehentlich zu früh oder zu 
spät verwenden. Für die Effizienz macht das wie gesagt keinen 
Unterschied. Wie gesagt ist es in einigen Programmiersprachen gar nicht 
anders möglich oder nur als Ausnahme, weil schlaue Leute erkannt haben 
dass es so sicherer ist.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren