Forum: Mikrocontroller und Digitale Elektronik Datentypen Atmega32


von Madde (Gast)


Angehängte Dateien:

Lesenswert?

Hallo!
Ich habe eine Lichtschranke mit einem Atmega32 gebaut und habe 
anscheinend ein type problem. Die Ausgabe übernehmen 6 7-Segment 
Anzeigen und funktionieren.
Hier das Problem
1
uint32 mpers;
2
3
  uint32_t numerator = calc_d_in_mm()*1000; //get distance
4
  uint32_t denominator = calc_t_in_ms(); //get time
5
6
  mpers = (numerator/denominator+5)/10;  //+5 -> rounding (int cuts off decimals)

Da double und float von vornherein alle Nachkommastellen abgeschnitten 
haben (Wieso denn?), habe ich einfach einen unsigned long benutzt und 
das komma um 3 stellen verschoben.
"mpers" wird anschließend in seine digits aufgeteilt und an die 
einzelnen Anzeigen übergeben:
1
    LB_v1 = (mpers/1)%10;    //split number to digits
2
    LB_v2 = (mpers/10)%10;
3
    LB_v3 = (mpers/100)%10;
4
    LB_v4 = (mpers/1000)%10;
5
    LB_v5 = (mpers/10000)%10;
6
    LB_v6 = (mpers/100000)%10;
mpers = 1234;           //wird richtig dargestellt
mpers = calc_d_in_mm(); //wird richtig dargestellt
mpers = calc_t_in_ms(); //wird richtig dargestellt

ABER:
numerator = calc_d_in_mm()*1000;
mpers = numerator/1000;
//stellt viel zu großen Blödsinn dar, als ob ein type overflow 
stattfinden würde
//das selbe passiert bereits, wenn man mit 10 multipliziert, ABER nicht 
bis 9

Die Distanz hat einen bereich von 0-29999mm.

Ich muss die Arbeit am Dienstag als Abiturarbeit abgeben und wäre euch 
unendlich dankbar, wenn ihr mir sagen könntet woran es liegt...
Anbei noch die komplette Datei.

von Sven P. (Gast)


Lesenswert?

Madde wrote:
> Da double und float von vornherein alle Nachkommastellen abgeschnitten
> haben (Wieso denn?),
Weil du vermutlich nicht gegen die Fließkomma-Bibliotheken gelinkt hast. 
Aber lass das Fließkomma besser wieder in der Schublade verschwinden...

Ansonsten nur so als Tipp für Festkomma: Man muss ja nicht mit 
10er-Potenzen hochskalieren und nachher wieder teilen. 2er-Potenzen tuns 
auch und sind schneller...

von Madde (Gast)


Lesenswert?

Du meinst das

mpers = (numerator/denominator+5)/10;

als 2er potenz?

Liebend gerne, aber alles größer 9 ergibt, wie gesagt, fehler :(

von Oliver (Gast)


Lesenswert?

Dürft ihr denn keine Debugger benutzen? Im Programm einzelschrittweise 
die Werte ansehen, und prüfen, wo es schiefgeht, ist in 3 Minuten 
erledigt.

Oliver

von Madde (Gast)


Lesenswert?

Ich arbeite zuhause mit linux und eclipse, da bin ich etwas überfragt. 
Im AVR Studio war das recht gut, ja.

von Rik (Gast)


Lesenswert?

Hallo Madde,
das AVR-Studio 3.x funktioniert problemlos unter Linux mit wine 
(zumindest der Assembler und Simulator, den Rest des Studios benutze ich 
nicht).

von Falk B. (falk)


Lesenswert?


von Oliver (Gast)


Lesenswert?

>Ich arbeite zuhause mit linux und eclipse, da bin ich etwas überfragt.
>Im AVR Studio war das recht gut, ja.

Na ja, ist dein Abi. Die einen nutzen linux und Eclipse, die anderen 
sind schon lange fertig. Nichts gegen Eclipse oder linux, aber 
programmieren ohne Debugger ist sinnlos, auch wenn die echten Kerle 
meinen, es ginge auch ohne.

Ich hätte es ja mal schnell durch den Simulator gejagt, aber leider gibt 
mal keinen kompilierbaren Code. Musst du also selber lösen.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Ich denke mal dein Hauptproblem besteht darin, dass du von falschen 
Vorstellungen ausgehst, wie in C Datentypen für eine Berechnung 
ausgewählt werden. Die Aussage ...

> Da double und float von vornherein alle Nachkommastellen
> abgeschnitten haben (Wieso denn?),

... geht für mich ebenfalls in diese Richtung.

Dein Problem ist, dass du falsche Vorstellungen davon hast, wie der 
Compiler eine Operation auswählt. In

  double m = 7 / 5;

erhält m den Wert 1, obwohl es als double definiert ist! Warum ist das 
so? Weil der Compiler bei der Auswahl der Operation sich ausschliesslich 
an den beteiligten Operanden orientiert! 7 ist ein Integer, 5 ist ein 
Integer. Folgerichtig wird die Division als Ganzzahldivision ausgeführt. 
Und 7 durch 5 als Ganzzahldivision ergibt nun mal 1. Erst danach, wenn 
das Ergebnis feststeht, kommt der Zieldatentyp von m ins Spiel. Das 
Ergebnis ist ein Integer, das Ziel ist ein double. Also wird die Integer 
1 auf eine double 1 umgewandelt und zugewiesen.

Auch in deinem Code sind solche Misverständnisse zu sehen.

zb
  d_in_mm = (LB_d5*10000)+(LB_d4*1000)+(LB_d3*100)+(LB_d2*10)+LB_d1;

LB_d5 ist ein uint8_t, 10000 ist ein int. Der größte gemeinsame Nenner 
zwischen den beiden Datentypen ist ein uint16_t. Und genau diese 
Operation nimmt der Compiler dann auch her. LB_d5 wird mit 10000 per 
16-Bit-unsigned Multiplikation verknüpft. (Zur Erinnerung: ein uint16_t 
kann nicht größer als 65535 werden). Wenn LB_d5 also den Wert 7 erreicht 
hat, dann geht diese Operation schon mal schief. Um das zu vermeiden, 
musst du den Compiler zwingen, die Operation als 32-Bit Operation 
durchzuführen. Es reicht, wenn einer der beteiligten Operanden ein 
uint32_t ist. Also entweder du castest den LB_d5 auf einen 32 Bit 
Datentyp hoch, oder du machst aus den 10000 eine long Zahl: 10000L oder 
besser gleich eine 32 Bit unsigned long Zahl 10000UL

Druchforste mal deinen Code auf derartige Misverständnisse und du wirst 
sehen, dass deine Ergebnisse besser werden.

von Madde (Gast)


Lesenswert?

Vielen Dank für die Antwort!
Ich habe auch mal die Compileroptimierung ausgeschaltet und nun 
funktioniert es... Dafür aber anscheinend nicht mehr der Timer.
Ich werde mal Deinem Rat folgen und den Code durchgehend mit UL oder 
casts versehen.

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.