Hallo, mir ist schon öfter aufgefallen das ich so meine liebe Mühe mit dem umwandeln von verschiedenen Datentypen habe wenn es darum geht Daten zu übertragen. Als Beispiel eine SPI Kommunikation. Die Funktion zum senden und empfangen von Daten erwartet ein char array. In diesem array habe ich 2 Byte. Die Funktion sendet und ich bekomme auch einzelne Bytes zurück. In meinem Fall kommen 8 Bytes zurück die ebenfalls in einem char array landen. Jetzt kommt mein Problem. Jetzt muss ich diese Bytes irgendwie wieder zusammen bringen. Zum Debuggen würde ich diese gerne als binäre Kette aus Nullen und Einsen sehen. Habe ich mein Debugging fertig und ich bekomme die richtigen Werte binär gesendet muss ich aus den einzelnen Bytes ein Double machen um damit weiter zu arbeiten. Doch wie ist dafür der richtige Weg?
Da gibt keinen richtigen Weg. Das erste Problem ist schon mal, du kannst dich nicht darauf verlassen, dass der Chip die Double genau so kodiert wie dein Prozessor. Dann diese kleinen Gemeinheiten. z.B. Bytearray darf an ungeraden Adressen stehen, Double nur an geraden. Da verwendet man unterschiedliche Lösungen. Wenn die Interna zusammen passen, nimmst du eine Union. Du bastelst die Zahl mit <<, | und Unsigned-Casts zusammen. Du nimmst eine Library wie ASN.1 . Alles nicht so das, was man als gute Lösung bezeichnen könnte.
Beitrag #5258534 wurde von einem Moderator gelöscht.
Beitrag #5258535 wurde von einem Moderator gelöscht.
Stephano B. schrieb Unfug im Beitrag #5258534:
>
Dich gibts ja immernoch? Ich dachte du wärst endlich mal gebannt
nach deiner Aktion heute morgen
@topic:
Der Standardweg läuft über Structs. Du definierst dir deine
Nachricht, welche auch komplexer sein kann, füllst diese
mit den Übertragenen Daten und liest aus dieser aus.
z.b.:
1 | struct Message |
2 | {
|
3 | double messdaten; |
4 | // hier beliebig viele Einträge
|
5 | uint8_t channel; |
6 | float temperature; |
7 | };
|
8 | |
9 | // füllen
|
10 | Message msg; |
11 | Read(&msg); |
12 | |
13 | // und auslesen
|
14 | double ergebnis = 25 * msg.messdaten; |
Hat man bereits ein char Buffer kann man diesen einfach "uminterpretieren" und so auslesen, ohne Kopiervorgang
1 | char buffer[8]; |
2 | |
3 | // irgentwie füllen
|
4 | |
5 | for (uint8_t i = 0; i < sizeof(buffer); ++i) |
6 | buffer[i] = ReadSPIByte(); |
7 | |
8 | // uminterpretieren, C version
|
9 | |
10 | Message *ptr1 = (Message *)buffer; |
11 | |
12 | // uminterpretiere, C++ version
|
13 | |
14 | Message *ptr2 = reinterpret_cast<Message *>(buffer); |
15 | |
16 | // was damit machen, ob ptr1 oder ptr2 ist egal, da selber Inhalt
|
17 | LCDSendData(ptr1->messdaten); |
Fall 1 kommt auch mit dem Fall klar, dass double unter einem festen Alignment stehen muss. typen schrieb: > Zum Debuggen würde ich diese gerne als binäre Kette > aus Nullen und Einsen sehen. Wo willst du diese Ausgeben? Wenn du Onchip debuggen kannst, reicht der Debugger. Wenn du z.b. ein LCD oder einen Seriellport hast, müsstest du die bits einzeln zusammen bauen. Stephano B. schrieb im Beitrag #5258534: > C++ kommt mir wie ein zusammengemurkstes Flickwerk vor Tja weil du eben Stephano B. schrieb im Beitrag #5258534: > noch nie Software > geschrieben hast ;-)
Luftikus schrieb: > Message *ptr2 = reinterpret_cast<Message *>(buffer); Glückwunsch zur Freifahrt ins Undefined Behaviour Land.
mh schrieb: > Glückwunsch zur Freifahrt ins Undefined Behaviour Land. Tatsächlich nicht. Aus dem C++ Standard: >An object pointer can be explicitly converted to an object pointer of a different type.71 When a prvalue v of >object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast<cv >T*>(static_cast<cv void*>(v)). Converting a prvalue of type “pointer to T1” to the type “pointer to T2” >(where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of >T1) and back to its original type yields the original pointer value das Array zerfällt implizit zu einem Pointer auf byte (oder char, was immer man so nimmt). Unter der Voraussetzung dass das Array auf der "anderen Seite" nach den selben Platformspezifischen Eigenheiten aus einem Objekt vom Typ Message erzeugt wurde, und 1zu1 übertragen wurde, ist garantiert dass wieder ein gültiges Objekt herauskommt. Da AVRs den Typ double nicht hardwäremäßig unterstützen gibt es auch keine Alignmentprobleme
Luftikus schrieb: > das Array zerfällt implizit zu einem Pointer auf byte (oder char, was > immer man so nimmt). Unter der Voraussetzung dass das Array auf der > "anderen Seite" nach den selben Platformspezifischen Eigenheiten > aus einem Objekt vom Typ Message erzeugt wurde, und 1zu1 übertragen > wurde, ist garantiert dass wieder ein gültiges Objekt herauskommt. Nein. Garantiert ist, dass ich einen Zeiger auf ein Objekt in einen Zeiger auf char und diesen dann wieder zurück konvertieren kann, ohne dass es Verluste gibt. Wenn ich die Daten wo anders hin kopiere, ist nicht garantiert, dass die Kopie genauso funktioniert, wie das Original. > Da AVRs den Typ double nicht hardwäremäßig unterstützen gibt es > auch keine Alignmentprobleme Wie kommst du auf AVR? Du bist der erste, der den erwähnt.
Luftikus schrieb: > mh schrieb: >> Glückwunsch zur Freifahrt ins Undefined Behaviour Land. > > Tatsächlich nicht. > > Aus dem C++ Standard: > ... Dein Zitat (8.2.10.7) aus dem Standard regelt das Konvertieren von Pointern, nicht den Zugriff auf Objekte durch Pointer. Die richtige Stelle im C++17 draft (N4687) ist 6.10.8:
1 | 6.10 Lvalues and rvalues [basic.lval] |
2 | ... |
3 | 8 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:(8.1) — the dynamic type of the object, |
4 | (8.2) — a cv-qualified version of the dynamic type of the object, |
5 | (8.3) — a type similar (as defined in 7.5) to the dynamic type of the object, |
6 | (8.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object, |
7 | (8.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object, |
8 | (8.6) — an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union), |
9 | (8.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object, |
10 | (8.8) — a char, unsigned char, or std::byte type. |
Punkt 8.8 regelt den Zugriff über char und byte Pointer auf andere Objekte. Der umgekehrte Weg ist nicht explizit aufgelistet und damit undefined behaviour.
Die Diskussion über Standards geht da ein bisschen in die falsche Richtung. Weder in den Chips noch im C++ Standard ist definiert, wie die Zahlen intern dargestellt werden. Hatte sogar mal einen Chip, der die Bits anders herum erwartete, als das SPI-Modul des MC. Casts und Unions funktionieren eigentlich nur, wenn du auf beiden Seiten den selben Prozessor und den selben Compiler verwendest. Der einzige Tipp den man da geben kann - die Umwandlung von Bits in Strukturen sauber kapseln, damit sich die Hacks und Tricksereien nicht auf das ganze Programm verteilen.
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.