www.mikrocontroller.net

Forum: Compiler & IDEs Intergerarithmetik


Autor: Andreas B. (bitverdreher)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
gerade bin ich dabei, mal einen standesgemäßen PID Regler (Lageregelung 
Motor) aufzubauen und habe dazu folgendes Programm erstellt:

static int16_t eOld;
static int32_t eSum;
uint8_t tmp_sreg;
int16_t e;
int16_t eDiff;
int32_t y_P = 0;
int32_t y_I = 0;
int32_t y_D = 0;
int32_t y;

   if (iStatusFlags & FLAG_SLID_ENABLE) {
      
      CLEAR_INT;
      e = lSlidTargetPosition - lSlidActPosition;
      RESET_INT;

      // I Anteil begrenzt
      eSum += e;
      if (eSum > 5000) eSum = 5000;
      if (eSum < -5000) eSum = -5000;
               
      y_I = iSlidKiTa * eSum / 64;
      
      eDiff = e - eOld;

      // Reglergleichung PID
      y_P = iSlidKp * e / 256;
      y_D = iSlidKdTa * eDiff / 256 ;
      y = y_P + y_I + y_D;

      if (iDebug == 1) {
         UsartPuts("\ny_P: ");
         UsartPuts( ltoa( y_P, sOutString, 10 ));
         UsartPuts("     y_D: ");
         UsartPuts( ltoa( y_D, sOutString, 10 ));
         UsartPuts("     y_I: ");
         UsartPuts( ltoa( y_I, sOutString, 10 ));
         UsartPuts("\ne: ");
         UsartPuts( ltoa( e, sOutString, 10 ));
         UsartPuts("  eDiff: ");
         UsartPuts( ltoa( eDiff, sOutString, 10 ));
         UsartPuts("  eSum: ");
         UsartPuts( ltoa( eSum, sOutString, 10 ));
         UsartPuts("\n");
         iDebug = 0;
      }
      
      // Store e
      eOld = e;

      // Direction
      if (y > 0) SLID_FORWARD; else SLID_BACKWARD;

      // Norm y
      y = abs(y);
      
      // Set speed
      if (y > 255) SLID_SPEED = 255; else SLID_SPEED = y;

      // Check Timeout
      if (y < iSlidMinDeviation) {
         iStatusFlags |= FLAG_SLID_REACH;
      } else {
         iStatusFlags &= ~FLAG_SLID_REACH;
         if ((iSlidTimeoutCount > 0) && (labs(eDiff) > 20)) {
            if (iSlidTimeoutCount-- == 0) lErrorFlags |= ERR_SLID_TIMEOUT;
         }
      }

   }

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

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Andreas B. (bitverdreher)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.