Forum: Mikrocontroller und Digitale Elektronik Prüfen ob zwei Makros (Ports) gleich sind


von Unlucky2012 (Gast)


Lesenswert?

Hi,

ich möchte gerne abhängig davon, ob zwei Makros gleich sind, 
unterschiedlichen Code generieren. Meine bisherigen Versuche sehen in 
etwa so aus:
1
#define LED1_DDR DDRD
2
#define LED2_DDR DDRD
3
#if LED1_DDR == LED2_DDR
4
  // Code
5
#endif

Leider wird das mit einer Fehlermeldung quittiert, die da lautet: 
"error: operator '*' has no left operand".

Interessanterweise liegt das wohl daran, dass DDRD selbst ein Makro ist. 
Ersetze ich nämlich DDRD durch z.B. jeweils "1", dann funktioniert es 
tadellos.

Kennt ihr das Problem bzw. kennt ihr eine Möglichkeit es zu umgehen?

Vielen Dank!

von Achim M. (minifloat)


Lesenswert?

Unlucky2012 schrieb:
> #if LED1_DDR == LED2_DDR
>   // Code
> #endif

Da spielt dir der Präprozessor einen Streich. Wann wird io.h 
eingebunden? ist das mmcu-Makro für den Compileraufruf korrekt gesetzt?

Unsaubere Lösung:
Man könnte das in Hochsprachen-C mit einem leeren if-Block versuchen. 
Das if würde ausgewertet und der Compiler und nicht der Präprozessor 
erkennt, dass sie gleich sind. Das geht aber nur gut, wenn es dir um 
Ausgabe einer Warnung geht.

mfg mf

von Klaus W. (mfgkw)


Lesenswert?

Es geht schon:
1
    if( &LED1_DDR == &LED2_DDR )
2
    {
3
       ...
4
    }
5
    else
6
    {
7
       ...
8
    }

Damit ist zwar theoretisch erstmal jede Codevariante im Programm 
enthalten, obwohl natürlich nur einer der Zweige jemals ausgeführt wird.
Weil das if aber auf einem konstanten Ausdruck besteht, ist jeder 
halbwegs aktuelle Compiler in der Lage, die überflüssige Hälfte 
wegzuoptimieren.

von Unlucky2012 (Gast)


Lesenswert?

Mini Float schrieb:
> Da spielt dir der Präprozessor einen Streich. Wann wird io.h
> eingebunden? ist das mmcu-Makro für den Compileraufruf korrekt gesetzt?

io.h wird als erste Datei überhaupt eingebunden. mmcu Makro wird von 
meiner Umgebung (Eclipse mit AVR-Plugin) korrekt gesetzt.

Klaus Wachtler schrieb:
> Weil das if aber auf einem konstanten Ausdruck besteht, ist jeder
> halbwegs aktuelle Compiler in der Lage, die überflüssige Hälfte
> wegzuoptimieren.
Ja, nur würde ich das schon ganz gerne über den Präprozessor lösen. Dann 
sieht es auch ein menschlicher Leser. Außerdem ist es dann nicht von der 
Optimierungsstufe abhängig.

Gibt es denn ansonsten keine "vernünftige" Lösung?

von Roland H. (batchman)


Lesenswert?

Unlucky2012 schrieb:
> Gibt es denn ansonsten keine "vernünftige" Lösung?

Ich mache es so:
1
#define CONCAT2x(a, b) a##b
2
#define CONCAT2(a, b) CONCAT2x(a, b)
3
4
// Config
5
6
#define PORT1 A
7
#define PORT2 A
8
9
// HW access
10
11
#define DDR_PORT1 CONCAT2(DDR, PORT1)
12
#define DDR_PORT2 CONCAT2(DDR, PORT2)
13
14
#if PORT1 == PORT2
15
 ...
16
#else
17
 ...
18
#endif

D. h. beim Vergleich kommt nichts "externes" ins Spiel, was den 
Vergleich stört. Es ist auch eine Trennung zwischen "Konfiguration" und 
"HW-Zugriff", was eine eventuelle Portierung auf eine andere Architektur 
erleichtert.

