Forum: Compiler & IDEs Probleme mit dem Preprozessor. Er verrechnet sich! Oder mach ich was falsch?


von Leo B. (luigi)


Lesenswert?

Servus beisammen,

Folgender Code baut offensichtlich ziemlich Mist und ich finde nicht 
heraus warum:
1
#define F_PROG  39
2
#define T0_PSC  1024
3
4
#define W_OPOS  0b11100000
5
#define W_POW  0b11010000
6
7
#ifndef F_CPU
8
#define F_CPU  10000000
9
#endif
10
11
12
13
// Berechnungen
14
#define F_T0    (F_CPU/T0_PSC)
15
#define TIMER0_TOP  (2*F_T0/F_PROG+1)/2        // clever runden
16
#define F_PROG_REAL  (F_CPU/TIMER0_TOP/T0_PSC)        // Reale Programmfrequenz
17
#define F_PROG_ERR  ((F_PROG_REAL*1000)/F_PROG-1000) // Fehler in Promille
18
#if (TIMER0_TOP > 255)
19
  #error "Programmfrequenz kann mit dem Prescaler von Timer0 nicht eingestellt werden!"
20
#endif
21
#if ((F_PROG_ERR>100) || (F_PROG_ERR<-100))      // max. +/-100 Promille Fehler
22
  #error "Systematischer Fehler der Programmfrequenz grösser 10 Prozent und damit zu hoch!"
23
24
25
26
27
// Ab hier nurnoch Debugausgabe
28
#define TEST    (F_CPU/TIMER0_TOP)
29
#endif
30
#if (F_PROG==39)
31
  #error "F_PROG==39"
32
#endif
33
#if (F_T0==9765)
34
  #error "F_T0==9765"
35
#endif
36
#if (TIMER0_TOP==250)
37
  #error "TIMER0_TOP==250"
38
#endif
39
#if (F_PROG_REAL==9)
40
  #error "F_PROG_REAL"
41
#endif
42
#if (F_PROG_ERR==-770)
43
  #error "F_PROG_ERR==-770"
44
#endif
45
#if (TEST==9980)
46
  #error "TEST==9980"
47
#endif
Ich erhalte dabei folgende Fehler:
1
../def_ini.h:44:4: error: #error "Systematischer Fehler der Programmfrequenz grösser 10 Prozent und damit zu hoch!"
2
../def_ini.h:47:4: error: #error "F_PROG==39"
3
../def_ini.h:50:4: error: #error "F_T0==9765"
4
../def_ini.h:53:4: error: #error "TIMER0_TOP==250"
5
../def_ini.h:56:4: error: #error "F_PROG_REAL==9"
6
../def_ini.h:59:4: error: #error "F_PROG_ERR==-770"
7
../def_ini.h:62:4: error: #error "TEST==9980"

Mach ich was falsch?
Wo könnte der Fehler liegen?

Danke schonmal euch allen
lg Leo

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


Lesenswert?

Leo B. schrieb:
> #define TIMER0_TOP  (2*F_T0/F_PROG+1)/2        // clever runden

Da muss in der Ersetzung noch ein Klammerpaar drumrum.
Präprozessor berechnet ja an dieser Stelle keinen Wert, sondern er
setzt nur sukzessive alle rechten Seiten der Makros ein, und da
kommt ohne Klammer dann Mist raus.

Ich habe aber auch erst ein
1
avr-gcc -dM -E foo.c | sort | less

gebraucht, um das zu begreifen. ;-)

von Leo B. (luigi)


Lesenswert?

OK, das verstehe ich jetzt nicht ganz...
Soweit mir bekannt kennt der Preprozessor die "Punkt vor Strich"-Regel. 
Trotzdem funktioniert das Ganze mit Klammern da drum. Ich kanns nicht 
ganz nachvollziehen aber es klappt...

Danke Danke!

Und den Weg wie du da drauf gekommen bist muss ich mir morgen aneignen 
^^
Das " | sort | less" scheint mir noch recht interessant zu sein.

Danke nochmal!

von Klaus Falser (Gast)


Lesenswert?

Leo B. schrieb:
> #ifndef F_CPU
> #define F_CPU  10000000
> #endif

Sollte das ganze für den AVR sein, dann kann es noch zzsätzliche 
Probleme geben, weil 10000000 nicht in einer 16 bit Integer Zahl platz 
hat.
Dann muss man 10000000UL verwenden.

von Peter II (Gast)


Lesenswert?

Klaus Falser schrieb:
> Sollte das ganze für den AVR sein, dann kann es noch zzsätzliche
> Probleme geben, weil 10000000 nicht in einer 16 bit Integer Zahl platz
> hat.
der Preprozessor weiss aber nicht das es ein AVR ist. Es kommt darauf an 
was später mit F_CPU  gemacht wird. Ich glaube nicht das diese Zahl in 
einer Variabel vom Typ int gespeichert wird - warum auch?

