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.
union aus float und 4 char... union{ float Zahl; char Zeichen[4]; };
??? Ich weiß, "Kanningham und Richie" Seite 155 oder wie das hier tausendfach zitierte Standardwerk heißt. Geht es ggf. etwas ausführlicher bitte?
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.
Kannst doch einige Informationen (Paket-Typ...) auch im Identifier kodieren.
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.
Kurzes Beispiel ohne union.
1 | Type v1; |
2 | |
3 | unsigned char *ptr = (unsigned char *)&v1; |
4 | |
5 | for (int i = 0; i < sizeof(Type); i++) { |
6 | uart_send(ptr[i]); |
7 | // oder
|
8 | uart_send(*ptr); ptr++; |
9 | }
|
@arc net Vielen Dank. Kann ich das reziprok auf der Empfangsseite auch verwenden?
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).
Beim AVR Microcontroller natürlich in umgekehrter Reihenfolge, da der Wert -1.234 rückwärts in die 4 Byte abgelegt wird -> (B6 F3 9D BF). http://de.wikipedia.org/wiki/Byte-Reihenfolge
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ß
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.