von (prx) A. K. (prx)


Lesenswert?

Hast du das mal ausprobiert? Es hat nämlich einen Haken: Es funktioniert 
nicht. Wenn weder A noch B definiert sind, dann ist
  #if A == B
nämlich ein gültiger Präprozessor-Ausdruck, und mit dem exakt gleichen 
Ergebnis wie
  #if A == A

Der Ausdruck in einem #if Statement wird numerisch ausgewertet, nicht 
als String. Ein nicht definiertes Symbol ist dabei, anders als in C 
selbst, kein Fehler, sondern steht für 0. Somit ist
  #if A == B
eigentlich
  #if 0 == 0

von (prx) A. K. (prx)


Lesenswert?

Und zur ursprünglichen Frage: Mit dem Präpozessor ist das nur möglich, 
wenn die Makros einfache numerische Ausdrücke sind. Was auf Ports wie 
DDRA nicht zutrifft. Folglich ist das mit dem Präprozessor in dieser 
Form nicht möglich.

von (prx) A. K. (prx)


Lesenswert?

Rolands Variante funktioniert, wenn man sich die Mühe macht, die Ports 
vergleichbar zu 8051 umzubenennen, also 1,2,3 statt A,B,C. Also alle 
Register von PORT A konsequent mit #defines versieht, die daraus PORT1, 
PIN1 und DDR1 machen:
1
#define P1 1
2
#define P2 2
3
4
#if P1 == P2
5
 #error 1
6
#else
7
 #error 2
8
#endif
9
10
#define CONCAT2x(a, b) a##b
11
#define CONCAT2(a, b) CONCAT2x(a, b)
12
13
#define DDR1 DDRA
14
#define DDR2 DDRB
15
16
#define DDR_PORT1 CONCAT2(DDR, P1)
17
#define DDR_PORT2 CONCAT2(DDR, P2)
Aber ob man dann selber nach einiger Zeit noch durchblickt?

von Roland H. (batchman)


Lesenswert?

A. K. schrieb:
> Hast du das mal ausprobiert? Es hat nämlich einen Haken: Es funktioniert
> nicht

Erwischt. Das kommt vom Zusammenschustern, und die Grundlage verglich 
numerische Werte (da ging es um Doppelbelegung eines Timers).

> Rolands Variante funktioniert, wenn man sich die Mühe macht, die Ports
> vergleichbar zu 8051 umzubenennen, also 1,2,3 statt A,B,C.

Ich hab's mir bei den LPCs abgeschaut ;-)

In der Tat verwende ich für AVR/STM32/PIC32 GPIO eine ähnliche Variante, 
nämlich zusätzlich zu A, B, C noch 0, 1, 2 in der Konfiguration zu 
verwenden. Das erste findet Verwendung in Makros, die numerische 
Variante für "parametrisiertes" GPIO.

LPC/RX/MSP430 verwenden numerische Port-IDs, dort ist Port-Name = 
Port-ID.

A. K. schrieb:
> Aber ob man dann selber nach einiger Zeit noch durchblickt?

Das geht schon. Es nervt etwas, jeden GPIO-Pin mit drei Zeilen zu 
beschreiben:
1
#define DEFAULT_LED_RED_0_PORT_NAME    B
2
#define DEFAULT_LED_RED_0_PORT_ID      1
3
#define DEFAULT_LED_RED_0_PIN          5

Ich weiß nicht, ob man von der Zahl 1 auf den String B mittels 
Präprozessor schließen kann, dann könnte ich mir die erste Zeile sparen.

bzw.
1
#define DEFAULT_LED_RED_0_PORT_NAME 2
2
#define DEFAULT_LED_RED_0_PORT_ID   2
3
#define DEFAULT_LED_RED_0_PIN       4

So passt das für alle Plattformen, den Rest erledigen Makros (zugegeben 
teilweise mit wilden CONCATs) oder Funktionen. Die Komplexität liegt 
aber weder in der Konfiguration noch im Aufruf.

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.