Forum: Compiler & IDEs je nach Variablenwert Endlosschleife bei ATMega32


von Gallig (Gast)


Lesenswert?

Hallo ich programmiere gerade eine Software für einen ATMega 32. Beim 
ersten Test lief alles Problemlos, jetzt habe ich jedoch eine Variable 
geändert und plötzlich stecke ich anscheinend in einer Endlosschleife.

Durch Debugausgaben auf ein Display konnte ich das Problem auf folgende 
Stelle eingrenzen.
1
volatile uint16_t puls;    // Pulsbreite des Hauptstroms
2
volatile uint64_t mittelwert;  // Variable für den Mittelwert
3
volatile uint8_t messwerte[960];  // Feld für Messwerte von A/D-Wandlung
4
uint16_t c;      // Zählvariable für FOR-Schleifen
5
6
puls=((zih*1000)/290);
7
for(c=0; c<puls; c++){  // Summe über "Pulsbreite" für ersten Messwert
8
    mittelwert+=messwerte[c];  // berechnen
9
}

In der Standardeinstellung hat zih einen Wert von 30 (puls=103)und alles 
läuft ohne Probleme, wenn ich diesen nun auf 40 oder mehr erhöhe bleibt 
das Programm in der FOR-Schleife hängen weil die Variable puls einen 
irrsinnig hohen Wert bekommt, anstatt 137.

Ich habe bisher schon mit verschiedenen anderen Datentypen rumprobiert 
aber das Problem bleibt nach wie vor. Vielleich stehe ich nur auf dem 
Schlauch und bin nach mehreren Stunden der Fehlersuche nur blind für 
meine eigenen Fehler geworden.

Falls also jemand einen Fehler im Code entdeckt oder eine Idee hat wie 
ich das Problem beheben könnte... ich bin für jede Hilfe dankbar.

Ach ja ich programmiere mit AVR-Studio und winavr 20070525,
Optimierung: -Os

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Gallig wrote:

> puls=((zih*1000)/290);

> In der Standardeinstellung hat zih einen Wert von 30 (puls=103)und alles
> läuft ohne Probleme, wenn ich diesen nun auf 40 oder mehr erhöhe bleibt
> das Programm in der FOR-Schleife hängen weil die Variable puls einen
> irrsinnig hohen Wert bekommt, anstatt 137.

40 * 1000 = 40000

Beide Seiten haben den Typ `int', der kann nur maximal 32767 darstellen.
Das Ergebnis ist eine negative Zahl: -25536.  Diese wird jetzt durch
290 dividiert, das ergibt -88.  Anschließend erfolgt eine implizite
Typumwandlung auf uint16_t, da das der Zieltyp der Variablen puls ist.
Die Umwandlung von signed nach unsigned int bei gleicher Bitanzahl
ist dabei ein einfaches kopieren des Bitmusters, d. h. das Bitmuster
von -88 wird jetzt nur anders bewertet und heißt dann 65448.

Du musst dich also darum kümmern, dass der innere Multiplikationsterm
nicht überläuft.  Falls zih keine Werte größer 65 annehmen kann,
genügt es dafür bereits, den ganzen Ausdruck komplett in der uint16_t-
Domain zu berechnen:

puls = ((zih * 1000U) / 290U);

von OliverSo (Gast)


Lesenswert?

Der Compiler rechnet alles als int, wenn nicht explizit anders 
vorgegeben.

Jetzt schau dir den Wertebereich von int an, dann kommst du auch darauf, 
was da passiert.

Wenn du aus 1000 1000UL machst, sollte es auch mit 40 funktionieren.

Oliver

von Gallig (Gast)


Lesenswert?

Vielen Dank für eure schnellen Antworten, jetzt habe sogar ich es 
kapiert ;)
Also werden konstanten generell als INT betrachtet, zufern sie in diesen 
Datentyp "rein passen"?
Richtet sich die Berechnung mit Variablen dann auch immer automatisch 
nach dem größten Datentyp im Term?

von Gallig (Gast)


Lesenswert?

Noch eine kurze Frage, der maximale Wert für zih wäre 300, d.H. ich 
bräuchte bei der Berechnung ein uint32_t wenn ich das soweit richtig 
verstehe. Wie wäre das das Postfix für diesen Datentyp? 1000ULL?

von Johannes M. (johnny-m)


Lesenswert?

Ein "L" reicht. "ULL" ist "unsigned long long", das wäre uint64_t.

von Gallig (Gast)


Lesenswert?

Danke :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Gallig wrote:

> Also werden konstanten generell als INT betrachtet, zufern sie in diesen
> Datentyp "rein passen"?

Ja, danach `unsigned int', danach `long', usw.

> Richtet sich die Berechnung mit Variablen dann auch immer automatisch
> nach dem größten Datentyp im Term?

Ja, so kann man das ausdrücken.

von Rolf Magnus (Gast)


Lesenswert?

> Ja, danach `unsigned int', danach `long', usw.

Nur wenn die Konstante oktal oder hexadezimal angegeben ist. Bei 
dezimalen ist es 'int', dann 'long', dann 'long long'.

von Εrnst B. (ernst)


Lesenswert?

1
puls=((zih*1000U)/290U);
Lässt sich natürlich auch als
1
puls=((zih*100U)/29U);
Umformen, und schon kann zih zehnmal größer werden, ohne dass die innere 
Klammer den uint16-Wertebereich überschreitet...

von Gallig (Gast)


Lesenswert?

Danke für die Hilfe. Das mit dem Kürzen ist auch ein guter Tip aber ich 
lasse es lieber so, denke dann ist der Code leichter zu verstehen da die 
Zeitvariable dort von ms in µs umgerechnet wird.

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.