Forum: Mikrocontroller und Digitale Elektronik double oder float in I²C EEPROM speichern


von Gregor (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

Ich möchte mal ein kleines Projekt starten! Dieses wird ein
Elektronisches Sparschwein! Die Geldwurfanlage und so weiter ist noch in
Arbeit, es happert am Programm!
Angelehnt an diesen Beitrag:
Beitrag "float - Wert in EEPROM speichern"
hab ich meine Funktionen geschrieben, die es irgendwie nicht richtig 
tun.

Ich benutze einen ATMEGA16L8 so wie das AVRStudio 4.18 SP2 und das
AVR-Starterkit STK500 zu testzwecken. Angeschlossen sind im Moment die 8
Taster um die Münzen zu simulieren! Der Speicher ist eine (double)
Variable, die ich einem I²C EEPROM (ST24C02) speichern möchte, damit der
Speicher erhalten bleibt, wenn der Strom ausgeschaltet wird.

Um die Funktion des EEPROMs zu testen schreibe ich den Startwert rein
und lese ihn anschliessend wieder aus, um damit weiter zu arbeiten!

Jedoch schreibe ich eine 10.00 rein und bekomme immer wieder 12.08
raus!?!
Bei anderen Versuchen meines Programms bekam ich andauernd 0.00 raus!?!

Hier der wichtige Teil des Codes:
1
int main()
2
{
3
  lcd_init();      // LCD Initialisieren
4
  
5
  
6
  
7
  lcd_print("Hallo Da");
8
  lcd_line(3);
9
  lcd_print("gobert ");
10
  _delay_ms(PAUSE4);  
11
  
12
  EEOpen();
13
  
14
  float dSpeicher = 10.00;// 1. Durchlauf! 
15
  schreiben(dSpeicher);// Zählstand des Geldspeichers in den EEPROM schreiben
16
  
17
  
18
  dSpeicher = lesen();      // 2. Durchlauf!
19
      // Zählstand des Geldspeicher aus dem EEPROM lesen
Meine Funktionen sind:
1
// Schreibvorgang. Speichert (double) in die ersten 8 Speicherzellen des I²C-EEPROM
2
void schreiben(float dWert)
3
{
4
  unsigned int address=0;
5
    
6
  char *c = (char*) &dWert;
7
  
8
9
  for(address=0; address<sizeof(float);address++)
10
  {
11
    lcd_clear();
12
    lcd_print("schreibe");
13
    lcd_line(3);
14
    lcd_print(*c);
15
    _delay_ms(PAUSE1);
16
    if(EEWriteByte(address,*c)==0)    // Bediengung erfüllt wenn schreiben Fehlerhaft
17
    {
18
      lcd_clear();
19
      lcd_print("Fehler a");
20
      lcd_line(3);
21
      lcd_print("t: Write");
22
      while(1);            // Endlosschleife um weiteres schreiben zu verhindern
23
            
24
    }
25
    if(EEReadByte(address)!=*c)
26
    {
27
      lcd_clear();
28
      lcd_print("Fehler a");
29
      lcd_line(3);
30
      lcd_print("t: Read");
31
      while(1);            // Endlosschleife um weiteres schreiben zu verhindern
32
            
33
    }
34
    //address++;
35
    c++;
36
  }
37
  lcd_ausgabe(dWert);
38
  _delay_ms(PAUSE1);
39
}
40
41
// Lesevorgang. Speichert 8 Bytes in dWert und gibt (double) zurück
42
float lesen(void)
43
{
44
  unsigned int address=0;
45
  float dWert;
46
  
47
  char *c = (char *) &dWert;
48
 
49
  for(address=0; address<sizeof(float);address++)
50
  {
51
    *c = EEReadByte(address);
52
    
53
    c++;
54
  }
55
  
56
  
57
  
58
  lcd_ausgabe(dWert);
59
  _delay_ms(PAUSE1);
60
  return dWert;
61
   
62
}

ich verwnede double bzw float um auch 1 Cent münzen addieren zu können! 
Vielleicht gitbs auch andere Möglichkeiten bzw Variablen, mit denen ich 
rechnen könnte und die dann einfacher zum abspeichern im EEPROM sind!?!

lg und danke im vorraus
Gregor

von Karl H. (kbuchegg)


Lesenswert?

Gregor schrieb:

> Jedoch schreibe ich eine 10.00 rein und bekomme immer wieder 12.08
> raus!?!
> Bei anderen Versuchen meines Programms bekam ich andauernd 0.00 raus!?!

Sieh dir mal die einzelnen Bytes an, die du reinschreibst und die wieder 
gelesen werden. Vielleicht kann man ja dort etwas erkennen.

> ich verwnede double bzw float um auch 1 Cent münzen addieren zu können!

Ist noch lange kein Grund.
Mit einem uint32_t kann man wunderbar auch Cent zählen.
Ganze 4294967296 davon.
Das sind dann ~ 42949672 Euro, oder knapp 43 Milliarden Euro.
Sicher das das nicht reicht?

> Vielleicht gitbs auch andere Möglichkeiten bzw Variablen, mit denen ich
> rechnen könnte und die dann einfacher zum abspeichern im EEPROM sind!?!

Eher nicht.
An dieser Stelle ist float sicher nicht dein Problem.

Der float wird eher dann zum Problem, wenn du 100 mal 1 Cent einwirfst 
und am Ende dann nicht 1 Euro im Sparschwein ist. Aber das ist momentan 
eine andere Baustelle.

Sieh dir mal auf Byte Ebene an was du schreibst und was du liest.

PS: Der Datentyp der Wahl um mit Bytes zu arbeiten ist 'unsigned char'. 
Auf keinen Fall aber ist er 'char'.

von Detlev T. (detlevt)


Lesenswert?

Hallo Gregor,

beim gcc-avr kann man auch ganze Speicherblöcke lesen/schreiben. Geht 
das bei deinem Compiler nicht? Das wäre vielleicht eine Alternative.

Für diesen speziellen Fall würde ich allerdings auch zu integer-Werten 
raten.

Gruß, DetlevT

von Karl H. (kbuchegg)


Lesenswert?

Detlev T. schrieb:
> Hallo Gregor,
>
> beim gcc-avr kann man auch ganze Speicherblöcke lesen/schreiben. Geht
> das bei deinem Compiler nicht? Das wäre vielleicht eine Alternative.

Achtung: Er benutzt einen externen EEPROM.
Warum weiß ich zwar nicht, aber ist so.

von Gregor (Gast)


Lesenswert?

Die double oder float variable erschien fürs erste als bequemer zum 
rechnen!

Den externen EEPROm wollte ich nutzen, da der eine wesentlich mehr 
Schreibzyklen garantiert (mehr als 1.000.000).

Finds schade, dass die DIee so nicht funktioniert(warum auch immer) und 
ich keine Lösung daüfr finde!?!

Werde dann wohl doch auf den internen EEPROM zurück greifen und die 
fertigen Funktionen ausprobieren!

Ps: char oder unsigned char macht keinen unterschied( Ergebnis: 12.08)

von Falk B. (falk)


Lesenswert?

@  Karl heinz Buchegger (kbuchegg) (Moderator)

>> ich verwnede double bzw float um auch 1 Cent münzen addieren zu können!

>Ist noch lange kein Grund.
>Mit einem uint32_t kann man wunderbar auch Cent zählen.
>Ganze 4294967296 davon.
>Das sind dann ~ 42949672 Euro, oder knapp 43 Milliarden Euro.
>Sicher das das nicht reicht?

Na Karl Heinz, hat dich die Finanzkrise auch erwischt? Bei mir sind das 
nur 43 MILLIONEN Euro. ;-)

MFg
Falk

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

> bei mir sind das nur 43 MILLIONEN Euro. ;-)

