Forum: Mikrocontroller und Digitale Elektronik Mehrzeilige defines


von Alexandre (Gast)


Lesenswert?

Hallo zusammen,

Ich habe da einen Fehler, der für euch wahrscheinlich sehr einfach ist.
Lösung habe ich beim durchsuchen leider keine gefunden.

Also ich habe einerseits diese Deklarationen, welche mir einfch die 
I/O's entsprechend setzen:
1
#define Power_Relay_1                   PORTA   |= BIT1;        \
2
                                        PORTA   |= BIT2;                        
3
#define Maintain_Relay_1                PORTA   &= ~BIT1
4
#define Release_Relay_1                 PORTA   &= ~BIT1;       \
5
                                        PORTA   &= ~BIT2;
6
#define Power_Shutter_1_Up              Power_Relay_1     
7
#define Maintain_Shutter_1_Up           Maintain_Relay_1
8
#define Release_Shutter_1_Up            Release_Relay_1
9
#define Status_Relay_1                  ((PINA&BIT2) != 0x00)
10
                                          
11
#define Power_Relay_2                   PORTA   |= BIT3;        \
12
                                        PORTA   |= BIT4;  
13
#define Maintain_Relay_2                PORTA   &= ~BIT3
14
#define Release_Relay_2                 PORTA   &= ~BIT3;       \
15
                                        PORTA   &= ~BIT4;
16
#define Power_Shutter_1_Down            Power_Relay_2     
17
#define Maintain_Shutter_1_Down         Maintain_Relay_2
18
#define Release_Shutter_1_Down          Release_Relay_2
19
#define Status_Relay_2                  ((PINA&BIT4) != 0x00)
20
                                          
21
#define Power_Relay_3                   PORTA   |= BIT5;        \
22
                                        PORTA   |= BIT6;
23
#define Maintain_Relay_3                PORTA   &= ~BIT5
24
#define Release_Relay_3                 PORTA   &= ~BIT5;       \
25
                                        PORTA   &= ~BIT6;
26
#define Power_Shutter_2_Up              Power_Relay_3     
27
#define Maintain_Shutter_2_Up           Maintain_Relay_3
28
#define Release_Shutter_2_Up            Release_Relay_3
29
#define Status_Relay_3                  ((PINA&BIT6) != 0x00)
30
31
// !!! TEILWEISE PORTG !!!
32
#define Power_Relay_4                   PORTA   |= BIT7;        \
33
                                        PORTG   |= BIT2;
34
#define Maintain_Relay_4                PORTA   &= ~BIT7
35
#define Release_Relay_4                 PORTA   &= ~BIT7;        \
36
                                        PORTG   &= ~BIT2;
37
#define Power_Shutter_2_Down            Power_Relay_4     
38
#define Maintain_Shutter_2_Down         Maintain_Relay_4
39
#define Release_Shutter_2_Down          Release_Relay_4
40
#define Status_Relay_4                  ((PING&BIT2) != 0x00)

Andererseits habe ich auch diese makros, um die entsprechenden (oben 
aufgeführten) I/O's zu setzen:
1
#define Power_Relay_Index                       if(Index == 0)          \
2
                                                  Power_Relay_1;        \
3
                                                if(Index == 1)          \
4
                                                  Power_Relay_2;        \
5
                                                if(Index == 2)          \
6
                                                  Power_Relay_3;        \
7
                                                if(Index == 3)          \
8
                                                  Power_Relay_4;
9
                                       
10
                                                  
11
#define Release_Relay_Index                     if(Index == 0)          \
12
                                                  Release_Relay_1;      \
13
                                                if(Index == 1)          \
14
                                                  Release_Relay_2;      \
15
                                                if(Index == 2)          \
16
                                                  Release_Relay_3;      \
17
                                                if(Index == 3)          \
18
                                                  Release_Relay_4;

Wenn ich dann aber:
1
Index = 1;
2
Power_Relay_Index;
ausführe, erwarte ich dass er ganz einfach Power_Relay_1 ausführt, also 
A1 und A2 setzt, aber ...
er setzt mir in der Reihenfolge:
A1, A2, A4 und danach A6...

Nach den "/" sind die Zeilen fertig, also keine Leerzeichen oder so.
Das ";" am Ende jedes #define hab ich auch schon versucht wegzulassen.

Mein Compiler: IAR
Mein Debugger AVR-Studio

Vielen Dank im Voraus !

von Stefan (Gast)


Lesenswert?

Ich mach das so (mit gcc):
1
#
2
    #define init_adc_pins()      { \
3
                                   writeBit(DDRB,5,1); \
4
                                   writeBit(DDRB,6,0); \
5
                                   writeBit(PORTB,7,0); \
6
                                   writeBit(DDRB,7,1); \
7
                                 }

von Maxx (Gast)


Lesenswert?

#defines sind keine Funktionen.

Der grundlegende Fehler ist es sie als Funktionsersatz zu nutzen. Du 
merkst, dass ein Fehler irgendwo zu Mist führt und du den nicht so 
einfach lokalisieren kannst.

Lass dir vom Compiler den Quellcode nach dem Preprozessor ausgeben. Dann 
kannst du zumindest sehen, was der Compiler als Eingabe bekommt.

von Wolfgang H. (frickelkram)


Lesenswert?

So kann man das nicht analysieren, weil Dein codeschnipsel umgebrochen 
wird (keine code ansicht) ist nicht klar ob Du da irgendwo einen Fehler 
gemacht hast.
Wenn Du gcc benutzt kannst Du die Option "-save-temps" nutzen. Dann 
bleibt nach dem compilieren eine *.i-Datei liegen. In der kannst Du 
sehen was der Preprozessor aus Deinen #defines und Macros gemacht hat.

von Alexandre (Gast)


Lesenswert?

Hallo und danke erstmal für die raschen Reaktionen !

Also zum debuggen ist es wirklich Mist, da mann es eben nicht 
nachverfolgen kann.
*.i Datei habe ich leider keine.
Ich versuche es mal mit den {} !

von chris (Gast)


Lesenswert?

#define Power_Relay_Index                       if(Index == 0) 
\
                                                  Power_Relay_1; 
\
                                                if(Index == 1) 
\
                                                  Power_Relay_2; 
\
                                                if(Index == 2) 
\
                                                  Power_Relay_3; 
\
                                                if(Index == 3) 
\
                                                  Power_Relay_4;


machte einen Fehler in If´s ,in zweierlei Hinsicht.

if (foo) Power_Relay_Index; else bar();

Zum einen macht hier das doppelte Semicolon probleme, gibt 
Kompilerfehler.
Zum anderen bezieht sich das Else jetzt nicht mehr auf foo sondern auf
Index == 3.

Entweder du machst es so:
#define Power_Relay_Index                       if(Index == 0) 
\
                                                  Power_Relay_1; 
\
                                                else \
                                                if(Index == 1) 
\
                                                  Power_Relay_2; 
\
                                                else \
                                                if(Index == 2) 
\
                                                  Power_Relay_3; 
\
                                                else \
                                                if(Index == 3) 
\
                                                  Power_Relay_4;
                                                else

Dies lässt dir die Freiheit eventuell auch noch weitere Else Zweige 
anzuhängen, oder normalerweise wird es so verpackt:

#define Power_Relay_Index                    do { \
                                                if(Index == 0) 
\
                                                  Power_Relay_1; 
\
                                                if(Index == 1) 
\
                                                  Power_Relay_2; 
\
                                                if(Index == 2) 
\
                                                  Power_Relay_3; 
\
                                                if(Index == 3) 
\
                                                  Power_Relay_4; 
\
                                             while(0)

Muss jetzt aber nicht heissen, daß dies dein Problem lösen könnte, bei
größerer SW oder länger wartbarer SW solltest du es allerdings zu Herzen
nehmen.

von chris (Gast)


Lesenswert?

while sollte } while sein.

von Wolfgang H. (frickelkram)


Lesenswert?

Hi Alexandre,

Alexandre schrieb:
> Hallo und danke erstmal für die raschen Reaktionen !
...
> Also zum debuggen ist es wirklich Mist, da mann es eben nicht

dem ist natürlich nicht so. Du kannst das debuggen.

> nachverfolgen kann.
> *.i Datei habe ich leider keine.

