Fehler in Integer-Berechnung

Gast #3664180
Lesenswert?

Moin,

ich habe hier folgende Funktion geschrieben. Urpsrünglich hatte ich sie 
für float Werte geschrieben, was auch funktioniert hat. Jetzt möchte ich 
sie auf Integer Werte umschreiben und bekomme falsche Werte. Sicherlich 
läuft ein Integer über oder so, aber ich weiss nicht wo.
1
#define PWM_TOP 1600
2

3
void SetPWM(uint8_t dutycycle)
4
{
5
  uint16_t _ocr1a = 0;
6
  // Fehlerhafte Eingabe abfangen
7
  if (dutycycle > 100) dutycycle = 100;
8
  if (dutycycle < 0)   dutycycle = 0;  
9
  
10
        //Vergleichswert berechnen
11
  _ocr1a = ((PWM_TOP*dutycycle) / 100); 
12
  
13
  sprintf(sbuf,"OCR1A: %u", _ocr1a);
14
  usb_send_str(sbuf);
15
  
16
  //Vergleichswert setzen
17
  OCR1A = _ocr1a;
18
}

Erwarten würde ich:
_ocr1a = 1600*50/100 = 80.000/100 = 800
Das Ergebnis ist:
_ocr1a = 144

Ich dachte erst, dass das Zwischenergebnis von 80.000 das Problem wäre, 
da es nicht in uint16_t passt. Aber die Berechnung mit uint32_t ergab 
das gleiche unsinnige Ergebnis.

Ich sehe meinen Fehler gerade echt nicht...

Viele Grüße,
Christian
#3664186
Lesenswert?

Probiers mal so. Unsinnigen Vergleich <0 kannste weglassen und dutycycle 
must du erst umwandeln.
1
   uint16_t _ocr1a = 0;
2
   // Fehlerhafte Eingabe abfangen
3
   if (dutycycle > 100) dutycycle = 100; 
4
         //Vergleichswert berechnen
5
   _ocr1a = (PWM_TOP/100)*(uint16_t)(dutycycle);
6
 ...
#3664188
Lesenswert?

Christian schrieb:
> Ich dachte erst, dass das Zwischenergebnis von 80.000 das Problem wäre,

Das ist auch das Problem. Die "Falle" in C ist, daß der Compiler erst 
rechnet, und danach in das Ergebnisformat umwandelt. In deinem Fall wird 
mit unsigned int (= uint16_t) gerechnet, und da passiert der Überlauf. 
Ob das falsche Ergebnis dann einem uint16_t oder einem uint32_t 
zugewiesen wird, ändert nichts mehr.

Willst du das vermeiden, musst du die Operanden casten:
1
_ocr1a = (((uint32_t)PWM_TOP*dutycycle) / 100);

Dann wird mit 32 Bit gerechnet, und alles wird gut (und dauert dafür 
länger).

Oliver
Gast #3664190
Lesenswert?

Stimmt der Vergleich kleiner Null macht jetzt in Festkommaarithmetik 
keinen sinn mehr ;-)

Ich hatte dutycycle auch schon im Funktionsaufruf als uint16_t 
deklariert. Das Ergebnis war das gleiche. ich Probiers nochmal mit 
deiner Rechenreihenfolge! Danke!
Gast #3664574
Lesenswert?

Christian schrieb:
> void SetPWM(uint8_t dutycycle)
> {
>   uint16_t _ocr1a = 0;
>   // Fehlerhafte Eingabe abfangen
>   if (dutycycle > 100) dutycycle = 100;
>   if (dutycycle < 0)   dutycycle = 0;
Wie soll dutycycle denn bitte kleiner 0 werden?
Tipp: void SetPWM(uint8_t dutycycle)
Was haben unsigned Datentypen fuer eine Eigenschaft?

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