Jetzt müßte man aber auch noch das minimale virtuelle Volumen des 
virtellen Sparschweins ausrechnen, wenn mit größtmöglicher mechanischer 
virtueller Münze eingezahlt wird .....

von Karl H. (kbuchegg)


Lesenswert?

Gregor schrieb:
> Die double oder float variable erschien fürs erste als bequemer zum
> rechnen!

Das schaut aber nur so aus.
Ob du intern in Euro (mit Kommazahlen) oder in Cent (ohne Kommazahlen) 
rechnest, macht aber einen Riesenunterschied. Sowohl was Genauigkeit 
(speziell beim Aufsummieren) als auch Geschwindigkeit angeht.

> Den externen EEPROm wollte ich nutzen, da der eine wesentlich mehr
> Schreibzyklen garantiert (mehr als 1.000.000).

Dann rechne dir doch einmal aus, wieviele Einzahlungen du auf dein 
internes EEPROM machen musst, ehe die garantierte Lebensdauer hinüber 
ist und wie lang das in etwa durchhalten wird, wenn du, sagen wir mal, 
alle 2 Tage eine Einzahlung machst.

Mit diesen (zugegeben angenommenen) Rahmenbedingungen wird das EEPROM 
noch funktionieren, wenn deine Kinder selber schon Opa sind.

Und ausserdem sagt niemand, das das EEPROM sofort ausfällt, wenn die von 
Atmel garantierte Zyklenzahl erreicht ist.

