www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Ökonomische Datenübertragung


Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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  ... ??

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
union aus float und 4 char...

union{
 float Zahl;
 char Zeichen[4];
};

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
???
Ich weiß, "Kanningham und Richie" Seite 155  oder wie das hier 
tausendfach zitierte Standardwerk heißt. Geht es ggf. etwas 
ausführlicher bitte?

Autor: 6642 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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!

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... 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.

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kannst doch einige Informationen (Paket-Typ...) auch im Identifier 
kodieren.

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Super. Gibt es irgend wo ein Beispiel dafür?

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kurzes Beispiel ohne union.

Type v1;

unsigned char *ptr = (unsigned char *)&v1;

for (int i = 0; i < sizeof(Type); i++) {
   uart_send(ptr[i]);
   // oder
   uart_send(*ptr); ptr++;
}

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@arc net
Vielen Dank.
Kann ich das reziprok auf der Empfangsseite auch verwenden?

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Kann ich das reziprok auf der Empfangsseite auch verwenden?

Theoretisch ja, wenn du wie oben von mir geschrieben auf die
Endianness der Systeme achtest.

Autor: cerberus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 :-)

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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).

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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ß

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.