mikrocontroller.net

Forum: Compiler & IDEs "High" und "Low" als Makro


Autor: Steffen Hausinger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich suche eine Möglichkeit, mit der ich Variablen (>8 Bit) in die 
entsprechenden Bytes aufteilen kann und so hinterher einzeln bearbeiten 
kann.

Um 16-Bit Variablen in 8-Bit Blöcke zu teilen klappt das einwandfrei:
#define HIGH(Word16) (((uint8_t*) &Word16)[1])
#define LOW(Word16) (((uint8_t*) &Word16)[0])

uint16_t foo;
LOW(foo)=0x12;

Soweit so gut. Jetzt möchte ich nach diesem Prinzip eine 32-Bit Variable 
aufteilen, aber nicht in Bytes, sondern in 16-Bit Worte:
#define HIGH(Word32) (((uint16_t*) &Word32)[1])
#define LOW(Word32) (((uint16_t*) &Word32)[0])

uint32_t foo;
HIGH(foo)=0x1234;

Diese Variante funktioniert ersteinmal auch. Allerdings gibt mir der 
Compiler folgende Warnmeldung:

>"dereferencing type-punned pointer will break strict-aliasing rules"


In den beiden Beiträgen, die ich hier im Forum zu diesem Thema gefunden 
habe, werden Workarounds vorgeschlagen. Beispielsweise soll eine Union 
verwendet werden. Das möchte ich aber nicht, da ich dann ja zukünftig 
alle Variablen als u_Union16, Union16, u_Union32, ... deklarieren 
müsste. Sehr unschön.

Was kann ich tun?

Grüße
Steffen

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen Hausinger wrote:
> Beispielsweise soll eine Union
> verwendet werden. Das möchte ich aber nicht, da ich dann ja zukünftig
> alle Variablen als u_Union16, Union16, u_Union32, ... deklarieren
> müsste. Sehr unschön.

Du musst sie ja nicht als Union deklarieren, du kannst sie ja auch im 
Makro zur Union casten.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde das nicht mit Pointer-Casting machen, sondern mit Shift-
Operationen. Diese setzt der Compiler zwar nicht immer optimal um,
aber auch die Pointermethode ist oft nicht effizient, weil der
Compiler wegen der Verwendung des Adressoperators die Variable ins RAM
legt, statt sie in Registern zu halten.

Oder eben -fno-strict-aliasing verwenden. Dann geht aber die Effizienz
möglicherweise woanders verloren.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Den Beitrag von Stefan Ernst habe ich zu spät gelesen. Das, was er
beschrieben hat, ist natürlich die beste Methode, auch
taktzyklenmäßig.

Autor: Steffen Hausinger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Idee mit dem Typecast als Union gefällt mir gut! Sie ergibt auch den 
gleichen kompakten Code wie die von mir im Eingangsposting beschriebene 
Variante. Ich habe es wie folgt implementiert:
// Union zum Konvertieren
typedef union
{
 uint32_t u32;
 uint16_t u16[2];
 uint8_t u8[4];
} Word32;

// High-Wort der 32-Bit Variable "foo" den Wert 0x1234 zuweisen
uint32_t foo;
Word32 *__Converter;
__Converter=(Word32*) &(foo);
__Converter->u16[1]=0x1234;

Nur leider klappt der Code nicht mehr, wenn ich daraus ein Makro forme! 
Wenn ich der Variable "foo" im High-Wort den Wert 0x1234 wie folgt 
zuweisen will
uint32_t foo;
HIGH16(foo)=0x1234;

dann sagt er mir

>"expected expression before '=' token".

Und wenn ich umgekehrt einer 16-Bit Variablen den High-Teil von foo 
zuweisen will:

>"expected expression before 'typecast'"

Das ist mir soweit auch verständlich, weil er bei der Variable ja einen 
Wert erwartet und keine weiteren Befehle, mit denen am Ende irgendwann 
ein Wert berechnet wird.

Gibt es eine Abhilfe? Ohne, die Union etc. an anderer Stelle "global" zu 
deklarieren?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen Hausinger wrote:

> Nur leider klappt der Code nicht mehr, wenn ich daraus ein Makro forme!

Wie sehen denn die Makros aktuell aus?

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sowas in der Art sollte funktionieren:

#define HIGH16(Var) (((uint8_t*) &Var)[1])
#define LOW16(Var) (((uint8_t)*) &Var)[0])

Kann aber sein, dass es da noch ganz böse Fallstricke geben kann.

EDIT: Ups hab nur den Titel gelesen und nicht gesehen, dass der Thread 
schon eine Zeit lang läuft. Im ersten Post befinden sich ja sogar 
"meine" defines. hehe :-)

Autor: Steffen Hausinger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wie sehen denn die Makros aktuell aus?

Naja, im Prinzip so wie oben im C-Code, also:
#define HIGH16(Word) (typedef union{uint32_t u32;uint16_t u16[2];} Word32; \
                     Word32 *__Converter;\
                     __Converter=(Word32*) &(Word);\
                     __Converter->u16[1])

Der Ausdruck steht in runden Klammern. Ich hatte ihn ursprünglich in 
geschweiften Klammern stehen, damit der typedef ausserhalb des Makros 
seine Gültigkeit verliert. Aber das funktionierte genausowenig.

Fällt Dir da ein Fehler auf?

@Simon: Mit der Umwandlung 16Bit -> 2x 8Bit funktioniert es auch 
einwandfrei. Nur bei 32Bit -> 2x 16Bit meckert der Compiler. Das wundert 
mich.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen Hausinger wrote:

> Naja, im Prinzip so wie oben im C-Code, also:
>
#define HIGH16(Word) (typedef union{uint32_t u32;uint16_t u16[2];} Word32; \
                     Word32 *__Converter;\
                     __Converter=(Word32*) &(Word);\
                     __Converter->u16[1])

Igitt, so wird das natürlich nichts.
Nimm folgendes:
typedef union
{
 uint32_t u32;
 uint16_t u16[2];
 uint8_t  u8[4];
} Word32;

#define HIGH16(X) (((Word32*)&(X))->u16[1])
#define LOW16(X)  (((Word32*)&(X))->u16[0])

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach, ich sehe erst jetzt deinen Satz oben:
> Ohne, die Union etc. an anderer Stelle "global" zu deklarieren?

Was spricht denn gegen ein globales Union-Typedef?
Du kannst es doch einfach in die selbe Datei packen, wie die Defines.

Autor: Steffen Hausinger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

ich mache es jetzt so, wie Du vorschlägst. Das typedef sieht für mein 
Empfinden zwar nicht besonders elegant aus, aber dafür ist der Code sehr 
kompakt und der Compiler meckert nicht.

Danke für Deine und Eure Hilfe!
Steffen

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.