darum habe ich Dir den command line parameter geschickt. Wenn Du uns 
verrätst welchen Compiler Du verwendest können wir die vielleicht auch 
sagen wie Du das in Deiner Umgebung machen kannst.

> Ich versuche es mal mit den {} !

Versuch macht nicht unbedingt klug. Du solltest Dir wirklich mal die 
Ausgabe nach dem Preprozessor anschauen (bei GCC *.i Datei). Dann siehst 
Du auch was der Preprozessor aus Deinen Defines und Makros macht.

Einfach mal die ";" weg zu lassen oder "{}" drum zu setzen ohne zu 
wissen wie sich das auf den Quellcode auswirkt wird nicht viel bringen 
...

von Stefan (Gast)


Lesenswert?

> if (foo) Power_Relay_Index; else bar();
> Zum einen macht hier das doppelte Semicolon probleme, gibt Kompilerfehler.

Deswegen habe ich einen Vorschlag gemacht, der geschweifte Klammern um 
den Block verwendet. Und auch die leere Klammer "()" im Makro Namen 
dient der Vermeidung anderer unerwarteter Probleme.

Aber: Ich glaube unser beider Antworten helfen dem Alexandre nicht bei 
seinem konkreten Problem. Er braucht wohl wirklich den temporären Output 
des pre-compilers.

von Peter D. (peda)


Lesenswert?

Alexandre schrieb:
> Index = 1;
> Power_Relay_Index;
> ausführe, erwarte ich dass er ganz einfach ...

Warum erwartest Du es?
Steht sowas in irgendeinem C-Buch?

C kann keine Namen zerlegen.
Macros können aber Argumente übergeben werden:
1
#define Power_Relay(Index) ...
2
...
3
Index = 1; 
4
Power_Relay( Index );

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Der seit Jahren bewährte Trick, um Makros die aus mehreren Statements 
bestehen, "if-sicher" zu machen, ist nicht das Einschließen in { }, 
sondern:

#define make_this_and_that do { make_this; make_that; } while (0)

OHNE!!! abschließendes Semikolon nach dem while(0)!

von Ingo K. (unseen)


Lesenswert?

Alexandre schrieb:
> aber ...
> er setzt mir in der Reihenfolge:
> A1, A2, A4 und danach A6...

Genau das hast du doch auch hingeschrieben:
1
#define Power_Relay_Index                       if(Index == 0)          \
2
                                                  Power_Relay_1;        \
3
                                                if(Index == 1)          \
4
                                                  Power_Relay_2;        \
5
                                                if(Index == 2)          \
6
                                                  Power_Relay_3;        \
7
                                                if(Index == 3)          \
8
                                                  Power_Relay_4;
wird vom Preprocessor hierzu expandiert:
1
if(Index == 0)
2
  PORTA   |= BIT1;
3
PORTA   |= BIT2;;
4
5
if(Index == 1)
6
  PORTA   |= BIT3;
7
PORTA   |= BIT4;;
8
9
if(Index == 2)
10
  PORTA   |= BIT5;
11
PORTA   |= BIT6;;
12
13
if(Index == 3)
14
  PORTA   |= BIT7;
15
PORTG   |= BIT2;;

Da die Power_Relay_...-Makros zwei Statements enthalten wirkt das if 
immer nur auf den ersten davon.

Ich würde empfehlen für sowas komplett auf Makros zu verzichten und es 
mit Funktionen zu formulieren - wenn das Inlining wichtig ist kann man 
die immer noch als "static inline" in ein Headerfile schreiben und 
vermeidet trotzdem solche Probleme.

von Karl H. (kbuchegg)


Lesenswert?

Es gibt eben eine sinnvolle Grenze, ab der es keinen Sinn mehr macht, 
alles in Makros abzubilen.

Mach Funktionen draus und gut ists. Zuminest aus den 'höherwertigen' 
Makros.

Wenn du noch dafür sorgst, dass die Funktionen geinlined werden, ist das 
Ergebnis genausogut, wie wenn du dich durch die Makros durchgewurschtelt 
hättest und der Optimizer gewütet hat.

von Alexandre (Gast)


Lesenswert?

Der Versuch mit den {} hat es leider nicht gebracht.
@Chris:
Ich verstehe (denke ich zumindest) zwar den Sinn, aber dachte es sollte 
auch ohne do{} gehen.

