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 (:
All Wissender schrieb: > Hans D. schrieb: >> ist es so ohne weiteres möglich einen float in Bytes aufzuteilen? > > Ja. Wie genau?
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; |
Das binär zu versenden geht natürlich, aber willst du das wirklich? Es es treten allen Bytes 00x00..0xff auf...
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
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]); }
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
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.
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).
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...
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; |
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
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).
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.
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
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?
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
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); |
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.