Forum: Compiler & IDEs Intergerarithmetik


von Andreas B. (bitverdreher)


Lesenswert?

Hallo,
gerade bin ich dabei, mal einen standesgemäßen PID Regler (Lageregelung 
Motor) aufzubauen und habe dazu folgendes Programm erstellt:
1
static int16_t eOld;
2
static int32_t eSum;
3
uint8_t tmp_sreg;
4
int16_t e;
5
int16_t eDiff;
6
int32_t y_P = 0;
7
int32_t y_I = 0;
8
int32_t y_D = 0;
9
int32_t y;
10
11
   if (iStatusFlags & FLAG_SLID_ENABLE) {
12
      
13
      CLEAR_INT;
14
      e = lSlidTargetPosition - lSlidActPosition;
15
      RESET_INT;
16
17
      // I Anteil begrenzt
18
      eSum += e;
19
      if (eSum > 5000) eSum = 5000;
20
      if (eSum < -5000) eSum = -5000;
21
               
22
      y_I = iSlidKiTa * eSum / 64;
23
      
24
      eDiff = e - eOld;
25
26
      // Reglergleichung PID
27
      y_P = iSlidKp * e / 256;
28
      y_D = iSlidKdTa * eDiff / 256 ;
29
      y = y_P + y_I + y_D;
30
31
      if (iDebug == 1) {
32
         UsartPuts("\ny_P: ");
33
         UsartPuts( ltoa( y_P, sOutString, 10 ));
34
         UsartPuts("     y_D: ");
35
         UsartPuts( ltoa( y_D, sOutString, 10 ));
36
         UsartPuts("     y_I: ");
37
         UsartPuts( ltoa( y_I, sOutString, 10 ));
38
         UsartPuts("\ne: ");
39
         UsartPuts( ltoa( e, sOutString, 10 ));
40
         UsartPuts("  eDiff: ");
41
         UsartPuts( ltoa( eDiff, sOutString, 10 ));
42
         UsartPuts("  eSum: ");
43
         UsartPuts( ltoa( eSum, sOutString, 10 ));
44
         UsartPuts("\n");
45
         iDebug = 0;
46
      }
47
      
48
      // Store e
49
      eOld = e;
50
51
      // Direction
52
      if (y > 0) SLID_FORWARD; else SLID_BACKWARD;
53
54
      // Norm y
55
      y = abs(y);
56
      
57
      // Set speed
58
      if (y > 255) SLID_SPEED = 255; else SLID_SPEED = y;
59
60
      // Check Timeout
61
      if (y < iSlidMinDeviation) {
62
         iStatusFlags |= FLAG_SLID_REACH;
63
      } else {
64
         iStatusFlags &= ~FLAG_SLID_REACH;
65
         if ((iSlidTimeoutCount > 0) && (labs(eDiff) > 20)) {
66
            if (iSlidTimeoutCount-- == 0) lErrorFlags |= ERR_SLID_TIMEOUT;
67
         }
68
      }
69
70
   }

Sorgen macht mir die Zeile:
      y_P = iSlidKp * e / 256;

Hier wird nicht in int32_t gerechnet, sondern in int16_t, obwohl y_P als 
32 bit definiert ist. Wie bringe ich gcc dazu, diesen Ausdruck gleich 
als 32 bit zu berechnen ?
Ich kann ja e/256 in Klammern setzten. Dann funktioniert es auch Aber 
ich verliere Genauigkeit dabei. Dann kann ich mir auch sparen, y_P als 
32 bit zu definieren.
Gleiches gilt ja auch für die anderen beiden Regelparameter.
Die Teiler 64 und 256 stehen noch nicht fest. Ich bin noch am austesten 
der Regelparameter.
Der Aufruf erfolgt indirekt über einen Timerinterrupt (Setzen eines 
Flags im ISR und dann Aufruf im Hauptprogramm). Die Regelparameter 
iSlidKp, iSlidKiTa und iSlidKdTa sind global und werden an anderer 
Stelle aus dem EEprom ausgelesen.
Sonstige konstruktive Kritik am Code ist willkommen.

Gruß
Andy

von Michael U. (amiga)


Lesenswert?

Hallo,

ich immernoch ASM-Programmierer, der  manchmal etwas C versucht... ;)

Also als Frage an alle:

      y_P = (int32_t) iSlidKp * e / 256;

sollte das doch lösen?

Außerdem wäre nicht eher eine Klammer um (iSlidKp * e) sicherer, damit 
der Compiler wirklich erst die Multiplikation macht und nicht etwa erst 
e/256 teilt, was ja wieder Genauigkeit verschenken könnte?

Gruß aus Berlin
Michael

von Andreas B. (bitverdreher)


Lesenswert?

Hi Michael,
Du hast recht, mit der impliziten Typumwandlung funktioniert es. Ich 
dachte eigentlich, das macht der Compiler von sich aus, wenn das 
Ergebnis 32 bit ist.
Er rechnet brav von links nach rechts (immer ?). Aber Klammern können 
natürlich nicht schaden.;-)

Wenn jemand mal einen Link hat, wo erklärt wird wie gcc mit 
Intergerausdrücken umgeht, wäre das prima.

Gruß
Andy

von Matthias L. (Gast)


Lesenswert?

>Du hast recht, mit der impliziten Typumwandlung funktioniert es. Ich
>dachte eigentlich, das macht der Compiler von sich aus, wenn das
>Ergebnis 32 bit ist.

Nein. der Compiler rechnet das Ergebnis im dem größten, bei der 
Berechnung vorkommenden Datentypen (also die rechte Seite!). Erst danach 
wird das Ergebnis! in den Datentypes des Ziels (links) umgewandelt.

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.