Da ich in meinen if() KEINE {} habe, sollte es sich nur auf die nächst 
Zeile anwenden, allerdings besteht mein Power_Relay_1 aus 2 Zeilen.


Habe es nun so geändert:

#define Power_Relay_1                   {\
                                        PORTA   |= BIT1;        \
                                        PORTA   |= BIT2;        \
                                        }

und es scheint zu klappen !

Vielen Dank euch allen !

mfg,
   Alexandre

von Alexandre (Gast)


Lesenswert?

Sorry für die anderen Antworten, Ihr seid halt viel schneller als ich.
Unsere Antworten haben sich überschnitten, aber dank euch:
- Habe ich das Problem verstanden

wird vom Preprocessor hierzu expandiert:

if(Index == 0)
  PORTA   |= BIT1;
PORTA   |= BIT2;;

if(Index == 1)
  PORTA   |= BIT3;
PORTA   |= BIT4;;

if(Index == 2)
  PORTA   |= BIT5;
PORTA   |= BIT6;;

if(Index == 3)
  PORTA   |= BIT7;
PORTG   |= BIT2;;

und das mit dem i file muss ich mir auch anschauen, dies hätte es 
einfacher gemacht !
Habe übrigens das IAR Embedded Workbench

DANKE NOCH !
   Alexandre

von Alexandre (Gast)


Lesenswert?

Zu Peter:

Klar kann C keine Namen Zerlegen, ich habe es einfach Index genannt weil 
Lesbar.
Index wird auf 1 gesetzt
Und in der Makro wir Index auch verwendet
Dass die Makro auch xxx_Index heisst, ist nur damit ich weiss dass diese 
"Index" auch intern verwendet.

Danke noch allen,

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Alexandre schrieb:
> Der Versuch mit den {} hat es leider nicht gebracht.
> @Chris:
> Ich verstehe (denke ich zumindest) zwar den Sinn, aber dachte es sollte
> auch ohne do{} gehen.
>
> Habe es nun so geändert:
>
> #define Power_Relay_1                   {\
>                                         PORTA   |= BIT1;        \
>                                         PORTA   |= BIT2;        \
>                                         }
>
> und es scheint zu klappen !

Bitte nicht! Du bringst dich selbst in Teufels Küche!

beispiel:
1
#define my_makro { a = 1; b = 2; }
2
3
if (doit)
4
   my_macro;
5
else
6
   bark();

wird expandiert zu:
1
#define my_makro { a = 1; b = 2; }
2
3
if (doit) { 
4
    a = 1; 
5
    b = 2; 
6
};
7
else
8
   bark();

merkst du was?

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


Lesenswert?

Alexandre schrieb:
> Habe übrigens das IAR Embedded Workbench

Der kann man auch irgendwo sagen, dass sie die nach dem
Präprozessorschritt entstehende Datei extra ablegen soll.

