Forum: Mikrocontroller und Digitale Elektronik MEGA32 ALU-Problem?


von M. Gerlach (Gast)


Lesenswert?

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?

von crazy horse (Gast)


Lesenswert?

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.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

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

von Daniel B. (khani)


Lesenswert?

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.

von Peter Dannegger (Gast)


Lesenswert?

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

von M. Gerlach (Gast)


Lesenswert?

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

von M. Gerlach (Gast)


Lesenswert?

Ach eins noch,

dass ich zwei Adressvariablen benutze hängt mit der Feldstruktur auf
dem EEPROM zusammen.

von M. Gerlach (Gast)


Lesenswert?

Oh ich wurde gerade abgelengt.
Tschuldigung.

von Bri (Gast)


Lesenswert?

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

von M. Gerlach (Gast)


Lesenswert?

Das wird schwierig. An dem Programm auf dem PC kann ich nicht so einfach
etwas ändern. Und wieso RS232_...? Ich sende via USB.

von M. Gerlach (Gast)


Lesenswert?

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?

von Peter Dannegger (Gast)


Lesenswert?

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

von M. Gerlach (Gast)


Lesenswert?

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!

von M. Gerlach (Gast)


Lesenswert?

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!

von M. Gerlach (Gast)


Lesenswert?

Keine Meinungen mehr?

von edvdoctor (Gast)


Lesenswert?

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;

von M. Gerlach (Gast)


Lesenswert?

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;

von edvdoctor (Gast)


Lesenswert?

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) ?

von edvdoctor (Gast)


Lesenswert?

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;

von edvdoctor (Gast)


Lesenswert?

Habe nochmal nachgeschaut:
float und double haben jeweils 32 Bit, war also voll ok.

von M. Gerlach (Gast)


Lesenswert?

@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
Noch kein Account? Hier anmelden.