Forum: Compiler & IDEs Präprozessorabfrage


von grundschüler (Gast)


Lesenswert?

ich habe folgenden code:
1
#define dsz_port    D
2
#define dsz_pin     5
3
#define dsz_int_port     2
4
5
6
7
8
#ifdef PORTA //m644
9
10
#else //m328
11
12
#if dsz_port==B
13
#define dsz_vect   PCINT0_vect
14
15
test //gewollte Fehlermeldung wenn diese Stelle erreicht wird
16
#endif
17
18
#endif

ich will mittels glue das PCINT-Register je nach Port-Register 
automatisch zuweisen.

Die Abfrage '#if dsz_port==B' funktioniert jedoch nicht. Obwohl 
dsz_port==D  ist, wird  '#if dsz_port==B' angesprungen.

Was mache ich falsch?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wo ist D definiert, und wo B?

von grundschüler (Gast)


Lesenswert?

gute Frage. D ist der Buchstabe D. Er wird nur für den GLUE-Macro 
benötigt

#define PORT(x)         GLUE(PORT, x)
#define PIN(x)          GLUE(PIN, x)
#define DDR(x)          GLUE(DDR, x)

und müsste eigentlich den ASCII-Wert 69 darstellen.

von Theor (Gast)


Lesenswert?

Zeichenkonstanten werden mit Apostrophen umgeben. Also etwa 'A', 'B', 
'1' usw.

von grundschüler (Gast)


Lesenswert?

das hatte ich probiert.


#if dsz_port=='B'
#define dsz_vect   PCINT0_vect
#endif
#if dsz_port=='C'
#define dsz_vect   PCINT1_vect
#endif
#if dsz_port=='D'
#define dsz_vect   PCINT2_vect
#endif


geht auch nicht.

von lalala (Gast)


Lesenswert?

grundschüler schrieb:
> das hatte ich probiert.
>
> #if dsz_port=='B'
> #define dsz_vect   PCINT0_vect
> #endif
> #if dsz_port=='C'
> #define dsz_vect   PCINT1_vect
> #endif
> #if dsz_port=='D'
> #define dsz_vect   PCINT2_vect
> #endif
>
> geht auch nicht.

sollte aber. Wie testest du, ob es funktioniert?

von Carl D. (jcw2)


Lesenswert?

> was mach ich falsch?
1. den AVR erkennt man nicht am Vorgandensein von PORTA, denn den hat 
auch ein Tiny24
2.
1
#if    defined( __AVR_ATmega644__ )
2
  #define DSZ_VECT   PCINT0_vect
3
  #define DSZ_PORT   PORTA
4
  #define DSZ_PIN    PINA
5
  #define DSZ_DDR    DDRA
6
  #define DSZ_BIT    5
7
#elif  defined( __AVR_ATmega328__ )
8
  #define DSZ_VECT   PCINT0_vect
9
  #define DSZ_PORT   PORTB
10
  #define DSZ_PIN    PINB
11
  #define DSZ_DDR    DDRB
12
  #define DSZ_BIT    1
13
#ekse
14
  #error device Not supported (yet)
15
#endif
(die konkreten Werte sind mal so aus dem Höhlen Bauch)

Und als Tip (passend zum Nick):
Viel fremden Code anschauen. Die Anderen machen nicht alles falsch.

: Bearbeitet durch User
von grundschüler (Gast)


Lesenswert?

Carl D. schrieb:
> den AVR erkennt man nicht am Vorgandensein von PORTA,

der AVR interessiert nicht. Wenn es PORTA gibt, ist die Zuordnung

PORTA => PCINT0 etc.

sonst um eins versetzt:

PORTB => PCINT0 etc.

Es geht mir im Prinzip nur darum, die Auswahl
#define dsz_int_port     2
einzusparen und in Abhängigkeit von

#define dsz_port    D
zu bringen.

Scheint nicht so einfach zu sein.



lalala schrieb:
> sollte aber. Wie testest du, ob es funktioniert?

ich setze einen Fehler in die betreffende Auswahl und schaue, ob es eine 
Fehlermeldung gibt oder ob der Fehler übersprungen wird.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

