Forum: Mikrocontroller und Digitale Elektronik AVR - float in einzelne Bytes


von Hans D. (Gast)


Lesenswert?

Moin,

ist es so ohne weiteres möglich einen float in Bytes aufzuteilen?
Möchte gerne ein float über die Serielle jagen und das Byte für Byte..

Danke (:

von All Wissender (Gast)


Lesenswert?

Hans D. schrieb:
> ist es so ohne weiteres möglich einen float in Bytes aufzuteilen?

Ja.

von Hans D. (Gast)


Lesenswert?

All Wissender schrieb:
> Hans D. schrieb:
>> ist es so ohne weiteres möglich einen float in Bytes aufzuteilen?
>
> Ja.

Wie genau?

von Ingo L. (corrtexx)


Lesenswert?

Hans D. schrieb:
> Wie genau?
Stichwort: Union

von Hans D. (Gast)


Lesenswert?

So
1
  answer.data[0]    = ( current & 0x000000FF );
2
  answer.data[1]    = ( ( current & 0x0000FF00 ) >> 8 );
3
  answer.data[2]    = ( ( current & 0x00FF0000 ) >> 16 );
4
  answer.data[3]    = ( ( current & 0xFF000000 ) >> 24 );
5
  answer.dataLen    = 4;

von H.Joachim S. (crazyhorse)


Lesenswert?

Das binär zu versenden geht natürlich, aber willst du das wirklich? Es 
es treten allen Bytes 00x00..0xff auf...

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

1
float myFloat = 3.14;
2
char raw [sizeof(myFloat)];
3
memcpy (raw, &myFloat, sizeof(myFloat));
Das Problem daran ist, dass das Ergebnis (d.h. die Daten in "raw") nicht 
von C(++) garantiert sind, da das float-Format von der Plattform 
abhängt. Beim AVR-GCC ist "float" ein IEEE 754 32bit float und "raw" 
dann entsprechend die Binärdaten; wenn du diesen Code aber auf einen 
anderen Controller überträgst (oder auch nur einen anderen Compiler 
verwendest) kann das Ergebnis anders sein.
Siehe auch Serialisierung. Für float ist das Problem aber nur schwer 
korrekt zu lösen.

Ingo L. schrieb:
> Stichwort: Union
union ist hier wie üblich nicht optimal; memcpy ist die korrekteste 
Möglichkeit (siehe Artikel). Union ist nur zum Speicher sparen da, nicht 
zum Daten konvertieren.

Hans D. schrieb:
> So
Das geht so nur für Integer.

: Bearbeitet durch User
von kyrk.5 (Gast)


Lesenswert?

Es gibt da einige Sachen worauf man aufpassen soll:

- Je nach Compiler kann der float Darstellung sich ändern. Wobei mittler 
weile verwenden alle Compilerherstellen den gleichen Standard. Das 
heisst wenn du von uC richtung PC etwas schickst muss der PC die gleiche 
verständniss vom float haben, sprich gleich Standard verwenden.

- Endiannes: Bytes von vorne nach hinten, oder hinten nach vorne 
schicken. Der empfänger muss hier auch das gleiche machen

- float oder double: Aufpassen, es kann ja sein dass doubles als float 
behandelt werden oder umgekehrt. Kompilereinstellungen angucken.

float test = 0.0f;
char * ptrToTest = (char *)&test; //habe nicht getestet, daher ohne 
garantie :)

int bytecnt = sizeof(float) / sizeof(char);
oder
int bytecnt = sizeof(test) / sizeof(char);