von Maxx (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Alexandre schrieb:
>> Habe übrigens das IAR Embedded Workbench
>
> Der kann man auch irgendwo sagen, dass sie die nach dem
> Präprozessorschritt entstehende Datei extra ablegen soll.

Project Options -> C/C++ Compiler -> Tab "Preprocessor" -> Checkbox 
"Preprocessor output to file"

von Peter D. (peda)


Lesenswert?

Alexandre schrieb:
> Klar kann C keine Namen Zerlegen, ich habe es einfach Index genannt weil
> Lesbar.
> Index wird auf 1 gesetzt
> Und in der Makro wir Index auch verwendet

Ja, ist mir später auch aufgefallen.

Es ist für mich total ungewöhnlich, daß eine Funktion einen Parameter in 
einer extra Variablen übergeben kriegt.
Sobald sie von einer Variablen im gleichen Kontext abhängt, übergebe ich 
diese immer mit als Argument.
Damit wird dann der Zusammenhang deutlicher und es können weniger Fehler 
passieren.

von Wolfgang H. (frickelkram)


Lesenswert?

Hi,

Alexandre schrieb:
> Der Versuch mit den {} hat es leider nicht gebracht.

ohne jetzt hämisch sein zu wollen ... hatte ich das nicht schon 
vermutet?

> @Chris:
> Ich verstehe (denke ich zumindest) zwar den Sinn, aber dachte es sollte
...
>
> mfg,
>    Alexandre

Ich möchte Dir noch einmal empfehlen den Sourcecode nach dem 
Preprozessor anzuschauen und zu lernen was der Preprozessor aus Deinen 
Macros macht. Dann liest Du Dir noch einmal die Hinweise der anderen 
durch und wird erkennen warum bestimmte Dinge so und nicht anders macht 
und wo Dein Konzept eventuell krankt.

von PittyJ (Gast)


Lesenswert?

Nimm richtige Funktionen und lass den Compiler für dich optimieren.

#defines und Makros führen nur zu Problemen, insbesondere wenn man 
Codefragmente damit abbilden will.
Ich benutze schon seit Jahren keine Makros mehr. Der Code macht das was 
er soll und es gibt keine Überraschungen.

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


Lesenswert?

Wolfgang Heinemann schrieb:
> und wird erkennen warum bestimmte Dinge so und nicht anders macht und wo
> Dein Konzept eventuell krankt.

Und vielleicht fängst du ja dann doch auch einfach mal an, statt
des Makrogewurschtels kleine Funktionen zu nehmen.

Deklarier' sie “static”.  Wenn der Compiler dann sieht, dass du sie
nur einmal benutzt, inlinet er sie ohnehin automatisch.  Oder mach'
sie gleich “static inline”.

Dann hast du syntaktisch eine Funktion, mit einem ordentlichen (und
typenreinen) Parameter wenn nötig, mit einem ordentlich in {}
gefassten Rumpf.

(Edit: PittyJ war schneller. ;)

von MarcVonWindscooting (Gast)


Lesenswert?

PittyJ schrieb:
> #defines und Makros führen nur zu Problemen, insbesondere wenn man
> Codefragmente damit abbilden will.

Jawohl! Und man denke auch mal dran dass enum (gerne auch anonym) 
ebenfalls jede Menge dieses #define-Unsinns vermeiden kann. Es sind 
nicht nur die Funktionen.

von Wolfgang H. (frickelkram)


Lesenswert?

In den 1980er Jahren gab es mal eine Routersoftware. Die war komplett in 
Assembler geschrieben. Die Software war dazu gedacht auf mickrige PC zu 
setzen und für bestimmte Netzwerkkarten entwickelt worden.

Das gesamte Teil war mit mehreren MACRO-Layern geschrieben. Die 
Konfiguration für die Netzwerkkarten war über defines gelöst.
Das Ding hat funktioniert, aber verstehen konnte man den gesamten Code 
nicht, ich habe es jedenfalls nicht geschafft ...

Leider habe ich den Namen von der Software vergessen ... ist lange her.
Macros nutze ich schon ab und zu aber doch recht sparsam. Macros sind 
kein Teufelswerk sondern ab und zu schon ganz nützlich. Manchmal kann 
man damit etwas lesbarer gestalten ohne eine Funktion bauen zu müssen.

Ich halte übrigens den Hinweis auf enum für sinnvoll. Mit enums erhöhst 
Du die Lesbarkeit und der Compiler kann Dich weiter bei der Vermeidung 
von Fehlern unterstützen. Bei vielen defines interessiert der 
eigentliche Wert ja nicht.

von Alexandre (Gast)


Lesenswert?

Hallo zusammen,

War leider bis jetzht in einer Besprechung, und bin Morgen an einer 
Ausstellung, nicht dass ihr meint es sei mir nun alles Wurst.

Aber ich denke Freitag sollte ich mich wieder daran machen können, und 
es auch so ändern dass es sauber ist.

Vielen Dank,
   Alexandre

P.S.: Tolles Forum, und soooooooo schnell !!!

von Alexandre (Gast)


Lesenswert?

Hallo zusammen,

Also irgendwie ist mein Freitag-post verloren gegangen.
Ich habe das ganze also über inline gemacht.
Nun hab ich auch ein gutes Gewissen.
Das mit dem *i file ist nun auch eingerichtet, somit ich das ganze auch 
genauer anschauen kann.

Vielen Dank noch,
   mfg,
   Alexandre

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.