Variablen im EEPROM verwalten - Ideen / Anregungen gesucht



  • Liebe Mitglieder,

    ich habe eine Bibliothek geschrieben, die mir beliebige Werte in ein EEPROM schreibt und natürlich auch rücklesen kann, um einem Controller den völligen Gedächtnisverlust bei Stromausfall zu ersparen; der Compiler kann diese Form "Zusatzspeicher" nicht unterstützen.

    Ich habe natürlich bei anderen erstmal "gespickt", aber die hauen zumeist die externen Adressen fest in den Code. Das geht bei einer Handvoll Parameter, aber bei einem Setup von über 30 Parametern per Zettelchenhaufen zu überwachen, daß jede Variable wirklich ihr freies Adresslein erhält, ist nicht praktikabel.

    Also habe ich's so gemacht, daß ein Zähler mitläuft, der die gerade belegte Adresse liefert und danach auf die nächste freie Adresse zeigt - das geht ganz gut, wenn die Initialisierung immer in der gleichen Reihenfolge abläuft.
    Nachteilig ist, daß man zu jeder Variablen die Adresse zur Laufzeit aufheben und jede hinzugekommene Variable bei der Entwicklung hinten angelegt oder das EEPROM vor Programmstart geputzt werden muß, sonst droht Datenschrott.

    Das geht im jetzigen Fall nicht mehr, weil mir das RAM fehlt, jede Adresse aufzuheben - na gut, dann behandle ich das zur Laufzeit wie ein Literal, speichere den Namensstring einfach mit und mache eine Volltextsuche drüber. Das funktioniert zwar schon, aber ein EEPROM ist nicht wirklich schnell, also die sequentielle Suche dauert bei den "hinteren" Variablen schon ein paar Sekundenbruchteile, kein Drama, aber das Gefühl der "Knackigkeit" ist dahin. Knopfdruck = "blipp" wird anders gefühlt als Knopfdruck === "blipp".

    Die Kiste ist zwar schon fertig, aber mich stört sowas, obwohl's nur ein Schönheitsfehler ist. Ewig viel Mühe will ich da auch nicht mehr dranhängen, deswegen Hashtables aufzuziehen ist unangemessen.

    Aber wenn jemand eine Idee für 'nen "Quickie" hat, würd' ich da noch ein bißchen Aufwand reinhängen.

    Danke für's Kopfschiefhalten ;)!



  • du könntest die variablen alle in eine struct legen:

    struct eeprom_vars
    {
      int var1;
      char some_text[...];
      double dx;
      ...
    };
    

    und dann mit solchen makros

    #define FIELD_OFFSET(type, field) ((long)&(((type *)0)->field))
    #define FIELD_SIZE(type, field) sizeof(((type *)0)->field)
    

    über die EEPROM schreib/lese -funktionen darauf zugreifen

    #define READ_EEPROM_VAR(var,dest)\
    	eeprom_read_function(FIELD_OFFSET(struct eeprom_vars,var),\
    	FIELD_SIZE(struct eeprom_vars,var))
    

    das geht jetzt mal davon aus, dass sich der anfang der struct im EEPROM an adresse 0 befindet (sonst musste einen offset dazu addieren).
    🙂



  • Danke NVRAM-freak,

    das dürfte wirklich funktionieren und vor allem viel Tipparbeit sparen 🙂

    Hat halt auf den ersten Blick den Nachteil, daß man immer die ganze struct schreibt und liest, muß mal sehen, ob das meine Applikation unbeschadet überlebt und nicht doch dedizierte Einzelzugriffe nötig sind.
    Da ich das sowieso auf mehrere structs aufteilen müßte, wird es wohl die Kombination, so daß ich jetzt nur noch die Adressen ganzer Blöcke aufheben muß.

    Ich probier' das auf jeden Fall aus - in diesem Sinne nochmal Danke! 😃



  • pointercrash() schrieb:

    Hat halt auf den ersten Blick den Nachteil, daß man immer die ganze struct schreibt und liest...

    nein, du schreibst und liest immer nur ein struct member. dafür sind ja die makros FIELD_SIZE und FIELD_OFFSET da.
    🙂



  • damit es klarer wird, hab' ich eben mal eine kleine demo gecodet (compiliert und läuft auf x86-pc, visual studio 2005):

    #include <stdio.h>
    #include <string.h>
    
    // deine EEPROM variablen
    struct eeprom_vars
    {
      int var1;
      char some_text[100];
      double dx;
      // to be continued...
    };
    
    //--------------- die makros ----------------
    
    #define FIELD_OFFSET(type, field) ((long)&(((type *)0)->field))
    #define FIELD_SIZE(type, field) sizeof(((type *)0)->field)
    
    #define READ_EEPROM_VAR(var,dest)\
      eeprom_read_function(FIELD_OFFSET(struct eeprom_vars,var),\
      &dest,\
      FIELD_SIZE(struct eeprom_vars,var))
    
    #define WRITE_EEPROM_VAR(var,src)\
      eeprom_write_function(FIELD_OFFSET(struct eeprom_vars,var),\
      &src,\
      FIELD_SIZE(struct eeprom_vars,var))
    
    //----------------- EEPROM simulator ------------------- 
    
    // das 'EEPROM'
    char eeprom[0x1000];
    
    // liest 'size' bytes von der eeprom-adresse 'address' ins RAM ab adresse 'dest'
    void eeprom_read_function (int address, void *dest, int size) 
    {
       memcpy (dest, eeprom+address, size);
    }
    
    // brennt 'size' bytes vom RAM von adresse 'src' ins EEPROM an adresse 'address' 
    void eeprom_write_function (int address, void *src, int size)
    {
       memcpy (eeprom+address, src, size);
    }
    
    //----------------- demo ------------------- 
    
    void main (void)
    {
       // das wird ins EEPROM geschrieben
       double d1 = 1.2345;
       char c1[100] = "hello from eeprom";
    
       // ...und das wird gelesen
       double d2;
       char c2[100];
    
       // schreibt d1 in eeprom-variable dx
       WRITE_EEPROM_VAR (dx, d1);
       // schreibt c1 in eeprom-variable some_text
       WRITE_EEPROM_VAR (some_text, c1);
    
       // liest eeprom-variable dx nach d2
       READ_EEPROM_VAR (dx, d2);
       // liest eeprom-variable some_text nach c2
       READ_EEPROM_VAR (some_text, c2);
    
       // und?
       printf ("%f %s\n", d2, c2);
    }
    


  • Embedded-Freak schrieb:

    pointercrash() schrieb:

    Hat halt auf den ersten Blick den Nachteil, daß man immer die ganze struct schreibt und liest...

    nein, du schreibst und liest immer nur ein struct member. dafür sind ja die makros FIELD_SIZE und FIELD_OFFSET da.
    🙂

    Danke nochmal,

    ich hatte nach Ideen gesucht und quasi 'ne schlüsselfertige Lösung bekommen. Es wird wirklich einzeln zugegriffen, hab's schon auf meinem Target am Laufen 😃
    Und jetzt schau' ich mir genauer an, was der Präprozessor beim Auflösen der Makros wirklich tut, damit ich auch wirklich kapier', wie es funktioniert 🙄

    Prima Sache jedenfalls! 🙂



  • pointercrash() schrieb:

    ich hatte nach Ideen gesucht und quasi 'ne schlüsselfertige Lösung bekommen.

    no problem, ich dachte mir dass mein erstes posting etwas unverständlich war und dann musste ich's wieder geradebiegen. eine andere möglichkeit wäre z.b. ein ausgewachsenes filesystem: http://elm-chan.org/fsw/ff/00index_e.html
    🙂



  • fat32-freak schrieb:

    no problem, ich dachte mir dass mein erstes posting etwas unverständlich war und dann musste ich's wieder geradebiegen. eine andere möglichkeit wäre z.b. ein ausgewachsenes filesystem: http://elm-chan.org/fsw/ff/00index_e.html
    🙂

    Hi "Irgendwas-" freak, 😉
    daß das etwas kryptisch für mich war, liegt eher in meinen mangelhaften Kenntnissen, was man mit dem Präprozessor so alles anstellen kann 🙄
    Als Filesystem habe ich bereits einen Port von http://sourceforge.net/projects/efsl/ für 'ne andere Sache auf meine Targets fertig, aber in diesem Fall wäre das "oversized" gewesen, weil ich nichtmal das kB RAM frei hab'. 😞



  • Moin

    Ich hab vor kurzem so was ähnliches implementiert. Zwar nicht in einem EEPROM sondern auf nem FLASH. Ist aber im prinzip das gleiche.

    Als erstes hab ich mir eine Datenstruktur angelegt, in der Addresse und Grösse der Felder hinterlegt sind. Zusätzlich dazu hab ich mir eine Enumeration angelegt für den zugriff. Die Datenstruktur enthält genausoviele Einträge wie Enumerationselemente existieren.

    /* enumeration */
    enum CONF_DATA_ID = 
    {
       DATA_ID1 = 0,
       DATA_ID2,
       ...
       CONF_MAX_DATA  /* must be the last entry */
    };
    
    /* structure for data elements */
    typdef CONF__DATA_INFO_STRUCKT
    {
        uint32_t adr,
        uint32_t size
    } CONF__DATA_INFO;
    
    CONF__DATA_INFO CONF_DataInfo[CONF_MAX_DATA] = 
    {
       {0,4},
       {4,2},
       {6,1},
    
    };
    
    uint32_t CONF__ReadUInt32( CONF_DATA_ID elementid)
    {
      if ( elementid < CONF_MAX_DATA )
      {
       EEPROM_Read(CONF_DataInfo[elementid].adr, CONF_DataInfo[elementid].size, buffer);
      }
    }
    
    ... = CONF__ReadUInt32(DATA_ID1);
    

    Meine lösung hat den nachteil, das man die addressen von hand ausrechnen muss, und aufpassen muss keine überlappungen zu produzieren.

    Ich persönlich versuche Macros soweit wie möglich zu umgehen.

    Ein anderer Punkt ist das Bit Aligment das kann ggf eine 8bit variable auf 32bit aufblähen. Kann bei meiner lösung nicht passieren, da ich die speicherzuordnung selber definiere.

    Wichtig ist vileicht noch am anfang eine Versionsnummer der Datenstruktur zu hinterlegen, für zukünftige erweiterungen. Damit kann man ggf prüfen ob die "DB" aktuell ist und ggf eine konvertierungsrotine drüberlaufen lassen.

    gruss Termite



  • Termite schrieb:

    Ich persönlich versuche Macros soweit wie möglich zu umgehen.

    du bist C++ programmierer, oder? in dem fall könnteste das mit templates machen.
    🙂



  • Nein C mit minimalistischen system rescourcen mit 64k ram und 1mb flash (wobei es auch noch kleiner gehen kann). c++ währe eine alternative gewesen. Aber es gab ein paar alauf schwierigkeiten weswegen doch wieder c eingesetzt wurde. aber das hat hier eigentlich nichts zu suchen.



  • Termite schrieb:

    c++ währe eine alternative gewesen. Aber es gab ein paar alauf schwierigkeiten weswegen doch wieder c eingesetzt wurde.

    jaja, das übliche. 😃
    sei froh, dass ihr die probleme rechtzeitig erkannt habt.


Anmelden zum Antworten