Hallo Leute,
mein mega32 rechnet falsch!?
Ich lade 4 Byte aus dem EEPROM in eine globale Struktur und will dort
aus dem 32bit integer einen float-Wert machen. Der int-Wert landet
korrekt in einer Hilfsvariable auf dem RAM. Dann muss ich den int-Wert
durch 10^6 teilen und in der Struktur ablegen. Dabei kommt manchmal das
richtige und manchmal das falsch Ergebnis raus.
War kann helfen?
da der AVR selber nicht viel mehr als addieren und schieben kann (das
macht er übrigens ziemlich fehlerfrei), bleiben als Lösung für dein
Problem:
-dein eigenes Programm
-ein Compilerfehler
-Nichtbeachten der prinzipiellen float-Ungenauigkeiten
deine Angaben sind also etwas dürftig.
Hi
was ist richtig und was ist falsch? Mit welchem Compiler arbeitest du?
Oder hast du die FP-Routinen in ASM kodiert?
Meine Kristallkugel hat leider keinen TÜV mehr bekommen und die neue
ist noch nicht lieferbar.
Matthias
Hallo M.Gerlach,
was fehlt, sind folgende Informationen :
- verwendete Programmiersprache/Compiler
- dein Quelltext (bitte nur den betreffenden Ausschnitt und den
kompletten Quelltext in den Anhang, wenn er Bestandteil des Problems
ist)
- ein kommentiertes Beispiel
Liefere diese Informationen und Dir wird geholfen - Vorteil : Du
brauchst Dir kein Kristallkugelgewaffel anhören.
Ich hoffe, ich konnte Dir beim Stellen einer sinnvollen und
beantwortbaren Frage helfen.
MfG, Daniel.
Warum legst Du nicht gleich den Wert als float ab, das sind auch 4
Byte.
float (23 Bit Mantisse) hat eine geringere Genauigkeit als long (32
Bit).
Peter
Also,
ich benutze C, pn2 mit avrgcc3.3 und studio4.11. Weiterhin habe ich ein
JTAG-ice.
Zunächst zum Ablegen als float:
ich sende die Daten vom PC über USB zum Controller. Dazu muss ich den
ürsprünglichen float-Wert durch Multiplikation in int wandeln, da meine
Senderoutine auf dem PC nur int-weise sendet.
Richtig oder falsch bedeutet tatsächlich keine Ungenauigkeit im
Kommestellenbereich sondern völliger blödsinn oder exakt.
Hier noch ein kleiner Auszug:
double temp2_m;
long temp_m,cali_temp;
unsigned int adr_i,adr_j; // Adressvariablen
for(adr_i=0;adr_i<10;adr_i++)
{
temp_m=0; // löschen damit ODER-Operation möglich
adr_j=0; // Adresszeiger
cali_temp=0;
cali_temp=memo_read(6*adr_i+adr_j);
temp_m=cali_temp<<24; // erstes byte --> high-byte
adr_j++;
cali_temp=0;
cali_temp=memo_read(6*adr_i+adr_j);
temp_m=temp_m|cali_temp<<16;
adr_j++;
cali_temp=0;
cali_temp=memo_read(6*adr_i+adr_j);
temp_m=temp_m|cali_temp<<8;
adr_j++;
cali_temp=0;
cali_temp=memo_read(6*adr_i+adr_j);
temp_m=temp_m|cali_temp; // viertes byte --> low-byte
temp2_m=(double)(temp_m);
temp2_m=temp2_m/1000000;
dat.1=temp2
Ich glaub du gehst die Sache komplett falsch an. Wenn ich es richtig
verstanden habe, versuchst du die Flaotzahl erstmal umzurechnen, damit
du sie dann senden kannst. Du solltest die Daten gleich binär schicken.
Falls dein Programm auf dem PC auch in C/C++ geschrieben ist, dann
solltest du es mit einer unit machen. zum Beispiel so:
typedef unit
{
float f;
char binary[4];
} Converter;
Das Senden sollte in etwa so aussehen:
Converter c;
c.f = 3.14;
RS232_Send(c.binary,4);
Das Empfangen auf dem Controller:
RS232_Receive(c.binary);
var = c.f
Weiterhin ist das Problem nicht auf das Laden der EEPROM-Daten
beschränkt. Das war jetzt nur eine Stelle an der das Auftritt. Generell
sieht es so aus, dass offensichtlich unter bestimmten Umständen
umfangreiche Rechenoperationen nicht nur falsch ausgeführt werden
sondern soger der Stack (Peter müsste wissen worums geht) beschädigt
wird. Das aussert sich durch Sprung ins Nirgendwo.
Kann es nicht sein, dass der compiler in den XYZ-Registern rumfuhrwerkt
weil nicht genügend Register zur Verfügung stehen?
Du solltest erstmal feststellen, wobei der Fehler auftritt:
- stimmen die Bytes im EEPROM ?
- stimmt der long Wert
- stimmt der float Wert
Und dann braucht man natürlich mindestens je einen Beispielwert, wo es
stimmt und wo nicht, z.B. so:
1,2345 stimmt
1,2346 -> 1,6432 stimmt nicht
Peter
Deie Daten im EEPROM und im long-Wert sind generell OK.
Gerade ist es z.B. so, dass folgende Zeile in der Laderoutine nicht
ausgeführt wird
temp2_m=temp2_m/1000000;
sondern mein Debug-Pfeil einfach wieder zurückgesetzt wird!
Um die Einzelrechenoperationen zu entschärfen, habe ich auch schon
versucht die Division auf zwei aufzuteilen.
Dabei kam der controller auf das merkwürdige Ergebnis
999878/1000=499.939
was genau die Hälfte des richtigen Ergebnisses ist!
Witzig, was!
Das Problem ist nicht klar beschrieben. Wo soll jetzt ein Fehler
passieren? Wenn Du den Int-Wert durch 10^6 teilst? Dann steht da einmal
was von Umwandlung von int in float, dann wieder von float in int...
Wenn es nur an der Umwandlung scheitert, dann würde ich mal einen
einfachen Cast austesten.
int i = 23;
float f;
f = (float)i;
Genau so läufts auch. Ich caste den int-Wert auf float und dividiere ihn
durch 10^6, wobei der int-Wert aus dem EEPROM stammt. Diese Operation
funktioniert mal und mal nicht obwohl der int-Wert immer richtig ist!
temp2_m=(double)(temp_m);
temp2_m=temp2_m/1000000;
dat.1=temp2;
Die 1000000 sind eigentlich falsch, muss eigentlich 1000000.0 sein, weil
float bzw. double verarbeitet wird. Sollte aber der Compiler merken und
wohl nicht das Problem sein.
Ich würde auch nach dem Schieben noch maskieren, so:
temp_m = (cali_temp << 24) & 0xF000; // erstes byte --> high-byte
temp_m= temp_m | (cali_temp << 16) & 0xF00;
usw.
Wieso nimmst Du double, das sind doch 64 Bit (müsste ich bei WinAVR
aber noch prüfen) ?
@edvdoctor
ja das warn schreibfehler. Bezüglich ausmaskieren: das kann ich noch
probiereb, aber das Problem sieht ja so aus , dass der int-Wert immer
korrekt zurechtgeschoben wird und der Operation cast/Division immer
richtig zur Verfügung steht.