"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!
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)
> 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.
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?
Matthias S. schrieb:> Und was ist besser, sizeof(buff) oder strlen(buff) zur bestimmung der> Länger der in buff befindlichen Zeichen zu ermitteln?
Das kommt daraf an(tm).
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.
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.
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?
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
charvariable[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.
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
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".
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.
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
@-ALL,
vielen Dank der Aufklärung, nun kann ich weiter machen :)!!!
{PS. [ von Stefan ⛄ F. (stefanus) ]
Danke für die Hilfe, würd ja gern Dir mal nen Pot Kaffee ausgeben, bin
morgen in Berlin - Spandau :) }
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?
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.
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 :)
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 :)
Matthias S. schrieb:> könntest Du etwas mir davon näherbringen?
Das musst du in der Doku der CRC Klasse nachlesen.
> ein schöner Schwalbenschwanz
Den habe ich coloriert weil er beim Trocknen ganz grau wurde. Da ist
ewig her, da war ich vielleicht 15.
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_tfoo;
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?
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_tfoo=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.
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!
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:
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.
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:
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.
Niklas Gürtler schrieb:> Hm? Der Compiler allokiert sowieso ein großes Stack Frame für alle> lokalen Variablen beim Funktionseintritt, egal wo sie definiert sind.
Ähm, NEIN.
Den gebe ich in meinen LinkerScripts an.
ABER das ist hier gar nicht das Thema!
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.
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.