von (prx) A. K. (prx)


Lesenswert?

Leo B. schrieb:

> OK, das verstehe ich jetzt nicht ganz...

Der Präprozessor rechnet erst im #if. Davor macht er nur 
Textersetzungen. Aus
1
#define TIMER0_TOP  (2*F_T0/F_PROG+1)/2
2
#define F_PROG_REAL (F_CPU/TIMER0_TOP/T0_PSC)
wird daher (vereinfacht)
1
#define F_PROG_REAL (F_CPU/(2*F_T0/F_PROG+1)/2/T0_PSC)
und das ist nicht eben nicht das Gleiche wie
1
#define F_PROG_REAL (F_CPU/((2*F_T0/F_PROG+1)/2)/T0_PSC)

Daraus leitet sich die Daumenregel ab, dass man Rechenausdrücke in 
#define immer einklammern sollte. Man weiss nie was sonst draus wird.

von Klaus Falser (Gast)


Lesenswert?

Peter II schrieb:
> Klaus Falser schrieb:
>> Sollte das ganze für den AVR sein, dann kann es noch zzsätzliche
>> Probleme geben, weil 10000000 nicht in einer 16 bit Integer Zahl platz
>> hat.
> der Preprozessor weiss aber nicht das es ein AVR ist. Es kommt darauf an
> was später mit F_CPU  gemacht wird. Ich glaube nicht das diese Zahl in
> einer Variabel vom Typ int gespeichert wird - warum auch?

Siehe oben.
Der Präprocessor rechnet nicht. Am Ende kommt eine Wurst von Anweisungen 
heraus, und diese wird vom Compiler nach den C Regeln aufgelöst.
Und wenn dort steht (10000000/8) dann wird beim AVR die 10000000 zuerst 
auf 16 bit integer gekürzt und dann (falsch) durch 8 dividiert.

von (prx) A. K. (prx)


Lesenswert?

Für ähnliche Überraschungen ist übrigens auch dieser Code gut:
1
#define TRACE(x) if (trace) printf(x)
2
3
if (a > 100)
4
  TRACE("Sollte nicht vorkommen");
5
else 
6
  printf("Hallo");
weil sich das dem Compiler tatsächlich als
1
if (a > 100)
2
  if (trace) 
3
    printf(x);
4
  else 
5
    printf("Hallo");
darstellt.

Schreibt man statt dessen
1
#define TRACE(x) if (trace) { printf(x); }
dann kriegt man de fakto
1
if (a > 100)
2
  if (trace) { printf(x); }; 
3
else 
4
  printf("Hallo");
und damit einen Syntax Error.

Aus diesem Grund klammert man das gern als
1
#define TRACE(x) do{ if (trace) printf(x); }while(0)

von (prx) A. K. (prx)


Lesenswert?

Klaus Falser schrieb:

> Der Präprocessor rechnet nicht.

An exakt einer Stelle tut er es doch: im #if Statement.

von Stefan E. (sternst)


Lesenswert?

Klaus Falser schrieb:
> Und wenn dort steht (10000000/8) dann wird beim AVR die 10000000 zuerst
> auf 16 bit integer gekürzt und dann (falsch) durch 8 dividiert.

Nein.
10000000 hat den Typ long, und auch die Division wird in long gemacht.

von Klaus F. (kfalser)


Lesenswert?

Stefan Ernst schrieb:
> Klaus Falser schrieb:
>> Und wenn dort steht (10000000/8) dann wird beim AVR die 10000000 zuerst
>> auf 16 bit integer gekürzt und dann (falsch) durch 8 dividiert.
>
> Nein.
> 10000000 hat den Typ long, und auch die Division wird in long gemacht.

Nicht so viel ich weiß.

10000000L ist eine Konstante vom Type long.
10000000  ohne L ist eine Konstante vom Type integer. Auf 32 bit 
Rechnern wird sie als integer darstellbar sein, auf 16 bit Rechnern 
nicht.
C spezifiziert 16 Bit als minimum für integer, deshalb ist auch auf 8 
Bit Rechnern ein int 16 bit lang.

von (prx) A. K. (prx)


Lesenswert?

Klaus Falser schrieb:

> 10000000L ist eine Konstante vom Type long.
> 10000000  ohne L ist eine Konstante vom Type integer.

Lexikalische Konstanten ohne Suffix sind vom kleinsten Typ in den sie 
problemlos reinpassen. Anfangend bei "int", bei hex/oct weiter mit 
"unsigned", dann "long", etc. Daher hängt es von der Zielmaschine ab, ob 
10000000 "int" oder "long" ist.

Das bedeutet übrigens auch, dass auf einer 16-Bit Zielmaschine 32768 
"long" ist, das vom Wert her identische 0x8000 aber "unsigned".

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.