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
Ach eins noch, dass ich zwei Adressvariablen benutze hängt mit der Feldstruktur auf dem EEPROM zusammen.
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
Das wird schwierig. An dem Programm auf dem PC kann ich nicht so einfach etwas ändern. Und wieso RS232_...? Ich sende via USB.
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) ?
Achso, ich hoffe das im Quelltext nicht wirklich: temp2_m=temp2_m/1000000; dat.1=temp2; steht, sondern: temp2_m=temp2_m/1000000; dat.1=temp2_m;
Habe nochmal nachgeschaut: float und double haben jeweils 32 Bit, war also voll ok.
@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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.