Forum: Mikrocontroller und Digitale Elektronik Datentyp double läuft über?


von Patrick (Gast)


Lesenswert?

Hallo,

ich habe mir eine Temperaturanzeige gebastelt, wo ein ATmega32 über 
A/D-Ports und den jeweiligen Spannungsfall dort die Temperatur eines 
KTY81-110 ausgibt.
Soweit funktioniert das ganz gut, jetzt habe ich das Programm erweitert 
um auf meinem LCD ausserdem folgende Informationen auszugeben:
- Minimale Temperatur jedes Temperaturfühlers
- Maximale Temperatur jedes Temperaturfühlers
- Durchschnitts-Temperatur jedes Temperaturfühlers

Ich arbeite mit einem Timer-Interrupt, der sekündlich ausgelöst wird. In 
der ISR wird dann der Temperaturwert (vom Typ double) ermittelt und 
verarbeitet (Ausgabe auf LCD, speichern in Array).

Meine Arrays für die Temperaturwerte sehen wie folgt aus:
1
double temperature_min[5];      // Speicherarray für Min-Wert der Temperatursensoren
2
double temperature_max[5];          // Speicherarray für Max-Wert der Temperatursensoren
3
double temperature_average[5]={0,0,0,0,0};        // Speicherarray für Durschnitts-Wert der Temperatursensoren
4
int n_temperature_average_values[5]={0,0,0,0,0};      // Speicherarray für Anzahl der Durchschnitts-Werte der Temperatursensoren

Das Array hat also 5 Felder, für jeden Temperatursensor eines.

Den Min/Max-Wert speichere ich einfach ins Array ab, sobald der aktuell 
gemessene Wert kleiner/größer als der dort gespeicherte Min/Max-Wert 
ist. Das funktioniert auch soweit.

Ausserdem summiere ich alle Messwerte jedes Fühlers ins 
temperatur_average Array auf und speichere gleichzeitig die Anzahl der 
aufsummierten Werte ins Array n_temperature_average_values. So kann ich 
durch die Anzahl der Werte und die Summe der Messwerte einen 
Durchschnitt ermitteln.

Mein Problem nun ist folgendes:
Nach 86400 Sekunden (entspricht 24h) lösche ich den 
Min/Max/Durchschnittswert und fange von neuem an. Irgendwo nach ~8-14h 
stimmt aber schon mein Durchschnittswert (laut LCD Ausgabe zumindest) 
nicht mehr. Bei einem Min-Wert von 16°C und Max-Wert von 18°C kann der 
Durchschnitt nicht bei -68°C liegen...

Ich weis jetzt nicht genau, wo das Problem ist: Ist mein Array vom Typ 
double zu klein? Wenn ich mal von 18°C ausgehe, welche 86400 mal 
gemessen wird, sollte das aufsummiert 15.552.000 ergeben. Der Typ 
"double" solte doch 32bit breit sein und somit ausreichen?

Oder liegt das Problem vielleicht bei der Ausgabe des 
Durchschnittswertes auf das LCD?
Die Ausgabe sieht zum Beispiel für den 1. Temperaturfühler so aus:
print_varf(temperature_average[0]/n_temperature_average_values[0], 
12,3);
Dabei entspricht "12" und "3" der X/Y-Position für die Ausgabe des 
Wertes auf dem LCD.

Die Funktion "print_varf" sieht dann wie folgt aus:
1
int print_varf(double d,int x,int y)
2
{
3
  char Buffer[20];                  // sprintf Variable
4
  sprintf (Buffer,"%3.1f", d);            // "d" in "Buffer" einsetzen als %f mit Formatierung (Vor-/Nachkommastellen)
5
  lcd_sprintf(Buffer, x, y);              // Ausgeben von "Buffer" an Position X/Y
6
  
7
  return(1);
8
}

Und mein lcd_sprintf:
1
#define lcd_sprintf( Buffer , x, y) {lcd_goxy(x,y); lcd_wstr( Buffer );}

Ich hoffe jemand kann mir einen Tipp geben, wo ich suchen muss. Vielen 
Dank!

von max (Gast)


Lesenswert?

Patrick schrieb:
> Der Typ
> "double" solte doch 32bit breit sein und somit ausreichen?