for (i = 0; i < bytecnt; i++) {
   sendCharOnUsart(ptrToTest[i]);
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Hans D. schrieb:
> ist es so ohne weiteres möglich einen float in Bytes aufzuteilen?
> Möchte gerne ein float über die Serielle jagen und das Byte für Byte..
Ich würde dir empfehlen, nur ASCII-Zeichen über die serielle Schnitte zu 
"jagen". Dann kannst du mit einem PC und einem Terminalprogramm ganz 
einfach mitlesen, was da für Daten unterwegs sind. Und du kannst dank 
der definierten Steuerzeichen STX, ETX, CR, LF usw. ganz einfach ein 
richtiges Protokoll (am Besten mitsamt Prüfsumme) implementieren.

Die denkbar schlechteste (Anfänger-)Idee ist, einfach irgendwelche Bytes 
über die serielle Schnitte zu schicken und zu hoffen, dass man am 
anderen Ende den Start in diesem Datenstrom findet. Du wirst 
herausfinden, dass das nicht so einfach ist. Und du wirst auch 
herausfinden, dass es gut wäre, die empfangenen Daten auf Konsistenz 
prüfen zu können.

: Bearbeitet durch Moderator
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

kyrk.5 schrieb:
> char * ptrToTest = (char *)&test; //habe nicht getestet, daher ohne

Das ist so zwar korrekt, die Gegenrichtung aber nicht. Funktioniert auch 
nur mit "char". Daher ist memcpy besser.

kyrk.5 schrieb:
> int bytecnt = sizeof(float) / sizeof(char);
sizeof(char) ist immer 1. Daher ist das hier überflüssig.

von All Wissender (Gast)


Lesenswert?

kyrk.5 schrieb:
> Es gibt da einige Sachen worauf man aufpassen soll:

Das geht weit über das hinaus was der TO wissen wollte
(aus der Fragestellung ist das zu schliessen).

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

All Wissender schrieb:
> kyrk.5 schrieb:
>> Es gibt da einige Sachen worauf man aufpassen soll:
> Das geht weit über das hinaus was der TO wissen wollte
Es ist aber etwas, das der TO wissen sollte. Denn manch einer will 
etwas, weil er sich die Konsequenzen nicht ausmalen kann...

von Stefan F. (Gast)


Lesenswert?

Lothar M. schrieb:
> Ich würde dir empfehlen, nur ASCII-Zeichen über die serielle Schnitte zu
> "jagen".

Das würde ich bei jeder Schnittstelle machen, soweit möglich. Auch im 
Internet haben sich menschen-lesbare Textformate (wie XML und JSON) 
durchgesetzt.

Und ich würde bei kleinen Mikrocontrollern wegen dem Speicherbedarf auf 
Fließkomma verzichten, soweit möglich. Statt 20,1°C kann man ja einfach 
201 verwenden und für die Darstellung so splitten:
1
value = 201; // für 20.1°C
2
grad = value / 10;
3
nachkommastellen = value % 10;

von Thomas T. (runout)


Lesenswert?

Hallo Lothar,

warum sollte eine serielle Schnittstelle
nicht "transparent" verwendet werden?

Häufig wird USART durch Ethernet (LAN/WLAN) oder USB getunnelt.
Fix-Frame-Format und CRC drüber.

Gut, für das gute,alte Debug-Interface ist das ASCII-Gerödel
"human-readable"...
Bei einer Prozesskopplung fängt dann das String-Geparse
auf der Emfpfängerseite an.

Ein USART ist einfach ein Datenkanal.
ASCII muss da nicht drüber.

Grüße
Runout

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Stefanus F. schrieb:
> Das würde ich bei jeder Schnittstelle machen, soweit möglich. Auch im
> Internet haben sich menschen-lesbare Textformate (wie XML und JSON)
> durchgesetzt.

Was aber auch nicht un-kontrovers ist :-)
http://java-is-the-new-c.blogspot.com/2014/10/why-protocols-are-messy-concept.html

Bei ASCII-Protokollen hat man schnell Nachrichten mit unnötigerweise 
variablen Paketgrößen. Das macht das Parsen komplizierter und verhindert 
Empfang per DMA o.ä., weil man nach CR/LF suchen muss. Das geht besser 
wenn man weiß dass jedes Paket z.B. 1kB groß ist.
Float<->Text ohne Präzisionsverlust ist auch nicht so offensichtlich und 
ineffizient (viele Ziffern).

von kyrk.5 (Gast)


Lesenswert?

Niklas G. schrieb:
> kyrk.5 schrieb:
>> int bytecnt = sizeof(float) / sizeof(char);
> sizeof(char) ist immer 1. Daher ist das hier überflüssig.

Genau, aber ist besser so zu lernen denn irgendeinmal kommt ein int rein 
wo es dann 2 oder 4 wäre. Sonst vergisst man es.

von Klaus (Gast)


Lesenswert?

Thomas T. schrieb:
> warum sollte eine serielle Schnittstelle
> nicht "transparent" verwendet werden?

Weil die Daten nicht geframed sind, oder anders herum formuliert ein 
Frame 8 Bit sind. Und genau dafür wurde die asynchrone serielle 
erfunden, um einzelne ASCII-Zeichen von einem Fernschreiber zu einem 
anderen zu senden. Da war dann 7 Bit plus Parity üblich.

Um größere Frames transparent zu übertragen gibts z.B. I2C (Start/Stop 
am Anfang und Ende eines Frames), SPI (CS markiert einen Frame), HDLC 
(Flags am Anfang und Ende eines Frames) oder Ethernet Pakete.

