Forum: Compiler & IDEs sauberes #define


von flyingwolf (Gast)


Lesenswert?

Hallo Forenuser.
ich habe mir aus den Codebeispielen diese schönen Zeilen zum setzen und
löschen von Bits kopiert
  #define setbit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
Nun möchte ich aber dieses weitertreiben, so dass ich Proceduren auch
auf anderen Ports oder anderen Prozessoren benutzen kann.
also z.B. einen gepulsten Port für IR-Kommunikation.
Wenn ich das in dieser Form mache
 #define ir_transmit PORTA,PA6
gibts einen Fehler bei
setbit(ir_transmit);
mit der Begründung
macro "setbit" requires 2 arguments, but only 1 given
Die Fehlermeldung ist klar.
Versteht der Compiler nicht, dass es sich bei ir_transmit um 2
Argumente handelt?
Wie macht man es richtig?

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


Lesenswert?

> Versteht der Compiler nicht, dass es sich bei ir_transmit um 2
> Argumente handelt?

Für den Präprozessor ist es nur 1 Argument.

> Wie macht man es richtig?
1
#define s(a,b) do { (a) |= 1 << (b); } while (0)
2
#define setbit(x) s(x)
3
#define x PORTD,2
4
5
setbit(x)

Schräg, ja, ich weiß. :-)

von Stefan (Gast)


Lesenswert?

Das SETBIT ist selbst schon ein Makro. Der Präprozessor merkt sich eine
Liste von allen Defines zum späteren ersetzen. Wenn im Sourcecode die
vorgesehene Ersetzung von SETBIT vor der Ersetzung von IR_TRANSMIT
abgearbeitet wird, kommt die Fehlermeldung. Du müsstest zusehen, dass
das define von IR_TRANSMIT dem Präprozessor zuerst bekannt wird.

Tipp: Wenn man Makros grundsätzlich nur in Grossbuchstaben schreibt,
sieht man eher was im Sourcecode los ist.

von peter dannegger (Gast)


Lesenswert?

"Schräg, ja, ich weiß. :-)"


Wow !

Die Typüberprüfung erfolgt also nur bei der ersten Macroexpansion und
läßt sich durch 3-fach Macros überlisten.

Ist das nun ein Bug oder ein Feature des AVR-GCC ?

Ist das auch bei anderen Compilern so ?


Peter



P.S.:
Muß ich gleich mal ausprobieren.

von flyingwolf (Gast)


Lesenswert?

ich schließe mich dem wow an.
Wäre Stefans lösung auch ok? Dann bliebe nämlich die Funktion von
SETBIT für einzeln zu setzende Bits erhalten oder führt das ggf zu
anderen Problemen?

von yalu (Gast)


Lesenswert?

> Ist das nun ein Bug oder ein Feature des AVR-GCC ?

Kein Bug! Schau mal da:

http://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments

Wichtig ist folgender Abschnitt:

All arguments to a macro are completely macro-expanded before they are
substituted into the macro body. After substitution, the complete text
is scanned again for macros to expand, including the arguments. This
rule may seem strange, but it is carefully designed so you need not
worry about whether any function call is actually a macro invocation.
You can run into trouble if you try to be too clever, though. See
Argument Prescan, for detailed discussion.

> Ist das auch bei anderen Compilern so ?

Das ist das im ISO-Standard so vorgesehen. Allerdings gab es in der
Vergangenheit etliche Compiler bzw. Präprozessoren (einschließlich
derjenigen von Microsoft), die diese Regel falsch umsetzten, so dass
das entweder beim Konstrukt von flyingwolf fälschlicherweise kein
Fehler oder bei dem von Jörg fälschlicherweise ein Fehler ausgegeben
wurde.

von Stefan (Gast)


Lesenswert?

@ flyingwolf

Kann ich selbst beantworten: Vergiss es wieder. Klappt nicht! Habe es
gerade ausprobiert. Sorry!

von A.K. (Gast)


Lesenswert?

Die Variante von Jörg funktioniert auch bei Digital Mars, nicht jedoch
bei Microsoft (warning C4003: Nicht genügend übergebene Parameter für
das Makro 'setbit2').

Eine Variante, die aber bei allen korrekten Präprozessoren
funktionieren sollte, und die sich elegant auf Funktionen auf die
PINA/DDRA Register erweitert:
1
#define ir_transmit_PORT  A
2
#define ir_transmit_PIN   6
3
4
#define p_port(x)  x##_PORT
5
#define p_pin(x)  x##_PIN
6
#define p_in(x)  PIN##x
7
#define p_out(x)  PORT##x
8
#define p_dir(x)  DDR##x
9
#define pp_in(x)  p_in(x)
10
#define pp_out(x)  p_out(x)
11
#define pp_dir(x)  p_dir(x)
12
13
#define testbit(x) (pp_in(p_port(x)) & 1<<p_pin(x))
14
#define setbit(x)   (pp_out(p_port(x)) |= 1<<p_pin(x))
15
#define clrbit(x)   (pp_out(p_port(x)) &= ~(1<<p_pin(x)))
16
#define setout(x)   (pp_dir(p_port(x)) |= 1<<p_pin(x))
17
#define setinp(x)   (pp_dir(p_port(x)) &= ~(1<<p_pin(x)))
18
19
void
20
f(void)
21
{
22
    if (testbit(ir_transmit)) {
23
  do_something();
24
    }
25
    setout(ir_transmit);
26
    setbit(ir_transmit);
27
}

Resultat ist:
1
void
2
f(void)
3
{
4
    if ((PINA & 1<<6)) {
5
        do_something();
6
    }
7
    (DDRA |= 1<<6);
8
    (PORTA |= 1<<6);
9
}

von peter dannegger (Gast)


Lesenswert?

Die Variante von Volker ist aber auch nicht schlecht:

http://www.mikrocontroller.net/forum/read-1-324854.html#new


Da hat man sogar wieder die vom 8051 gewohnten Bitvariablen.

Also ideal, um C51 Programme zu portieren.


Peter

von A.K. (Gast)


Lesenswert?

Bitfelder werden vom Compiler leider meist ausgesprochen ineffizient
implementiert.

von flyingwolf (Gast)


Lesenswert?

Bitfelder = struct?
Da habe ich letztens was interessantes gesehen. Alle Variablen waren in
einer Struktur definiert auch die 1-Bit-Vars und dann wurde die ganze
Structur ins EEProm geschrieben.

von A.K. (Gast)


Lesenswert?

"Bitfelder = struct?"

Ja. wenn sowas wie
    unsigned b1 : 1;
drin ist.

von peter dannegger (Gast)


Lesenswert?

Also ich hab das mit den Bitfeldern ausprobiert.

Zumindest der AVR-GCC macht dann die SBI- und CBI-Befehle draus, ganz
so wie es sein soll.


Peter

von Dirk B. (dirk-)


Lesenswert?

Hi,

>Also ich hab das mit den Bitfeldern ausprobiert.

>Zumindest der AVR-GCC macht dann die SBI- und CBI-Befehle draus, ganz
>so wie es sein soll.

@Peter

Weisst du wie es bei Keil und 8051er aussieht?

Gruß,

Dirk

von A.K. (Gast)


Lesenswert?

Solange es um einzelne Bits geht, kann der Compiler was draus machen -
wenngleich ich auch schon abschreckende Beispiele gesehen habe (weiss
bloss nicht mehr wo).

Der Haken bei Bitfeldern liegt aber wesentlich auch darin, dass man
keine Chancen hat, es besser zu machen. Jedenfalls nicht solange man
bei den Bitfeldern bleibt und nicht nebenbei noch die klassischen
Register und Bits pflegt. Sowas wie
   ControlRegister |= 1<<Enable | 1<<CrashAndBurn;
ist bei Bitfeldern nicht drin, und
   ControlRegister.Enable = 1;
   ControlRegister.CrashAndBurn = 1;
darf bei dem natürlich als "volatile" deklarierten ControlRegister
kein Compiler legal zusammenfassen (wenngleich SDCC sowas ähnliches
fertigbringt). Mit CBI/SBI ist das ok, u.U. sogar besser, bei nicht
bitweise adressierbaren Adressen zieht sich das jedoch arg in die
Länge.

von Peter D. (peda)


Lesenswert?

"Weisst du wie es bei Keil und 8051er aussieht?"


Beim Keil kann man mit "sbit" einzelne Bits einer bitadressierbaren
Variable definieren. Näheres im Keil Handbuch oder auf deren Webseite
(Knowledegbase).


Peter

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.