Forum: Compiler & IDEs Frage Makro-Frage


von Ulrich Schneider (Gast)


Lesenswert?

Hallo,

ich habe leider noch wenig Erfahrung mit C und GCC und eine kleine
"Ist das irgendwie möglich"-Frage zu Makros.

Folgendes Problem:
Ich habe angefangen für die Atmel AVRs zu programmieren, würde aber
gerne den Sourcecode etwas einfacher, lesbarer und weniger auf eine
spezielle Architektur zugeschnitten haben, was die einfache Ein/Ausgabe
auf bestimmte IO-Ports betrifft. Genauer gesagt möchte ich zum
Ein-/Aus-/Umschalten/Richtung bestimmen für ein bestimmtes Port-Pin im
Quelltext nur eine einzige Variable benutzen.

Mal ein Beispiel: Ich würde ich beispielsweise gerne so etwas wie

SET_IO_PORT_TO_OUTPUT( PORT_C5 )
schreiben statt
DDRC |= (1 << DDC5)

oder

SET_OUTPUT_PORT_LOW( PORT_D2 )
statt
PORTD &= ~(1 << PD2)

Allerdings hätte ich es gerne so, dass der kompilierte Code dadurch
nicht grösser und langsamer wird; wenn es also beispielsweise möglich
wäre dass der GCC-Preprozessor den Makroaufruf
SET_IO_PORT_TO_OUTPUT( PORT_C5 )
automatisch in ein
DDRC |= (1 << DDC5)
umformt.

Ist das irgendwie möglich? Das Problem das ich habe ist, dass er zu
jeder Angabe wie PORT_C5 automatisch die passenden Werte der anderen
DDR, PORT und PIN-Register wissen muss (bei PORT_C5 also z.B. DDRC,
PORTC, PINC)

Da DDRC, PINC etc. ja vermutlich letztlich nur Werte sind dachte ich
ich löse das einfach, indem ich die entsprechenden Werte in eine
grössere Zahl "hineinpacke" und sie daraus dann wieder extrahiere,
z.B.

#define PORT_C5  ((DDRC << 24) | (PINC << 16) | (PORTC << 8) | 5)
[...]
#define DDR(port) (uint8_t)(port >> 24)
#define BITINDEX(port) (uint8_t)(port)
#define SET_IO_PORT_TO_OUTPUT(port) DDR(port) |= (1 << BITINDEX(port))

War auch schön gedacht, funktioniert aber leider nicht - aufgrund der
Fehlermeldung "invalid lvalue in assignment" gehe ich davon aus dass
das Problem ist, dass nach allen Ersetzungen durch den Preprozessor ein
Aufruf wie
SET_IO_PORT_TO_OUTPUT( PORT_C5 )
nicht durch
DDRC |= (1 << 5)
ersetzt wird sondern durch sowas wie
12 |= (1 << 5)

Hat Jemand eine Idee, wie ich das Problem trotzdem lösen könnte?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nun ja, wenn du sowas akzeptierst:
1
#define SET_IO_PORT_TO_OUTPUT(port,pin) DDR##port |= (1 << (pin))
2
...
3
SET_IO_PORT_TO_OUTPUT(C,5);

dann geht es.

Das nennt man auch einen hardware abstraction layer (HAL).

von Ulrich Schneider (Gast)


Lesenswert?

Danke für den Tip, Jörn, auf die Idee bin ich nicht gekommen. Allerdings
hätte ich dann ja trotzdem noch zwei Parameter in jedem Aufruf, und wenn
es irgendwie mit einem ginge, das wäre halt noch schöner...

Das praktische wenn ich nur einen Parameter hätte wäre halt auch, dass
ich dann auch sowas schreiben könnte wie

#define POWER_STATUS_LED_IO_PORT PORT_C5
[...]
SET_IO_PORT_TO_OUTPUT( POWER_STATUS_LED_IO_PORT );

und falls ich mich dann entscheiden sollte die Power-LED an einen
anderen IO-Pin zu klemmen, dann müsste ich nur einen einzigen Wert
ändern.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

1
#define SET_IO_PORT_TO_OUTPUT_internal(port,pin) DDR##port |= (1 <<
2
(pin))
3
#define SET_IO_PORT_TO_OUTPUT(y) SET_IO_PORT_TO_OUTPUT_internal(y)
4
#define POWER_STATUS_LED_IO_PORT C,5
5
6
SET_IO_PORT_TO_OUTPUT(POWER_STATUS_LED_IO_PORT);

von Ulrich Schneider (Gast)


Lesenswert?

Funktioniert einwandfrei - vielen Dank!!!

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.