MfG Klaus

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas T. schrieb:
> Häufig wird USART durch Ethernet (LAN/WLAN) oder USB getunnelt.
> Fix-Frame-Format und CRC drüber.
Du bist da offenbar schon einen großen Schritt weiter als der TO...

> Ein USART ist einfach ein Datenkanal.
> ASCII muss da nicht drüber.
Eh' klar. Aber dann braucht man andere Synchronisations- und 
Datensicherungmechanismen als die STX-ETX-Geschichte, die von den 
Erfindern vorgesehen waren. Wir reden hier von und mit einem Anfänger, 
der das Thema blauäugig angeht. Da kann man sich leicht ein blaues Auge 
holen.

Niklas G. schrieb:
> Float<->Text ohne Präzisionsverlust ist auch nicht so offensichtlich und
> ineffizient (viele Ziffern).
Das Thema "Busauslastung" ist ein Anderes. Darauf kommen wir später 
zurück. Und soooo arg ineffizient ist die ASCII-Übertragung nun auch 
nicht.
Ein float hat eh nur 7 signifikante Stellen. Das sind dann mitsamt Punkt 
gerade mal doppelt so viele Bytes als die 4, die für die float-Rohdaten 
gänzlich ohne Datensicherung nötig wären.
Und nochmal einen Schritt zurück: ob für diese Anwendung ein float 
überhaupt nötig und sinnvoll ist, darüber haben wir noch nicht 
gesprochen und Hans vermutlich nicht nachgedacht.

Und so fragen wir einfach mal bei Hans nach:
> Möchte gerne ein float über die Serielle jagen und das Byte für Byte..
Was willst du denn da für Daten übertragen? Wie oft pro Sekunde soll das 
passieren? Woher kommen die Daten und wo gehen sie hin?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Lothar M. schrieb:
> der das Thema blauäugig angeht. Da kann man sich leicht ein blaues Auge
> holen.

 Hmmm, wie viele blaue Augen hat er dann zum Schluss?

 P.S.
 In float, natürlich.

: Bearbeitet durch User
von Peter S. (Gast)


Lesenswert?

Das kann man mit einer union und einer (anonymen) struct machen, wenn es 
auf der jeweiligen Architektur unterstützt wird und nicht portabel sein 
muss.

Im Prinzip so:
1
// union fuer zugriff als float und byte-weise
2
typedef union
3
{
4
    // fuer zugriff als float 
5
    float32_t float32;
6
7
    // fuer zugriff auf einzelne bytes
8
    struct
9
    {
10
        uint8_t byte0;
11
        uint8_t byte1;
12
        uint8_t byte2;
13
        uint8_t byte3;
14
    };
15
} txfloat_t;
16
17
// objekt anlegen
18
txfloat_t txfloat;
19
20
// als float reinschreiben
21
txfloat.float32 = 1.234f;
22
23
// als einzelne bytes senden
24
SendByte(txfloat.byte0);
25
SendByte(txfloat.byte1);
26
SendByte(txfloat.byte2);
27
SendByte(txfloat.byte3);

von Stefan F. (Gast)


Lesenswert?

Peter S. schrieb:
> Das kann man mit einer union und einer (anonymen) struct machen,
> wenn es auf der jeweiligen Architektur unterstützt wird und
> nicht portabel sein muss.

So weit waren wir schon, erzähle mal etwas neues.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Den umgewandelten float in 4 Bytes muss man ja nicht direkt binär auf 
den UART loslassen.
Die Bytes einfach in ASCI HEX umwandeln, schon ist es Text und die 
Steuerzeichen sind nutzbar.

@Niklas:
DMA freundliche Übertraungen haben üblicherweise einen Header mit fester 
Größe und in dem steht dann die Payloadlänge.
Schon lassen sich variable Längen mit DMA einlesen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Mw E. schrieb:
> DMA freundliche Übertraungen haben üblicherweise einen Header mit fester
> Größe und in dem steht dann die Payloadlänge.

Wenn man das so macht ist es ok, definitiv besser als Terminierung nur 
per CR/LF. Variable Längen würde ich aber dennoch nur nutzen wenn es 
wirklich nötig ist, und das ist es bei einer fixen Anzahl Zahlen (Int, 
Float) nicht... Wenn man ASCII nutzt, ist dann auch der Header 
ASCII-kodiert? Dann ist der ja auch variabel lang, wenn man nicht mit 
0en auffüllt... :-)

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.