EEPROM Read and write memory ATtiny85

Hallo zusammen,

ich habe folgendes Problem, ich möchte gerne den verfügbaren Speicher verwalten. es soll unbestimmt oft eine 16bit Variable gespeichert werden. Dazu möchte ich gerne den verfügbaren Speicher durchlaufen. Dazu der Ansatz immer drei Byte zu nutzen:

| Flip | High Byte | Low Byte |

Der Inhalt von Flip wird beim speichern invertiert, also ist dieser 0x00 oder 0xFF, so dass ich erkennen kann, wo zuletzt gespeichert worden ist.

Das Problem dabei ist, dass ich nach dem Auslesen des ersten Flips (erste Speicherzelle) 0xFF erwarte, da der Speicher im default auf allen Zellen 0xFF hinterlegt hat. Nun soll der erste Speicher Wert den Inhalt 0x00 erhalten und die beiden nachfolgenden Plätze den Speicherwert der 16bit Variable.

Beim auslesen durch den programmer erhalte ich folgendes Ergebniss:

:10000000 FF0003FF0002FF0001FFFFFFFFFFFFFFF4
:10001000 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

wenn ich in dem Code die else Abfrage raus lasse funktioniert es:

:10000000 000003000002000001FFFFFFFFFFFFFFF1
:10001000 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

Hat jemand eine Idee was ich falsch mache?

bin um jede Hilfe dankbar :slight_smile:

Der Code ist mit platformIO in einem Arduino framwork geschrieben

#include <Arduino.h>
#include <EEPROM.h>

// Speichersetup
#define START_ADRESS   0
#define RETURN_ADRESS  15

uint16_t limitation = 3;

uint16_t adress = START_ADRESS;
uint16_t flip;


void save();
void setup() {
}

void loop() {
  if(limitation > 0){
    save();
    limitation--;
  }
}

void save(){
  
  flip = EEPROM.read(adress);
  if(flip == 0xFF){
    EEPROM.write(adress + 1, (limitation >>8)&0xFF); // Speichere HBYTE           
    EEPROM.write(adress + 2, limitation&0xFF);       // Speichere LBYTE
    EEPROM.write(adress, 0x00);   
  }

  // ohne die else Abfrage funtioniert es?
  else{
    EEPROM.write(adress, 0xFF);   
  }

  if(adress < RETURN_ADRESS){
    adress = adress + 3;
  }
  else{
    adress = START_ADRESS;
  }
}

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Dein Flip ist 16 Bit, du ließt aber nur 8.

Kommt dir das nicht komisch vor?

Ehrlich?
Ich halte nicht viel davon, wie du das tust!

Hier mein Sermon zu dem eingebauten EEPROM:

