Adding/changing EEPROM definitions on ATMEL MEGA 8 Bit
Möglich ist, dass die geschilderte Lösung bereits von anderen beschrieben wurde, ich konnte trotz intensiver Suche im Web aber nichts Vergleichbares finden.
Ein oft gefragtes Problem in verschiedenen Foren ist die korrekte und dauerhafte Platzierung von Programm-Variablen im EEPROM. Wichtige Gründe dafür sind:
Zugriff mehrerer Prozessoren auf denselben EEPROM.
Änderungen oder Zusätze zur bestehenden EEPROM Struktur, wo sich inzwischen die Initialwerte geändert haben und die Änderungen nicht überprogrammiert werden dürfen.
Andere Gründe überlasse ich der Phantasie der User.
Ich habe nun folgende Lösung gefunden, die mir gegenüber den im Web beschriebenen praktikabler erscheint, aber dennoch ein exaktes und gründliches Arbeiten erfordert.
Ich bin noch nicht am Ende meiner Untersuchungen, möchte hier trotzdem den Stand der Dinge niederschreiben (ich sitze gerade im Ausland im Hotel, da hab ich Zeit).
Die Lösung ist unter folgenden Randbedingungen entstanden:
AVR Studio 6.2 Standard-Installation
Alle compiler-options standard. The only one I changed is Optimization from O1 to Os.
Ich weiß inzwischen, dass die Compiler-Options eine Rolle spielen, ob das Beschriebene funktioniert oder nicht. Ich will das in den nächsten Wochen noch gründlicher untersuchen.
Falls an dem Thema weiterhin Interesse besteht, teile ich meine Erkenntnisse gerne mit und bitte um Rückfragen.
Zur Überprüfung der Ergebnisse habe ich das Freeware-Tool “Hexplorer” verwendet. Damit ist es möglich, die vom Compiler erzeugte *.eep Datei (INTEL-HEX Format) zu importieren und sich das Ergebnis, das in den EEPROM gelangt, als reine Binärdarstellung anzuschauen.
Schritt 1:
Definieren einer, aber auch nur einer Struktur, die alle EEPROM Variablen enthält:
typedef struct
{
//Dummy for Byte 0
uint8_t EE_DUMMY; // don't use it
//a byte
uint8_t EE_HW_ADDR;
:
:
// a word
uint16_t EE_Def_Buz_Freq;
:
:
//an array
uint8_t EE_BLM[48];
} EE_data_t;
Zweiter Schritt:
Erzeugen Sie eine Instanz dieser Typdefinition. Hier ist es möglich, die Variablen mit Initialwerten zu belegen. Belegen Sie alle Werte, auch NULL-Werte !!
Schicken Sie die Instanz in den EEPROM Bereich durch Verwendung der EEMEM Klausel.
EE_data_t EEMEM EE_Data =
{
//** uint8_t EE_DUMMY
0x00, //don't use it
//Hardware address (DIP switch configuration)
//** uint8_t EE_HW_ADDR
0x55, // a funny address as joker
//**uint8_t EE_BLM[48]
{
254, LED_MSK_RED_VAL, 40,
250, LED_MSK_YEL_VAL, 45,
255, LED_MSK_GRN1_VAL, 35,
254, LED_MSK_GRN2_VAL, 30,
254, LED_MSK_RED_VAL, 44
}
Stellen Sie unbedingt sicher, dass die Instanz mitsamt den Initialwerten absolut identisch ist mit der zuvor getätigten Struktur-Definiton !
Das ist der kritischste Punkt an dem Verfahren.
Dritter Schritt:
Testen:
Verwenden Sie zuerst eine „Minidefiniton“ mit einigen wenigen Variablen. Mischen Sie dabei unterschiedliche Datentypen.
Projekt compilieren. Dann benutzen Sie das o.g. Tool “Hexplorer” oder was auch immer Sie haben, und importieren Sie das *.eep File, das beim ATMEL Studio standardmäßig im Projektordner, Unterordner „debug“ zu finden ist. („Datei >> Importieren >> INTEL Hex Format“, die Fehlermeldung mit der fehlenden Startadresse ignorieren, das macht das Programmiermodul später alleine).
Drucken Sie es aus, schreiben Sie es ab oder machen Sie ein Screenshot.
Dann fügen Sie an das Ende der Strukturdefinition eine weitere Variable an und belegen Sie diese auch in der Instanz mit einem (beliebigen) Wert.
Projekt neu compilieren. EEP File neu importieren. Vergleichen Sie Byte für Byte das alte und das neue EEP-File. Der zusätzliche Variablenwert muß am Ende des Hexdumps stehen. Der Rest muss genauso aussehen wie vorher. Dann ist alles richtig.
Vierter Schritt:
Jetzt ist es soweit, dass Sie trotz aller Umsicht und Vorausschau eine Änderung an Ihrer EEPROM Struktur vornehmen müssen. Und alles soll am alten Platz bleiben.
Fall 1: Es kommt was Neues hinzu.
Platzieren Sie zusätzliche Variablen immer am Ende der Struktur-Definition! Vergessen Sie nicht, bei der Instanz-Erzeugung passende Initialwerte einzutragen !
Fertig. Alle alten Werte bleiben auf derselben absoluten Adresse.
Fall 2: Eine bestehende Definition muss geändert werden.
Sie müssen aus einem Byte ein Word machen. Oder zu einer Array-Definition sind zusätzliche Spalten oder Zeilen erforderlich.
Lassen Sie die „alte“ Definition da, wo sie ist, und geben Sie ihr nur einen anderen Namen. Das ist jetzt verlorener Platz. Hängen Sie die geänderte Definition hinten dran mit dem bisher benutzten Namen ! Vergessen Sie nicht, auch hier der angehängten Strukturdefinition passende Initialwerte zu vergeben.
Eine andere Lösung:
Szenario ist folgendes: Sie haben ihr Device während der Produktion programmiert, incl. EEPROM-Werten. Während des allgemeinen Betriebs haben sich einige Werte im EEPROM geändert. Sie sind sich nicht sicher, dass z.B. ein Service-Mann mit einem Handheld-Programmer die EEPROM-Werte überschreibt, weil die EESAVE-Fuse zwar gesetzt ist, aber manche Handhelds die Fuse kurzerhand rücksetzen vor der Programmierung.
Dann hilft folgendes:
Voraussetzung ist, dass im Flash noch genügend Platz für den EEPROM Inhalt ist.
Dann platzieren Sie die Instanz Ihrer predefined Values im Flash, indem Sie die EEMEM Klausel weglassen:
alt: EE_data_t EEMEM EE_Data = ….
neu: EE_data_t EE_Data =
Nun müssen Sie dafür sorgen, dass diese Werte in das EEPROM gelangen, aber nur, wenn noch nichts drinsteht:
Lesen Sie aus dem EEPROM einen unveränderlichen signifikanten Wert aus und vergleichen Sie ihn mit der Predefinition im Flash. Ist dieser Wert noch nicht im EEPROM, schreibe ich die Default-Werte rein. Ich benutze hier z.B. eine (unveränderliche) Variable „Application ID“:
void Check_EEPROM_Defaults(void)
{
uint8_t Appl_ID[8];
eeprom_read_block( Appl_ID , &EE_Data.EE_APPL_ID , sizeof(EE_Data.EE_APPL_ID ));
if(memcmp((void*)&Appl_ID[0] , &EE_Data.EE_APPL_ID[0], 8) != 0)
{ //no Application ID wrote earlier, write default values to EEPROM !
eeprom_write_block( (void*)&EE_Data,&EE_Data_Top,sizeof(EE_Data) );
}
}
Die Zieladresse für eeprom_write_block definiere ich einfacherweise mit:
uint8_t EEMEM EE_Data_Top;
Das darf dann aber die einzige “EEMEM” Definition im ganzen Projekt sein !!
Benötigen Sie für nachträgliche Eintragungen vordefinierte Werte im EEPROM, müssen diese "manuell" durch die Software erzeugt werden.
Möglich ist, dass die geschilderte Lösung bereits von anderen beschrieben wurde, ich konnte trotz intensiver Suche im Web aber nichts Vergleichbares finden.
Ein oft gefragtes Problem in verschiedenen Foren ist die korrekte und dauerhafte Platzierung von Programm-Variablen im EEPROM. Wichtige Gründe dafür sind:
Zugriff mehrerer Prozessoren auf denselben EEPROM.
Änderungen oder Zusätze zur bestehenden EEPROM Struktur, wo sich inzwischen die Initialwerte geändert haben und die Änderungen nicht überprogrammiert werden dürfen.
Andere Gründe überlasse ich der Phantasie der User.
Ich habe nun folgende Lösung gefunden, die mir gegenüber den im Web beschriebenen praktikabler erscheint, aber dennoch ein exaktes und gründliches Arbeiten erfordert.
Ich bin noch nicht am Ende meiner Untersuchungen, möchte hier trotzdem den Stand der Dinge niederschreiben (ich sitze gerade im Ausland im Hotel, da hab ich Zeit).
Die Lösung ist unter folgenden Randbedingungen entstanden:
AVR Studio 6.2 Standard-Installation
Alle compiler-options standard. The only one I changed is Optimization from O1 to Os.
Ich weiß inzwischen, dass die Compiler-Options eine Rolle spielen, ob das Beschriebene funktioniert oder nicht. Ich will das in den nächsten Wochen noch gründlicher untersuchen.
Falls an dem Thema weiterhin Interesse besteht, teile ich meine Erkenntnisse gerne mit und bitte um Rückfragen.
Zur Überprüfung der Ergebnisse habe ich das Freeware-Tool “Hexplorer” verwendet. Damit ist es möglich, die vom Compiler erzeugte *.eep Datei (INTEL-HEX Format) zu importieren und sich das Ergebnis, das in den EEPROM gelangt, als reine Binärdarstellung anzuschauen.
Schritt 1:
Definieren einer, aber auch nur einer Struktur, die alle EEPROM Variablen enthält:
typedef struct
{
//Dummy for Byte 0
uint8_t EE_DUMMY; // don't use it
//a byte
uint8_t EE_HW_ADDR;
:
:
// a word
uint16_t EE_Def_Buz_Freq;
:
:
//an array
uint8_t EE_BLM[48];
} EE_data_t;
Zweiter Schritt:
Erzeugen Sie eine Instanz dieser Typdefinition. Hier ist es möglich, die Variablen mit Initialwerten zu belegen. Belegen Sie alle Werte, auch NULL-Werte !!
Schicken Sie die Instanz in den EEPROM Bereich durch Verwendung der EEMEM Klausel.
EE_data_t EEMEM EE_Data =
{
//** uint8_t EE_DUMMY
0x00, //don't use it
//Hardware address (DIP switch configuration)
//** uint8_t EE_HW_ADDR
0x55, // a funny address as joker
//**uint8_t EE_BLM[48]
{
254, LED_MSK_RED_VAL, 40,
250, LED_MSK_YEL_VAL, 45,
255, LED_MSK_GRN1_VAL, 35,
254, LED_MSK_GRN2_VAL, 30,
254, LED_MSK_RED_VAL, 44
}
Stellen Sie unbedingt sicher, dass die Instanz mitsamt den Initialwerten absolut identisch ist mit der zuvor getätigten Struktur-Definiton !
Das ist der kritischste Punkt an dem Verfahren.
Dritter Schritt:
Testen:
Verwenden Sie zuerst eine „Minidefiniton“ mit einigen wenigen Variablen. Mischen Sie dabei unterschiedliche Datentypen.
Projekt compilieren. Dann benutzen Sie das o.g. Tool “Hexplorer” oder was auch immer Sie haben, und importieren Sie das *.eep File, das beim ATMEL Studio standardmäßig im Projektordner, Unterordner „debug“ zu finden ist. („Datei >> Importieren >> INTEL Hex Format“, die Fehlermeldung mit der fehlenden Startadresse ignorieren, das macht das Programmiermodul später alleine).
Drucken Sie es aus, schreiben Sie es ab oder machen Sie ein Screenshot.
Dann fügen Sie an das Ende der Strukturdefinition eine weitere Variable an und belegen Sie diese auch in der Instanz mit einem (beliebigen) Wert.
Projekt neu compilieren. EEP File neu importieren. Vergleichen Sie Byte für Byte das alte und das neue EEP-File. Der zusätzliche Variablenwert muß am Ende des Hexdumps stehen. Der Rest muss genauso aussehen wie vorher. Dann ist alles richtig.
Vierter Schritt:
Jetzt ist es soweit, dass Sie trotz aller Umsicht und Vorausschau eine Änderung an Ihrer EEPROM Struktur vornehmen müssen. Und alles soll am alten Platz bleiben.
Fall 1: Es kommt was Neues hinzu.
Platzieren Sie zusätzliche Variablen immer am Ende der Struktur-Definition! Vergessen Sie nicht, bei der Instanz-Erzeugung passende Initialwerte einzutragen !
Fertig. Alle alten Werte bleiben auf derselben absoluten Adresse.
Fall 2: Eine bestehende Definition muss geändert werden.
Sie müssen aus einem Byte ein Word machen. Oder zu einer Array-Definition sind zusätzliche Spalten oder Zeilen erforderlich.
Lassen Sie die „alte“ Definition da, wo sie ist, und geben Sie ihr nur einen anderen Namen. Das ist jetzt verlorener Platz. Hängen Sie die geänderte Definition hinten dran mit dem bisher benutzten Namen ! Vergessen Sie nicht, auch hier der angehängten Strukturdefinition passende Initialwerte zu vergeben.
Eine andere Lösung:
Szenario ist folgendes: Sie haben ihr Device während der Produktion programmiert, incl. EEPROM-Werten. Während des allgemeinen Betriebs haben sich einige Werte im EEPROM geändert. Sie sind sich nicht sicher, dass z.B. ein Service-Mann mit einem Handheld-Programmer die EEPROM-Werte überschreibt, weil die EESAVE-Fuse zwar gesetzt ist, aber manche Handhelds die Fuse kurzerhand rücksetzen vor der Programmierung.
Dann hilft folgendes:
Voraussetzung ist, dass im Flash noch genügend Platz für den EEPROM Inhalt ist.
Dann platzieren Sie die Instanz Ihrer predefined Values im Flash, indem Sie die EEMEM Klausel weglassen:
alt: EE_data_t EEMEM EE_Data = ….
neu: EE_data_t EE_Data =
Nun müssen Sie dafür sorgen, dass diese Werte in das EEPROM gelangen, aber nur, wenn noch nichts drinsteht:
Lesen Sie aus dem EEPROM einen unveränderlichen signifikanten Wert aus und vergleichen Sie ihn mit der Predefinition im Flash. Ist dieser Wert noch nicht im EEPROM, schreibe ich die Default-Werte rein. Ich benutze hier z.B. eine (unveränderliche) Variable „Application ID“:
void Check_EEPROM_Defaults(void)
{
uint8_t Appl_ID[8];
eeprom_read_block( Appl_ID , &EE_Data.EE_APPL_ID , sizeof(EE_Data.EE_APPL_ID ));
if(memcmp((void*)&Appl_ID[0] , &EE_Data.EE_APPL_ID[0], 8) != 0)
{ //no Application ID wrote earlier, write default values to EEPROM !
eeprom_write_block( (void*)&EE_Data,&EE_Data_Top,sizeof(EE_Data) );
}
}
Die Zieladresse für eeprom_write_block definiere ich einfacherweise mit:
uint8_t EEMEM EE_Data_Top;
Das darf dann aber die einzige “EEMEM” Definition im ganzen Projekt sein !!
Benötigen Sie für nachträgliche Eintragungen vordefinierte Werte im EEPROM, müssen diese "manuell" durch die Software erzeugt werden.