Ist er nicht. Aber das ist kein Problem. Er läuft nicht über.
Du wirst lange bevor du den Rand des Wertebereiches erreichst ein 
anderes Phänomen sehen: temperature_average + 18 == temperature_average 
(Wenn 2^Exponent * Wert des Letzten Manissenbits > 18). Bei Double hast 
du bis dahin aber auch noch Platz.

Ich nehme eher an, dass dein int n_temperature_average_values bei 32767 
(9.1h) -> 32768 überlauft und du durch negative Werte teilst. 
sizeof(int) = ?

von (prx) A. K. (prx)


Lesenswert?

max schrieb:

>> "double" solte doch 32bit breit sein und somit ausreichen?
>
> Ist er nicht.

Ist er doch. Beim GCC für AVR.

von Patrick (Gast)


Lesenswert?

Hallo Max!

Danke für die schnelle Antwort!

So ein Mist... natürlich ist der int nur 8 bit breit... werde einen 
uint32_t daraus machen, dann sollte sich das Problem eigentlich gelöst 
haben.

von Patrick (Gast)


Lesenswert?

Achja, zur Ergänzung: Mein Projekt wurde mit AVRStudio 5 in C erstellt 
(gcc-Kompiler).

von (prx) A. K. (prx)


Lesenswert?

Patrick schrieb:

> So ein Mist... natürlich ist der int nur 8 bit breit...

Eher 16.

von Falk B. (falk)


Lesenswert?

@Patrick (Gast)

>Ich arbeite mit einem Timer-Interrupt, der sekündlich ausgelöst wird. In
>der ISR wird dann der Temperaturwert (vom Typ double) ermittelt und
>verarbeitet (Ausgabe auf LCD, speichern in Array).

Was schon mal nicht gut ist, sowas macht man ausserhalb eines 
Interrupts, siehe Artikel Interrupt.

>Meine Arrays für die Temperaturwerte sehen wie folgt aus:

>double temperature_min[5];      // Speicherarray für Min-Wert der 
>Temperatursensoren

Double braucht man hier nicht, nimm Festkommaarithmetik.

>Min/Max/Durchschnittswert und fange von neuem an. Irgendwo nach ~8-14h
>stimmt aber schon mein Durchschnittswert (laut LCD Ausgabe zumindest)
>nicht mehr. Bei einem Min-Wert von 16°C und Max-Wert von 18°C kann der
>Durchschnitt nicht bei -68°C liegen...

Poste Code, siehe Netiquette.

>Ich weis jetzt nicht genau, wo das Problem ist: Ist mein Array vom Typ
>double zu klein? Wenn ich mal von 18°C ausgehe, welche 86400 mal
>gemessen wird, sollte das aufsummiert 15.552.000 ergeben. Der Typ
>"double" solte doch 32bit breit sein und somit ausreichen?

Informiere dich über Fließkommazahlen. Die können deutlich mehr als 15 
Millionen als Zahl darstellen, haben aber Probleme mit Additionen von 
sehr unterschiedlichen Zahlen. Beispiel.

1e9 + 1 = 1e9

Klingt komisch, ist aber so. Das könnte dein Problem sein. Ein Grund 
mehr für Festkommaarithmetik.

MfG
Falk

von (prx) A. K. (prx)


Lesenswert?

Falk Brunner schrieb:

> Klingt komisch, ist aber so. Das könnte dein Problem sein.

Ist aber nicht sein Problem, auch wenn die korrekte Mittelung dadurch 
beeinträchigt wird. Zu einem Vorzeichenwechsel kann dies keinesfalls 
führen.

Wie schon erwähnt wurde kann man 86400 Werte mit einem AVR nicht mit 
"int" abzählen. Und da kriegt man einen Vorzeichenwechsel.

von Achim M. (minifloat)


Lesenswert?

A. K. schrieb:
> Wie schon erwähnt wurde kann man 86400 Werte mit einem AVR nicht mit
> "int" abzählen.

Irgendjemand hier im Forum hatte doch schon mal einen 24-Bit Datentyp 
für AVR gebastelt. Wegen Alignment kann man aber auch gleich uint32_t 
nehmen.
mfg mf

von Patrick (Gast)


Lesenswert?

So, habe das jetzt mittlerweile getestet: Mit dem uint32_t funktioniert 
alles wie es soll, vielen Dank!

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.