Versuch mal ein delay(500); ins loop(9 einzusetzen. Für EEPRPM.Write braucht es etwas Zeit.

Ähmmmm...

All of the read/write functions first make sure the EEPROM is ready to be accessed.

Aus: avr-libc: <avr/eeprom.h>: EEPROM handling

Und ja: Dem kann man Glauben schenken!

ein delay bringt leider nicht die Lösung

Danke für den schnellen Hinweis und die Antwort,

ich habe den Code etwas modifiziert und den flip auf 8bit reduziert, da ich ja auch nur eine Speicherzelle auslese, zusätzlich habe ich in dem else Bereich dafür gesorgt, dass die Information an eine andere Stelle geschrieben wird.

Jetzt scheint bei der ersten Auswertung beide Bedingungen zutreffend zu sein, also habe ich immer noch ein Fehler in meiner Flip-Variablen ???

#include <Arduino.h>
#include <EEPROM.h>


uint16_t limitation = 3;
uint16_t adress = 0;
uint8_t flip = 0;


void save();
void setup() {
}

void loop() {
  if(limitation > 0){
    save();
    delay(200);
    limitation--;
  }
}

void save(){
  
  flip = EEPROM.read(adress);
  if(flip == 0xFF){
    EEPROM.write(adress + 1, (limitation >>8)&0xFF); // Speichere HBYTE           
    EEPROM.write(adress + 2, limitation&0xFF);       // Speichere LBYTE
    EEPROM.write(adress, 0x00);   
  }

  else{
    adress  = adress + 21;
    EEPROM.write(adress,0x05);   
    adress  = adress - 21;
  }
    adress = adress + 3;
}

Die Auswertung des Speicher zeigt:

:10000000 000003000002000001FFFFFFFFFFFFFFF1
:10001000 FFFFFFFFFF05FFFFFFFFFFFFFFFFFFFFEA

Vielen Dank für weitere Hinweise

Was willst du mit deinem Code überhaupt erreichen?

Ich habe eine Leuchte, die eine variable Helligkeit annehmen kann, welche der Nutzer beliebig einstellen kann. In der Schaltung ist eine Überwachung der Spannung realisiert und ein elektrischer Puffer, so dass ein Ausschalten der Leuchte detektiert wird und durch den Puffer genug Zeit bleibt, die letzte Helligkeitsinformation zu speichern, sofern diese sich den geändert hat. Somit habe ich schonmal die notwendigen Speichervorgänge reduziert. Ziel ist es, dass die Leuchte immer mit der letzten Einstellung startet.

Jetzt könnte ich immer in denselben Speicher Bereich meine Helligkeitsinformation hinterlegen bis dieser Bereich irgendwann aufgibt, zugegeben 100.000 Vorgänge klingt erstmal viel, dennoch benötige ich mehr Vorgänge.

Mein zweiter Ansatz war, einen Bereich zu opfern und darin die Anzahl der Speichervorgänge zu hinterlegen, wenn dieser einen bestimmten wert z.B. 50.000 erreicht hat, den Wechsel in den nächsten Bereich durchzuführen, bis dieser ebenfalls seine maximalen Vorgänge erreicht hat usw.

| 0-50.000 | Speicherwert | 0-50.000 | Speicherwert|

Jetzt möchte ich es dynamisch gestalten, so dass der Speicher im kompletten Umfang, gleichmäßig „abgenutzt“ wird.

Durch eine Abfrageroutine zu Beginn wird anhand des Flips ermittelt, wo im Speicher zuletzt gespeichert worden ist und wo die nächste Speicherung erfolgen muss, so dass die Speichervorgänge alternierend durch den Gesamten Speicherbereich erfolgen.

So möchte ich die maximale möglich Anzahl an Speichervorgängen erzielen.

|Flip|HByte|LByte| |Flip|HByte|LByte| |Flip|HByte|LByte| |Flip|HByte|LByte|

So das dann z.B. das im Speicher steht:

0x00 0x00 0x01 | 0x00 0x00 0x02 | 0xFF 0x00 0x03 |…..

-> Anhand des Flips = 0xFF weiß ich, dass in einem Bereich zuvor das letzte Mal gespeichert worden sein muss, klar es gibt noch ein paar mehr Regeln aber so soll, dass prinzipiell ablaufen.

Also ein XY Problem!
Du fragst nach X willst aber Y erledigen.

Sowas!
Händische Adressberechnungen führen immer wieder zu Problemen. Bei dir, bei mir und bei anderen hier im Forum.
(Aber das sagte ich doch schon in meinem Tutorial)

Mein Rat: Lass das!

Dann schau Dir doch mal FRAM an.

Gruß Tommy

Das ist richtig!
Löst aber nicht die logischen Probleme.

Dazu war der Hinweis auch nicht gedacht. Die sind dort genau so vorhanden.

Gruß Tommy

Fram ist auch begrent (auch wenn auf sehr hohe Anzahl von Schreibzyklen ( Lesezyklen) sowas 10 ^15)
Ein externer EEprom Baustein verträgt 1000000 Scheibzyklen
Ansonsten einen RTC mit batteriegebufferten RAM (DS1307 oder DS3232)
Das RAM kann unendlich mal geschrieben werden.

grüße Uwe

du solltest eher den ganzen bereich gleichmäßig nutzen / beschreiben. Siehe dazu auch die Application Note von AVR.

mit "Arduino EEPROM wear leveling" findest du ganz bestimmt Beispiel-Umsetzungen und libraries.

Hallo combie,

so langsam bekomme ich ein Verständnis davon, wie der von dir beschriebenen Umgang mit dem EEPROM funktioniert. Über die .eep Datei wir der EEPROM bzw. dessen Speicherstruktur vordefiniert und im Hauptprogramm kann ich dann auf feste Adressen über die Pointer auf die zuvor definierten Speicherplätze zugreifen und so deren Inhalte auslesen. Somit wird vermieden das über den Compiler die falschen Speicheradressen angesprochen werden.

Ich bin noch Anfänger und habe nicht immer die Richtigen Begriffe für die jeweiligen Verfahren/Anwendungen, bitte um Verständnis.

Da ich mit PlatformIO arbeite, wird die .eep zunächst nicht erzeugt, hierzu wird ein python Skript genutzt, welches das eep file erzeugt, aktuell kann ich direkt aus PlatformIO kompilieren, dass .eep file erzeugen und alles zusammen über „mySmartUSB“ auf den ATtiny 85 übertragen, Jippiii (das waren schon viele Baustellen)

Gerne möchte ich etwas mit dem Code spielen, um ihn besser zu verstehen, im Moment scheitere ich aber an der .eep Datei.

avrdude: ERROR: address 0x81000f out of range at line 2 of .pio/build/attiny85/firmware.eep

mir ist klar, dass der ATtiny 512kb hat, nur nicht wie oder wo ich das definieren kann, so dass die .eep Datei richtig ist?

Diese sieht so aus:

:02000004008179

:0F000000003200003C00014600015000015A0090

:00000001FF

Das Programm:

#include <Arduino.h>
#include <EEPROM.h>
#include <avr/eeprom.h>

/*  Speicherbereich ATtiny 0x0000 bis 0x01FF 512
    EEMEM ist ein Compiler-Attribut und platziert Variablen im EEPROM,
    der Compiler kennt die Speichergröße nicht
*/

struct Speicherblock {
  bool flip;
  uint16_t value;
};

// erzeugt .epp Datei
Speicherblock EEMEM eepromSlot[5] = {
  {false, 50},
  {false, 60},
  {true,  70},
  {true,  80},
  {true,  90}
};
  
// im Programmcode erzeugt
struct Slot{
  bool read_flip;
  uint16_t read_value;
};
Slot readSlot = {false, 0};

void setup(){
  EEPROM.get((int)&eepromSlot[0], readSlot.read_flip);
}

void loop() {
}

und das skript:

Import("env")                                     
import os    

def generate_eep(source, target, env):                
    firmware_path = str(target[0])                        
    eep_path = firmware_path.replace(".elf", ".eep")     
    os.system(f"avr-objcopy -O ihex -j .eeprom {firmware_path} {eep_path}")
    print("EEPROM-Datei erzeugt:", eep_path)          
env.AddPostAction("$BUILD_DIR/firmware.elf", generate_eep)

Kannst du mir, oder jemand anderes, an der Stelle bitte weiterhelfen?

Zwei identische Strukturen, dass ist falsch!
Programmtechnisch ok, aber logisch falsch.
Sowas macht nur Probleme und bringt kein Glück.

Mit Python kenne ich mich nicht aus.
Kann dir aber zeigen wie die avr-objcopy Zeilen bei mir Beispielhaft aussehen.

"E:\\Programme\\arduino\\portable\\avr-gcc\\avr-gcc-15.2.0_mingw32_binutils2.45_avrLibc2.2.1\\bin\\avr-objcopy" -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 "E:\\temp\\Arduino/sketch_aug27d.ino.elf" "E:\\temp\\Arduino/sketch_aug27d.ino.eep"
"E:\\Programme\\arduino\\portable\\avr-gcc\\avr-gcc-15.2.0_mingw32_binutils2.45_avrLibc2.2.1\\bin\\avr-objcopy" -O ihex -R .eeprom "E:\\temp\\Arduino/sketch_aug27d.ino.elf" "E:\\temp\\Arduino/sketch_aug27d.ino.hex"

Ansonsten kannst du das Array im EEPROM auch aus einem Programm füllen, und wenn die EESAVE Fuse gesetzt ist, überlebt der EEPROM Inhalt auch den nächsten Upload bzw. Chip Erase.