Hallo Freunde der "schwarzen Magie"
wo ich gerade dabei bin ein paar Daten zwischen den Systemen hin und her
zu schicken ist mir etwas aufgefallen.
Meine bisherige Methode zur Datenverschiftung ist etwas ineffizient.
da wird eine einfache float-Variable, die doch mit 4 Byte auskommt mit
speicherfressenden Befehlen, wie sprintf in -1.23456 in ganze 8 Byte
aufgebauscht um dann zeichenweise ins UDR - Register verschoben zu
werden, wo sie auf der anderen Seite Zeichen für Zeichen in einem String
landet um mit hanebüchenem Aufwand oder atoi wieder in ihre 4 Byte Größe
reduziert zu werden.
Aber wie macht man es besser?
Wie kann man das Bytemuster einer Variablen (int float ...) in einen
String verwandeln den man via UART, CAN oder weiss der Teufel was
verschicken kann und am Ende der Nahrungskette wieder zurück in eine
float, int ... ??
Wozu denn als String? Ich übertrage immer ganze Variablen und Strukte.
Einfach einen pointer auf die variable setzen und dann mit dem
sizeof-Operator die Größe in Bytes bestimmen, die übertragen werden
muss. Dann halt (z.B. für einen Float-wert) 4 Bytes übertragen. Auf der
anderen Seite genau andersrum. Da muss man natürlich immer genau wissen,
was man überträgt. Aber das weiß man in aller regel ja.
Auch mit kompletten Strukten, die Variablen und Arrays enthalten klappt
das bestens.
Ich lass den PC mit float rechnen. Meine Devices brauchen keinen Float.
Da werden die Werte als integer ausgetauscht, und die Anzeige wird aufm
PC nach float umgerechnet. Dabei spar ich auch gleich noch die Float
library aufm Deveice.
Dein Float belegt vier Bytes. Soweit so gut.
Nun sende doch einfach diese vier Byte einzeln und setze sie
auf der Gegenseite wieder zusammen.
Der Haken ist, dass du noch ein Protokoll brauchst, das dem Empfänger
sagt, wann gehts los und wo ist Ende.
Die Strings hatten da den Charme, dass sie eine Null am Ende haben
oder evtl. mit <CR> abgeschlossen sind.
So weit klar?
Du könntest also so etwas wie:
<DLE> <STX> b3 b2 b1 b0 <DLE> <ETX>
senden.
DLE, STX und ETX sind ASCII Steuerzeichen die genau dafür gedacht sind.
(Data Link Escape, Start of Text, End of Text).
Nun sind aber wieder (min.) 8 Byte geworden, die gesendet werden müssen.
Aber so ist das Leben, ohne Protokolloverhead geht (fast) nix.
Ach so und nochmal zu deiner Frage:
>Wie kann man das Bytemuster einer Variablen (int float ...) in einen>String verwandeln den man via UART, CAN oder weiss der Teufel was>verschicken kann und am Ende der Nahrungskette wieder zurück in eine>float, int ... ??
Das haben meine Vorredner zwar schon erschöpfend behandelt...
Wer sagt denn, dass man nur "Strings" über die serielle Schnittstelle
schieben kann. Da werden "Zeichen" übermittelt, die je nach
eingestellten
Parametern 7,8 oder z.B. 9 bit lang sein können. In der Regel heutzutage
aber nunmal 8 bit. Also ein BYTE. Also: Jeder beliebige Wert von 0..255
kann an UDR gegeben werden.
Der Hinweis mit der union oben war, wie du nun komfortabel auf die
einzelnen 4 Speicherstellen deines Floats zugreifen kannst.
Möglichkeiten gibts aber noch genug andere.
Obacht nur bei der Endianness der beiden Systeme beim Zerlegen und
wieder Zusammensetzen der Zahlen!
und vor allem - solange nicht der Übertragungskanal zum Flaschenhals
wird - warum nicht ein paar Bytes mehr senden als nötig? Mir tut es
nicht weh, auch 20 Zeichen für eine Variable zu senden, solange mir
nicht die Übertragungszeit ausgeht. Und auch das
Auseinanderpuzzeln/Zusammensetzen - tutu doch auch nicht weh,
normalerweise. Gut, es gibt Anwendungen, wo es alles knapp wird. Hast du
so eine? Oder ist es "nur" sportlicher Ehrgeiz, overhead einzusparen?
... Da steh ich nun ich armer Tor
und bin so schlau als wie zuvor ...
> Hast du so eine?
Nun ja, ich spiele mit dem Gedanken ein bisschen mit dem CAN-Bus zu
übertragen und da sind 8 Byte das Maximum. Natürlich kann ich eine
Information zerlegen und im Ziel wider zusammenschrauben, was aber blöd
wäre, wenn ich doch eigentlich eh nur 4Byt bräuchte, + 1 um den Inhalt
der Nachricht einer Bestimmung zuzuweisen, was wieder 255 verschiedene
Möglichkeiten, also weit mehr als gebraucht, ergibt.
naja, gerade am CAN ist es doch besonders einfach, die Daten sozusagen
binär zu übertragen, da du alles, was du sonst noch brauchst, schon vom
CAN-Frame erledigt wird.
Ohne die IDs unendlich aufzublähen: das 1.Byte der Botschaft sagt,
welche Variable in dieser Botschaft steckt.
Ja, das war mein Gedanke.
- die 4 Byte zu übertragen statt den Ascii-Wert
...
Alleine an der Umsetzung hapert es.
Und an dieser Stelle hatte ich gehofft hier hler zu finden, aber da ich
schon zu doof war den Beitrag ins GCC-Forum zu schreiben ...
Mit Union und struct ist vermutlich irgend etwas zu gewinnen und auch
die Sache mit dem Pointer scheint mir nicht all zu falsch, aber sobald
ich irgend wo ein Stern setzt schreit der Compiler was von Pointer from
Integer without a cast... Mit dem Mist konnte ich bisher noch keinen
Blumnentopf gewinnen.
union ist in dem Fall genau das richtige.
Senden: float-zahl in die union schreiben, dann die 4 chars in den
messagebuffer kopieren, ab dafür.
empfangen: 4 chars aus der der Empfangsbotschaft in die union schreiben,
anschliessen float lesen, fertig.
cerberus, ich glaub du solltest wirklich erst noch ein paar Grundlagen
"pauken". Ist nicht böse gemeint, ist aber Fakt, wenn du bei Pointern
nur "Sterne" siehst ;-)
Ich kann mich noch gut an die Zeiten erinnern, als ich "mit sowas"
nichts zu tun haben wollte... Sich mal 2-3 Stunden damit befassen und
verstehen, wirkt aber wunder. Glaubs mir!
Wenn du deine Nachrichten über CAN versenden willst, vereinfacht sich
doch der Protokollkram schon wieder.
Du kannst einfach über den Identifier bestimmen, welche Nachricht
da gerade versendet wird.
Andererseits ist es in propriertären Systemen erlaubt und auch Usus,
den Identifier dahingehend zu "mißbrauchen", dass darin Absender,
Adressat, Nachrichtentyp, etc. Platz finden können. Und Nachrichten
größer 8 Byte werden, wie von dir vermutet, gesplittet. Auch da ist
alles erlaubt, was Spaß macht.
>Kann ich das reziprok auf der Empfangsseite auch verwenden?
Theoretisch ja, wenn du wie oben von mir geschrieben auf die
Endianness der Systeme achtest.
> Sich mal 2-3 Stunden damit befassen und verstehen, wirkt aber wunder. Glaubs
mir!
Ich habe diesen Mist mit den Pointern noch nie verstanden. Mir ist
durchaus klar, was das ist, und wie es theoretisch funktioniert, aber
wenn ich nur ein Sternchen im Programm wage pisst mich sofort der
Compiler an. Ich warte auf den tag an dem er mir bei einem "/*" Argument
makes Pointer from integer without a cast ausspuckt.
ERGO Ich glaube es Dir nicht.
Aber danke für die Hilfe. Ich glaube ich bekomme es so hin.
Ein weg für die Rückwandlung habe ich schon von der Eprom-Routine
Mit * und & muß es einfach mal "Klick" machen.
Danach wunderst du dich, warum du je ein Problem hattest das zu
verstehen.
Vielleicht findet sich ja mal jemand, der dir das beim Bier mal
in Ruhe erklären kann :-)
Ist doch eigentlich nicht so schwer:
float Wert; //Eine Variable vom Typ float
Wert=-1.234; //Der Wert -1.234 wird nach IEEE 754 als Gleitkommazahl
in 4 Byte abgelegt (BF 9D F3 B6).
Jetzt bastelt man sich einen Zeiger:
char *Zeiger;
und sagt ihm er soll auf die Adresse im Speicher Zeigen wo die Zahl
liegt.
Zeiger=&Wert; //Zeigt auf das Erste Byte vom Wert (BFh);
printf("%c", *Zeiger); //Gibt den ASCII Wert von BFh aus.
printf("%c", *(Zeiger+1)); //Gibt den ASCII Wert von 9Dh aus.
printf("%c", *(Zeiger+2)); //Gibt den ASCII Wert von F3h aus.
printf("%c", *(Zeiger+3)); //Gibt den ASCII Wert von B6h aus.
Und genau das machst du stillschweigend auch mit dem Union.
Mit:
- Zeiger kommst du an die Speicheradresse auf die der Zeiger zeigt.
- *Zeiger bekommst du den Inhalt dessen was an der Speicheradresse liegt
die in Zeiger gespeichert ist, interpretiert mit dem Typ des Zeigers.
- &Zeiger kommst du an die Speicheradresse des Zeigers selbst (dieses
geht natürlich auch bei anderen Variablen).
Benutze doch eine Base64 (3 Byte weden auf 4 abgebildet), dann hast du
nur Ascii Zeichen. Oder gleich Base 85 (4 Byte werden auf 5 abgebildet)
Das sollte jede Serielle Verbindung schaffen
Gruß