grundschüler schrieb:
> das hatte ich probiert.
>
> #if dsz_port=='B'

Und wie hast Du dsz_port definiert? Da musst Du natürlich auch 
Hochkommata verwenden. Sonst wird das nichts.

D (ohne Hochkommata) ist für den Präprozessor undefiniert (es sei denn, 
Du hast ein #define D irgendwas irgendwo davor stehen).

'D' (mit Hochkommata) ist hingegen eine Konstante.

von grundschüler (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Du hast ein #define D irgendwas irgendwo davor stehen)

Nein. der Buchstabe D wird mittels GLUE mit der Buchstabenfolge 
PIN,PORT,DDR verbunden. PIN ist für sich nix - zusammen mit D findet es 
sich als Definition in io.h. mit 'D' würde GLUE nicht funktionieren. 
Wahrscheinlich ist das die Besonderheit von GLUE, dass nix mit nix 
verbunden wird und dann etwas ergibt. Nix ist dann aber für einen 
Macro-Vergleich ungeeignet.


1
#if dsz_int_port==0
2
#define dsz_vect   PCINT0_vect
3
#endif
4
#if dsz_int_port==1
5
#define dsz_vect   PCINT1_vect
6
#endif
7
#if dsz_int_port==2
8
#define dsz_vect   PCINT2_vect
9
#endif
10
#if dsz_int_port==3
11
#define dsz_vect   PCINT3_vect
12
#endif
13
14
#define PCMSK(x)    GLUE(PCMSK, x)
15
#define PCIE(x)      GLUE(PCIE, x)
16
17
18
19
20
//-----------------------------------
21
dsz_init()
22
{
23
DDR(dsz_port)&=~(1<< dsz_pin);
24
//PORT(dsz_port)|=((1<< dsz_pin));
25
PCICR |= (1 << PCIE(dsz_int_port));//PCIntControlRegister
26
PCMSK(dsz_int_port) |= (1<< dsz_pin);
27
sei();
28
}
29
30
//--------------------------------------------------
31
32
ISR(dsz_vect){
33
dsz_isr_imp++;
34
//if (PIN(dsz_port)&(1<<dsz_pin)) led1_on;else led1_off;
35
}
36
37
//--------------------------------------------------
38
39
isr_dsz_start(){
40
PCMSK(dsz_int_port) |= (1 << dsz_pin);
41
42
}
43
44
//--------------------------------------------------
45
46
isr_dsz_stop(){
47
PCMSK(dsz_int_port) &=~(1 << dsz_pin);
48
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

grundschüler schrieb:
> Nein. der Buchstabe D wird mittels GLUE mit der Buchstabenfolge
> PIN,PORT,DDR verbunden.

Nicht, wenn Du

#if bla===D

schreibst. Da ist kein "GLUE".

von grundschüler (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> grundschüler schrieb:
>> Nein. der Buchstabe D wird mittels GLUE mit der Buchstabenfolge
>> PIN,PORT,DDR verbunden.
>
> Nicht, wenn Du
>
> #if bla==D
>
> schreibst. Da ist kein "GLUE".

Deswegen scheint es nicht so zu funktionieren, wie ich mir das gedacht 
hatte.

Das merkwürdige ist, dass es bei der Verbindung von Ziffern im Vergleich
#if bla==2 und mit GLUE funktioniert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ziffern sind Konstanten. Bezeichner (Dein "D") nicht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

lalala schrieb:
> grundschüler schrieb:
>> das hatte ich probiert.
>>
>> #if dsz_port=='B'
>> #define dsz_vect   PCINT0_vect
>> #endif
>> #if dsz_port=='C'
>> #define dsz_vect   PCINT1_vect
>> #endif
>> #if dsz_port=='D'
>> #define dsz_vect   PCINT2_vect
>> #endif
>>
>> geht auch nicht.
>
> sollte aber.

Nein, sollte nicht.

Der Präprozessor kann nur numerische Werte vergleichen, keine Strings.

von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
>
> Der Präprozessor kann nur numerische Werte vergleichen, keine Strings.

Und undefinierte Macros haben den numerischen Wert 0

von lalala (Gast)


Lesenswert?

Johann L. schrieb:
> lalala schrieb:
>> sollte aber.
>
> Nein, sollte nicht.
>
> Der Präprozessor kann nur numerische Werte vergleichen, keine Strings.

'B', 'C', 'D', etc. sind in C ja auch keine Strings. Ist das im 
Präprozessor anders?

Ich habe jetzt aber mal ein paar Codeschnipsel durch den GCC gejagt:
1
//#define TEST A //Option 1
2
#define TEST 'A' //Option 2
3
#if TEST=='A'
4
#warning TEST == 'A'
5
#else
6
#if TEST == A
7
#warning TEST == A
8
#else
9
#warning TEST ist was anderes
10
#endif
11
#endif

Mit Option 1: TEST == A
Mit Option 2: TEST == 'A'

Außerdem habe ich mal geraten, was der TO erreichen will und etwas in 
die Richtung gebaut:
1
#define GLUE(X, Y) X ## Y
2
//#define TEST2A B  //Option 1
3
#define TEST2A 'B'  //Option 2
4
#define TEST2(X) GLUE(PORT, X)
5
#if TEST2(A)=='B'
6
#warning TEST2A == 'B'
7
#else
8
#if TEST2(A) == B
9
#warning TEST2A == B
10
#else
11
#warning TEST2A ist was anderes
12
#endif
13
#endif

Bei beiden Optionen ist das Ergebnis: TEST2A==B
Das hatte ich ehrlich gesagt nicht so erwartet. Falls ich einen Fehler 
im Code habe, bitte melden :)

von Walter S. (avatar)


Lesenswert?

lalala schrieb:
> Ich habe jetzt aber mal ein paar Codeschnipsel durch den GCC
> gejagt://#define TEST A //Option 1
> #define TEST 'A' //Option 2
> #if TEST=='A'
> #warning TEST == 'A'
> #else
> #if TEST == A
> #warning TEST == A
> #else
> #warning TEST ist was anderes
> #endif
> #endif
> Mit Option 1: TEST == A
> Mit Option 2: TEST == 'A'

wenn Du
#if TEST == Z
schreibst kommt übrigens genauso
TEST == A
raus

von PittyJ (Gast)


Lesenswert?

Der Thread bestärkt meine Meinung, dass Anfänger nicht mit Macros 
arbeiten sollten. Normaler C-Code würde das Problem auch lösen. Das 
sieht villeicht nicht so elegant aus, dafür ist es für jeden zu 
verstehen.

Anfänger: lasst einfach die Finger von #. Das einzige, was ihr braucht 
ist #include.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das funktioniert nicht, wenn man Code schreiben möchte, den man für 
unterschiedliche AVRs übersetzen können möchte. Wenn der eine bestimmte 
Register nicht hat, kann der Code für diesen Controller gar nicht erst 
übersetzt werden; da ist bedingte (d.h. per #if / #ifdef gesteuerte) 
Compilation zwingend erforderlich.

Obendrein würde eine Fallunterscheidung zur Laufzeit viel überflüssigen 
Code erzeugen.

Der Präprozessor ist ein wichtiges Werkzeug; ihn verstehen und anwenden 
können gehört dazu.

Man muss allerdings auch nicht ums Verrecken alles mit dem Präprozessor 
erledigen.

Ist es wirklich nötig, Registernamen mit viel MagicFoo(tm) vom 
Präprozessor zusammenbasteln zu lassen, nur weil man tippfaul ist?

von grundschüler (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Ist es wirklich nötig, Registernamen mit viel MagicFoo(tm) vom
> Präprozessor zusammenbasteln zu lassen, nur weil man tippfaul ist?


Alles was der Präprozessor automatisiert erledigt, erzeugt keine Fehler. 
Wenn ich den Pin wechsele, brauche ich nur die Defines
#define dsz_port         D
#define dsz_pin          5
#define dsz_int_port     2

richtig zu machen. Der ganze PCINT-Code wird dann automatisch richtig 
angepasst.  Ohne Macros muss man den Code an vielen Stellen ändern. 
Dadurch ergeben sich Fehlermöglichkeiten.

Das ist nicht nur Tippfaulheit sondern auch Fehlervermeidung.

Ich könnte das noch reduzieren indem ich nur

#define dsz_pin          5
#define dsz_int_port     2

definiere oder die Durchnummerierung von Arduino verwende:

#if dsz_int_port ==   0
#define dsz_port   B
#endif
#if dsz_int_port ==   1
#define dsz_port   C
#endif
#if dsz_int_port ==   2
#define dsz_port   D
#endif

Allerdings möchte ich den D5-Pin auch so bezeichnen, weil das bei AVRs 
geläufig ist. Man weiß dann gleich welcher Pin gemeint ist.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

grundschüler schrieb:
> #define dsz_port         D

Genau da liegt halt das Problem.

Hätte D einen Wert, könntest Du es auch inhaltlich (d.h. mit 
Vergleichsoperationen) auswerten. Es hat aber keinen, es ist nur ein 
symbolischer Name, und damit kannst Du außer mit Deiner "glue logic" 
nichts weiter anfangen.

von grundschüler (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> nichts weiter anfangen.

ja doch:

#define pinSET(x,y)     PORT(x) |=(1<<y)
#define pinCLR(x,y)     PORT(x)&=~(1<<y)
#define pinTOG(x,y)     PORT(x)^=(1<<y)
#define pinVAL(x,y)     (PIN(x)&(1<<y))
#define pinDIRin(x,y)     DDR(x)&=~(1<<y)
#define pinDIRout(x,y)     DDR(x)|=(1<<y)
#define pinPULLUP(x,y,z) 
(z)?(PORT(x)|=(1<<y)):(PORT(x)&=~(1<<y))//0_no

wenn dsz_port einmal definiert ist, kann man da viel mit machen.

von Kaj (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> da ist bedingte (d.h. per #if / #ifdef gesteuerte)
> Compilation zwingend erforderlich.
>
> Obendrein würde eine Fallunterscheidung zur Laufzeit viel überflüssigen
> Code erzeugen.
Oder man macht es wie im "GNU Coding Standards, Chapter 3.5"
https://www.gnu.org/prep/standards/standards.html#Conditional-Compilation
1
3.5 Conditional Compilation
2
3
When supporting configuration options already known when building your
4
program we prefer using if (... ) over conditional compilation, as in
5
the former case the compiler is able to perform more extensive checking
6
of all possible code paths.
7
8
For example, please write
9
10
  if (HAS_FOO)
11
    ...
12
  else
13
    ...
14
15
instead of:
16
17
  #ifdef HAS_FOO
18
    ...
19
  #else
20
    ...
21
  #endif
22
23
A modern compiler such as GCC will generate exactly the same code in
24
both cases, and we have been using similar techniques with good success
25
in several projects. Of course, the former method assumes that HAS_FOO
26
is defined as either 0 or 1.
27
28
While this is not a silver bullet solving all portability problems, and
29
is not always appropriate, following this policy would have saved GCC
30
developers many hours, or even days, per year.

Hat mit dem konkreten Problem wenig zu tun, es sei aber trotzdem mal 
angemerkt im Zuge der "ohne geht es nicht"-Behauptung ;)

von Carl D. (jcw2)


Lesenswert?

Alles benötigte kann die Sprache eines gewissen Bjarne auch ohne #.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Kaj schrieb:
> es sei aber trotzdem mal angemerkt im Zuge der "ohne geht es
> nicht"-Behauptung

Das geht nur dann, wenn sämtliche symbolische Namen, die hier verwendet 
werden, immer bekannt sind.

Gerade das aber ist im AVR-Umfeld nicht gegeben; für unterschiedliche 
AVRs werden unterschiedliche Headerdateien eingebunden, die 
unterschiedliche Peripherieregister definieren.

Versucht man nun, Code zu schreiben, der beispielsweise eine zweite, nur 
bei einem bestimmten AVR-Typ vorhandene UART ansteuert, fliegt der 
Compiler auf die Schnauze, wenn das für einen anderen AVR-Typ übersetzt 
werden soll, weil die symbolischen Namen für die Register der zweiten 
UART undefiniert sind.

Da muss dann der die zweite UART ansteuernde Code mit bedingter 
Compilation, d.h. durch #if / #ifdef auskommentiert werden.

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.