Probleme mit dem Preprozessor. Er verrechnet sich! Oder mach ich was falsch?

OP #2392503
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
Moderator Persönliche Seite #2392528
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. ;-)
OP #2392550
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!
Gast #2392601
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?
#2392610
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.
Gast #2392620
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.
#2392635
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)
#2395283
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.
#2395301
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".

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren