Flash-EEPROM-Emulation

Angesichts der vielen Unterschiede, wie Daten in EEPROM und Flash geschrieben werden, wie genau wird die EEPROM-Emulation mit internem Flash durchgeführt?

Außerdem muss Flash in großen Blöcken gelöscht werden, bevor Daten überschrieben werden können. Wenn wir in diesem Fall nur eine kleine Datenmenge in das emulierte EEPROM schreiben wollen, ohne die anderen Inhalte zu stören, wie wird dies erreicht? Welche Softwaretechniken gibt es dafür?

Antworten (3)

Es gibt wahrscheinlich viele verschiedene Algorithmen, um die EEPROM-Emulation zu erreichen, ohne viele unnötige Schreibvorgänge zu erzwingen. Das Hauptkonzept ist wie ein Ledger (außer Werte hängen nicht von vorherigen Werten ab). Der Datensatz oder eine Variable hat viele "Datensätze" im Ledger. Wenn ein neuer Wert geschrieben wird, muss der alte Wert ungültig gemacht werden. Beispielsweise können Sie den alten Wert ungültig machen, indem Sie einen magischen Wert schreiben, der bedeutet, dass "dieser Datensatz ungültig ist". Dann kann ein neuer Datensatz erstellt werden. Das Nachschlagen eines Werts bedeutet, das Hauptbuch zu durchsuchen, um den aktuellen Datensatz zu finden. Alle ungültigen Datensätze können ignoriert werden. Flash-Seiten können gelöscht werden, wenn sie voll sind (die Werte müssen alle ungültig sein oder vor dem Löschen auf eine neue Flash-Seite migriert werden).

Hier ist ein einfaches, konkretes Algorithmusbeispiel:

Nehmen wir an, ich habe einen Datensatz von 6 Bytes. Ich werde 2 Flash-Seiten dem emulierten EEPROM für meinen 6-Byte-Datensatz widmen. Gehen Sie von folgenden Annahmen aus:

  • Angenommen, meine Flash-Seiten sind deutlich größer als 6 Byte.
  • Angenommen, dass Flash nach dem Löschen den Wert 0xFF hat.
  • Angenommen, ich kann den Flash wiederholt neu schreiben, ohne ihn zu löschen, aber nur Einsen in Nullen ändern (1-> 0).
  • Angenommen, meine Flash-Seite ist voll, wenn ich keine weiteren vollständigen Datensätze einfügen kann.
  • Ich achte nicht auf Endianness (Sie werden das Bild bekommen).

Header der Flash-Seite

Ich beginne meine Flash-Seite mit einer Art Kopfzeile, um anzuzeigen, dass ich dort aktiv Daten speichere (oder nicht). Lassen Sie mich 4 Bytes am Anfang der Flash-Seite verwenden, die eine der folgenden Bedingungen angeben:

  1. 0x5555FFFF: Diese Seite wird derzeit verwendet (der aktuelle Wert ist einer der Datensätze).
  2. 0x55555555: Diese Seite ist komplett voll (auf dieser Seite sind keine aktuellen Werte zu finden). (An dieser Stelle sollte die Seite gelöscht werden, bevor sie erneut benötigt wird).
  3. 0xFFFFFFFF: Diese Seite ist komplett gelöscht.
  4. Alles andere: Fehler. (Sollte nicht passieren.)

Kopfzeile aufzeichnen

Für jeden Datensatz werde ich auch mit einer Kopfzeile beginnen. Lassen Sie mich 2 Bytes verwenden, die eines der folgenden angeben:

  1. 0x55FF: Dies ist der aktuelle Datensatz.
  2. 0x5555: Dieser Datensatz ist ungültig.
  3. 0xFFFF: Dieser Aufzeichnungsslot wurde seit dem letzten Löschen nicht gefüllt.
  4. Alles andere: Fehler. Kein Datensatz-Header-Slot sollte etwas anderes haben.

Flash-Seiten beginnen gelöscht

Flash Page #0
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Systeminitialisierung

Nachdem ich festgestellt habe, dass die EEPROM-Daten nicht initialisiert wurden, werde ich damit beginnen, meinen Datensatz auf alle 0 zu initialisieren:

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF0000 // Record slot #0 (current value: 0x55FF header.)
00000000
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Beachten Sie, dass die Bytes 2-3 nicht geschrieben wurden und 0xFFFF bleiben. Wenn diese Flash-Seite voll ist und ich eine neue Flash-Seite starte, schreibe ich 0x5555 über die Bytes 2-3.

Der Datensatz-Header funktioniert im Wesentlichen gleich. Byte 1 des Datensatz-#0-Headers bleibt 0xFF, was anzeigt, dass es der aktuelle Datensatz ist (weil Byte 0 0x55 ist).

Schreiben Sie den Wert 0xDEADBEEFCAFE

In dieser Reihenfolge:

  1. Schreiben Sie die Daten des neuen Datensatzes.
  2. Schreiben Sie 0x55 in Byte 0 des neuen Datensatzes (Datensatzplatz Nr. 1), um anzuzeigen, dass es sich um den aktuellen Datensatz handelt.
  3. Schreiben Sie schließlich 0x55 in Byte 1 des alten Datensatzes (Datensatz-Slot #0), um ihn ungültig zu machen.

Die Reihenfolge ist wichtig für die Wiederherstellung nach falschen Resets (dies ähnelt einem Journal in einem Dateisystem).

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
55FFDEAD // Record slot #1 (current value: 0x55FF header.)
BEEFCAFE
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Schreiben Sie den Wert 0x12345678ABCD

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55FF1234 // Record slot #2 (current value: 0x55FF header.)
5678ABCD
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Die aktuelle Seite ist voll

So sehen die Seiten aus, wenn die aktuelle Seite (Seite #0) vollständig voll ist (der aktuelle Wert ist 0xAAAA5555BBBB):

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55551234 // Record slot #2 (now invalid: 0x5555 header.)
5678ABCD
...
55FFAAAA // Final record slot (current value: 0x55FF header.)
5555BBBB

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Beachten Sie, dass Flash-Seite Nr. 0 noch nicht ungültig gemacht wurde (der aktuelle Datensatz ist der letzte Datensatz in Flash-Seite Nr. 0).

Schreiben Sie den Wert 0x80009000ABCD

Der nächste Wert muss in die Flash-Seite #1 gehen. Nachdem wir den neuen Datensatz in Flash-Seite #1 geschrieben und den Seitenkopf erstellt haben, um anzuzeigen, dass Flash-Seite #1 jetzt den aktuellen Wert enthält, machen wir Flash-Seite #0 ungültig (schreiben 0x55 in Bytes 2-3) und den letzten Datensatz in Flash-Seite #0 (schreibe 0x55 in das Header-Byte 1 des letzten Datensatzes).

Flash Page #0
55555555 // Page Header (page is all now invalid: 0x55555555 header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55551234 // Record slot #2 (now invalid: 0x5555 header.)
5678ABCD
...
5555AAAA // Final record slot (now invalid: 0x5555 header.)
5555BBBB

Flash Page #1
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF8000 // Record slot #0 (current value: 0x55FF header.)
9000ABCD
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Jetzt ist Flash-Seite Nr. 0 vollständig ungültig, und die neue Seite ist Flash-Seite Nr. 1.

Flash-Seite #0 ist Garbage Collection (gelöscht)

Schließlich kann die Flash-Seite Nr. 0 gelöscht werden, wenn das System dies für zweckmäßig hält.

Flash Page #0
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF8000 // Record slot #0 (current value: 0x55FF header.)
9000ABCD
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Datensatzsuche, reale Komplexitäten

In der realen Welt wäre der Datensatz größer und komplexer als ein einzelner 6-Byte-Chunk. Es gibt viele Komplexitäten, die ich beschönigt habe. Dies ist ein wirklich einfaches Beispiel, aber es würde funktionieren. Es veranschaulicht, wie EEPROM im Flash emuliert werden kann, ohne übermäßiges Löschen, indem ein Hauptbuch von Wertaufzeichnungen erstellt wird.

Um die aktuellen Datensatzwerte nachzuschlagen, finden Sie heraus, welche Flash-Seite aktuell ist (eine und nur eine Flash-Seite sollte den Header 0x5555FFFF haben). Lesen Sie dann die Datensätze auf dieser Seite nacheinander durch, bis Sie den aktuellen Datensatz gefunden haben (ein und nur ein Datensatz sollte den Datensatzheader 0x55FF haben). Fehler könnten die Header-Konfiguration ungültig machen, was möglicherweise zu mehreren Seiten und/oder Datensätzen führt, die aktuell zu sein scheinen. Eine echte Anwendung müsste mit solchen Fehlern umgehen.

Aus diesem Anwendungshinweis :

Im Allgemeinen gibt es zwei gängige Ansätze, wenn Programm-Flash verwendet wird, um EEPROM zu emulieren. Der erste Ansatz besteht darin, eine Kopie der EEPROM-Daten in einem RAM-Puffer zu halten und den gesamten Inhalt des Puffers periodisch in den Programm-Flash zu schreiben. Dieser Ansatz ist relativ einfach zu implementieren, erlaubt das jederzeitige Lesen der Daten aus dem RAM-Puffer und ermöglicht die Steuerung der Anzahl der Programmier-/Löschzyklen. Der offensichtliche Nachteil dieses Ansatzes ist die Menge an RAM, die dem emulierten EEPROM-Puffer zugeordnet werden muss. Außerdem besteht die Gefahr, dass Daten verloren gehen, wenn ein Reset nach dem Aktualisieren eines RAM-Puffers erfolgt, aber bevor die Daten in das Flash programmiert werden.

Ein zweiter Ansatz verwendet mehrere Programm-Flash-Sektoren, um nichtflüchtige Daten unter Verwendung eines Flash-Dateisystems zu speichern. Abhängig von der Implementierung benötigt diese Methode normalerweise weniger RAM als der erste Ansatz. Der Hauptnachteil dieses Ansatzes ist die Größe und Komplexität der Firmware, die zur Implementierung eines robusten Flash-Dateisystems erforderlich ist, sodass das Risiko eines Datenverlusts bei einem unerwarteten Reset oder Stromausfall minimiert wird.

Ich möchte einen anderen Anwendungsknoten von ST freigeben :

Prinzip

Die EEPROM-Emulation wird auf verschiedene Weise durchgeführt, wobei die Einschränkungen des Flash-Speichers und die Produktanforderungen berücksichtigt werden. Der unten beschriebene Ansatz erfordert mindestens zwei Flash-Speicherseiten identischer Größe, die nichtflüchtigen Daten zugewiesen werden: eine, die anfänglich gelöscht wird, die andere, die bereit ist, zu übernehmen, wenn die vorherige Seite bereinigt werden muss. Ein Header-Feld, das das erste Halbwort (16 Bit) jeder Seite einnimmt, zeigt den Seitenstatus an. Jede dieser Seiten wird im Rest dieses Dokuments Seite0 und Seite1 genannt. Jede Seite hat drei mögliche Zustände:

● GELÖSCHT: Die Seite ist leer.

● RECEIVE_DATA: Die Seite empfängt Daten von der anderen vollständigen Seite.

● VALID _PAGE: Die Seite enthält gültige Daten und dieser Zustand ändert sich nicht, bis alle gültigen Daten vollständig auf die gelöschte Seite übertragen wurden.

Ein ziemlich detailliertes Diagramm über den Seitenstatus (zum Vergrößern anklicken):

Geben Sie hier die Bildbeschreibung ein

Anwendungsfall: Anwendungsbeispiel

Das folgende Beispiel zeigt die Softwareverwaltung von drei EEPROM-Variablen (Var1, Var2 und Var3) mit folgenden virtuellen Adressen:

Var1 virtuelle Adresse 5555h

Var2 virtuelle Adresse 6666h

Var3 virtuelle Adresse 7777h

Geben Sie hier die Bildbeschreibung ein