> Werde dann wohl doch auf den internen EEPROM zurück greifen und die
> fertigen Funktionen ausprobieren!

Du gibst zu schnell auf.
Da gibt es ein Problem und keiner weiß was genau das Problem ist. Ist 
immer schlecht, wenn man den Dingen nicht auf den Grund geht. Eines 
Tages wirst du vielleicht wirklich ein externes EEPROM benötigen, und 
was dann?

>
> Ps: char oder unsigned char macht keinen unterschied( Ergebnis: 12.08)

Das macht nichts.
Es geht ums Prinzip. Byte == unsigned char
AUch wenn es bei dir momentan keinen Unterschied macht. Es gibt einen! 
Und der schlägt genau dann zu, wenn du es am allerwenigsten gebrauchen 
kannst.

von Gregor (Gast)


Lesenswert?

Hallo,

ich hab jetzt mal das interne EEPROM genutzt.

Das scheint zu funnktionieren!
Hab mich da an das tutorial gerichtet!

Jeztt hab ich das Problem, dass ich schreiben und lesen kann, doch wenn 
ich es so compiliere, dass ich nur lese, bekomme ich ein "nan" raus!

Ich wollte nun versuchen das mit festen Adressen im EEPROM zu 
realisieren, doch irgendwie klappts nicht und ich weiss jetzt nicht so 
genau, wie ich das lösen soll!?!

Kann mir einer nen tipp geben, wie ich das machen kann? Hab das im 
Tutorial nicht richtig verstanden.
1
float eeFooFloat EEMEM;
2
3
oid schreiben(float dWert)
4
{
5
  union {
6
    float r;
7
    uint8_t i[sizeof(float)];
8
  } u;
9
10
  u.r = dWert;
11
12
  // Float in EEPROM
13
  eeprom_write_block(&(u.r),&eeFooFloat,sizeof(float));
14
}
15
16
float lesen(void)
17
{
18
  float dWert = 00.01;
19
  
20
  union {
21
    double r;
22
    uint8_t i[sizeof(float)];
23
  } u;
24
  u.r = dWert;
25
26
  eeprom_read_block(&(u.r),&eeFooFloat,sizeof(float));
27
  return u.r;
28
   
29
}

Wär gnaz hilfreich, wenn jemand nen Code-Schnipsel parat hätte, was ich 
ich wie und wo ändern sollte!?!

danke im vorraus

von Karl H. (kbuchegg)


Lesenswert?

Gregor schrieb:

> Das scheint zu funnktionieren!
> Hab mich da an das tutorial gerichtet!

die union brauchst du doch in Wirklichkeit gar nicht
1
float eeFooFloat EEMEM;
2
3
oid schreiben(float dWert)
4
{
5
  eeprom_write_block( &dWert, &eeFooFloat, sizeof(float));
6
}
7
8
float lesen(void)
9
{
10
  float dWert = 00.01;
11
12
  eeprom_read_block( &dWert, &eeFooFloat, sizeof(float) );
13
  return dWert;
14
}

> Jeztt hab ich das Problem, dass ich schreiben und lesen kann, doch wenn
> ich es so compiliere, dass ich nur lese, bekomme ich ein "nan" raus!

Kann es sein, dass du dir beim Flashen des neuen Programms das EEPROM 
löscht?

von Gregor (Gast)


Lesenswert?

Das weiss ich nicht!

Wenn die standard eeprom.h dies beim Programmstart tut, dann ja!?! 
Ansonsten ruf ich eigentlich nichts auf, dass mir das EEPROM löschen 
sollte!?!

von Karl H. (kbuchegg)


Lesenswert?

Gregor schrieb:
> Das weiss ich nicht!

Das solltest du aber!

> Wenn die standard eeprom.h dies beim Programmstart tut, dann ja!?!
> Ansonsten ruf ich eigentlich nichts auf, dass mir das EEPROM löschen
> sollte!?!

Das hat nichts mit C File oder Header File oder dergleichen zu tun.
Du musst in dem Brenn-Programm nach einer Einstellung suchen, mit der du 
dem Brennprogramm mitteilst: "nur das Flash neu beschreiben, das EEPROM 
in Ruhe lassen!"  Alternativ gibt es auch noch die Möglichkeit, mittels 
einer Fuse das EEPROM vor Überschreiben von aussen zu schützen.

von Gregor (Gast)


Lesenswert?

SUPER!!!!!!!!!!!!!

Danke für den Tipp!!!

Einstellugnen bei den Fuses:

EESAVE --> Hacken rein, und das wars!

Mensch bin ich blöd! Da hätt ich auch selber drauf kommen müssen ;)

Danke!

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.