Forum: Mikrocontroller und Digitale Elektronik Dauerspeicherung auf SPI EEPROM mit Wrap-around und Sektorlöschung (avr gcc)


von Hans F. (snuff)


Lesenswert?

Hallo Community

Ich suche Hilfe bei der erstellung eines effizienten Algorithmus zur 
Dauerspeicherung auf einem EEPROM.

Zuerst das Setup:

Verwendet wird ein externer EEPROM Speicher mit 2MB Speicher. 
Angesteuert wird er mit SPI.
Der Adressbereich des EEPROM's ist von 0x000000 bis 0x03FFFF
Die kleinstmögliche "Löscheinheit" ist ein 4KB-Sektor-Erase.
Eine Bibliothek zum Schreiben und Lesen ist vollständig vorhanden.

Das Programm ist ein kleines Messgerät, welches die Daten dauerhaft auf 
den EEPROM ablegt. Sobald der Speicher voll ist, soll das Program den 
ersten 4KB Sektor wieder Löschen und dort weiterschreiben. Ist auch 
dieser Sektor wieder voll soll der nächste Sektor gelöscht werden, usw..

Ich brauche also einen Algorithmus, welcher folgende zwei Dinge kann:
- Beim Aufstarten den letzten belegten Speicherplatz suchen, um dort 
dann weiterschreiben zu können.
- Vorzu Prüfen ob bald die grenze eines 4KB-Sektors erreicht ist und den 
darauffolgenden Sektor dann löschen.

Meine Ansätze:

Für das erste Problem kann ich solange den Speicher lesen, bis ich nur 
noch lauter Einsen zurückbekomme. (Bei diesem EEPROM bedeutet 0xFF dass 
er leer ist). Von der Art der Daten die ich auf meinen Speicher schreibe 
weiss ich, dass ich nie eine Einserfolge haben kann, die länger als 3 
Byte ist. Sollte ich beim Auslesen also auf eine längere Einserfolge 
treffen, dann weiss ich, dass dort der zuletzt genutzte Speicherplatz 
ist.

Für das zweite Problem könnte ich die Adressen vergleichen. Wan immer 
die ersten 12 Bit der Adresse Null sind, ist ein neuer 4kB Sektor 
erreicht. Dementsprechend muss der darauffolgende Sektor gelöscht 
werden.


Ohne zu Wissen ob diese Ansätze funktionieren (sind noch nicht 
umgesetzt), glaube ich, dass sie sehr ineffizient sind und suche deshalb 
nach besseren Lösungen.

Für Ratschläge und Optimierungen wäre ich sehr Dankbar.

mfg

von 4444444444444444444444 (Gast)


Lesenswert?

hi

gleiche frage stellte sich mir auch irgendwann

ich habe dann einfach das SPIFFS filesystem genommen

https://github.com/pellepl/spiffs


klingt evtl alles zu oversized ...
aber es ist am ende sehr praktisch wenn man alles als datei sieht.

zumal man nur ein paar kleine funktionen anpassen muss
und das ganze läuft dann und über das handling musst du dir keinen kopf 
machen.


schreibe eine datei .. hänge notfalls daten an .. wie du möchtest.


ggf eine verbindung zum PC um diese dateien einfach hoch/runterladen zu 
können

von A. B. (Gast)


Lesenswert?

Die größten (verfügbaren bzw. bezahlbaren) SPI-EEPROMs liegen im Moment 
bei 256 kByte. 2 MByte ist noch Zukunftsmusik.

Außerdem muss/kann man EEPROMs nicht sektorweise löschen (der Witz von 
EEPROMs ist doch gerade, dass man sie byteweise überschreiben kann, und 
dass das vorherige Löschen solcher einzelnen Bytes bzw. 4-Byte Blöcken 
automatisch erfolgt).

Bei NOR- oder NAND-Flash (um die es wohl geht?!) muss man beachten, dass 
das Löschen eines Sektors "lange" dauert, und der Flash in dem Zeitraum 
"blockiert" ist. Also muss man einen ausreichend großen Puffer 
einplanen.
Oder zwei Chips, die abwechselnd (sektorweise oder komplett) beschreiben 
werden.

Ein "Leertest" allein anhand "FF..." ist nicht ausreichend, denn bei 
Unterbrechung der Stromversorgung, Reset, ... kann "Müll" im Flash 
zurück bleiben. Also Datensätze anhand CRC oder so auf Gültigkeit 
überprüfen.

Übrigens, was hat das mit avr-gcc zu tun???

von eagle user (Gast)


Lesenswert?

Du könntest die ersten 2 bis 4 Byte von jedem Sektor für den 
Sektor-Zustand reservieren. Dann musst du bei der Suche nach dem 
Kaltstart nur ein Byte pro Sektor lesen. Ich glaube, das braucht man 
sowieso, weil ein Sektor nicht nur frei oder belegt sein kann:
 * frisch aus der Fabrik
 * enthält Daten
 * ist voll (mit Daten)
 * wird gerade gelöscht
 * ist gelöscht
Die letzten beiden Zustände sind wichtig falls der Strom ausfällt 
während gerade gelöscht wird. Erst wenn die Flash-Hardware sagt "ist 
gelöscht" wird der Status "ist gelöscht" geschrieben. Nach einem 
Stromausfall wird ein Sektor ggf. ein zweites Mal gelöscht.

Ich schreibe noch einen Lösch-Zyklus-Zähler hinter die Status-Bytes. 
Damit müsste man eigentlich eine binäre Sektor-Suche machen können.

von Hans F. (snuff)


Lesenswert?

1
ich habe dann einfach das SPIFFS filesystem genommen

Danke für den Tip. Für mich leider nicht die richtige lösung, da der 
Controller berets jetzt schon am Anschlag läuft, deshalb such ich auch 
nach Optimierungen.
1
Bei NOR- oder NAND-Flash (um die es wohl geht?!) muss man beachten, dass 
2
das Löschen eines Sektors "lange" dauert, und der Flash in dem Zeitraum 
3
"blockiert" ist.
Ja ich befürchte ich habe da wieder einmal was falsch verstanden. Ich 
entschuldige mich für die verwirrung. Auf jedenfall kann ich bei meinem 
Speicher nicht Byteweise löschen.

Gut dass du das mit der Löschzeit ansprichts, das hatte ich bisher gar 
nicht beachtet. Gemäss Datenblatt liegt die bei 18ms. Ich denke dass ich 
den Datenverlust in dieser Zeit verkraften muss, sprich das Programm in 
dieser Zeit warten lasse.
1
Also Datensätze anhand CRC oder so auf Gültigkeit 
2
überprüfen.
Paritybits sind bei den Daten vorhanden. Eine überprüfung auf 
Korrektheit kann ich also durchführen. Aber zur Erkennung des letzten 
Speicherortes ist dies doch irrelevant? In erster Linie will ich 
erkennen wo ich stehengeblieben bin, ob der letzte Datensatz dann 
korrekt ist ist eine andere Frage
1
Übrigens, was hat das mit avr-gcc zu tun???
Irgendwie müssen die Leute doch wissen welche Mikrocontroller 
Architektur ich verwende und welche Toolchain.

Nochmals vielen Dank für alle Antworten.
mfg

: Bearbeitet durch User
von Hans F. (snuff)


Lesenswert?

1
Du könntest die ersten 2 bis 4 Byte von jedem Sektor für den 
2
Sektor-Zustand reservieren.

Ich habe gerade versucht deinen Ansatz zu implementieren. Ich scheitere 
allerdings daran, dass ich den Status des Sektors ja nicht überschreiben 
kann ohne den gesamten Sektor zu löschen.

Beitrag #5253878 wurde von einem Moderator gelöscht.
von eagle user (Gast)


Lesenswert?

Hans F. schrieb:
> Du könntest die ersten 2 bis 4 Byte von jedem Sektor für den
> Sektor-Zustand reservieren.
>
> Ich habe gerade versucht deinen Ansatz zu implementieren. Ich scheitere
> allerdings daran, dass ich den Status des Sektors ja nicht überschreiben
> kann ohne den gesamten Sektor zu löschen.

deswegen brauchst du mehr als ein Byte; jede Statusänderung (es sind ja 
sehr wenige) ändert ein weiteres Byte von FF auf etwas mit 
Wiedererkennungswert.

von Uwe D. (monkye)


Lesenswert?


von Hans F. (snuff)


Lesenswert?

eagle user schrieb:
> deswegen brauchst du mehr als ein Byte; jede Statusänderung (es sind ja
> sehr wenige) ändert ein weiteres Byte von FF auf etwas mit
> Wiedererkennungswert.

Achso jetzt hab ich das Konzept verstanden!
Klingt eigentlich ganz plausibel, ich denke das sollte sich umsetzen 
lassen.


Nochmals Kurz wegen der EEPROM und FLASH geschichte. Ich glaube die 
Onlineshops verwenden den Begriff mehrdeutig. Ich habe bei meinem Chip 
nochmals nachgesehen. Auf dem Ettiket der Verpackung ist dieser 
unverfälschbar mit "Serial EEPROM" betittelt, das Datenblatt nennt den 
Chip allerdings "Serisl Flash".

von Einer K. (Gast)


Lesenswert?

Hans F. schrieb:
> Nochmals Kurz wegen der EEPROM und FLASH geschichte. Ich glaube die
> Onlineshops verwenden den Begriff mehrdeutig. Ich habe bei meinem Chip
> nochmals nachgesehen. Auf dem Ettiket der Verpackung ist dieser
> unverfälschbar mit "Serial EEPROM" betittelt, das Datenblatt nennt den
> Chip allerdings "Serisl Flash".

Du hast einen Flash Baustein!
Und natürlich ist das ein EEPROM
(electrically erasable programmable read-only memory)

Merksatz:
§1: Jeder Flash Baustein ist ein EEPROM.
§2: Nicht jedes EEPROM ist ein Flash Baustein.

Siehe: https://de.wikipedia.org/wiki/Flash-Speicher

von Stefan E. (sternst)


Lesenswert?

eagle user schrieb:
> deswegen brauchst du mehr als ein Byte; jede Statusänderung (es sind ja
> sehr wenige) ändert ein weiteres Byte von FF auf etwas mit
> Wiedererkennungswert.

Man braucht nicht unbedingt mehrere Bytes. Man kann ja das selbe Byte 
"überschreiben", sofern sich die Bits nur von 1 auf 0 ändern. Ein 
einzelnes reicht also auch schon für 8 Änderungen.

von 4444444444444444444444 (Gast)


Lesenswert?

OMG ..

lesen will gerlernt sein ...
ich verwende natürlich einen SPI Flash!!

EEPROM habe ich total überlesen.


sorry

von Uwe D. (monkye)


Lesenswert?

4444444444444444444444 schrieb:
> OMG ..
>
> lesen will gerlernt sein ...
> ich verwende natürlich einen SPI Flash!!
>
> EEPROM habe ich total überlesen.
>
>
> sorry

Und das ist also kein EEPROM?

von Hans F. (snuff)


Lesenswert?

Stefan E. schrieb:
> Man braucht nicht unbedingt mehrere Bytes. Man kann ja das selbe Byte
> "überschreiben", sofern sich die Bits nur von 1 auf 0 ändern. Ein
> einzelnes reicht also auch schon für 8 Änderungen.

Gut zu wissen, war mir nämlich nicht bewusst dass das geht. Werde ich 
auch versuchen zu implementieren.

Daanke und liebe Grüsse

von Hans F. (snuff)


Angehängte Dateien:

Lesenswert?

Ich hätte noch eine ergänzende Frage. Die hat jetzt mit dem eigentlichen 
Problem nichts zu tun. jedoch denke ich dass es überflüssig ist extra 
einen neuen Thread zu eröffnen.

Und zwar hab ich Probleme mit der Auto Address Increase Funktion des 
Speichers. Das Schreiben und Lesen von 8Bit Registern klappt problemlos. 
Nun möchte ich aber meinem Program je 56Bit an Daten aufsmal schreiben.

Die entsprechende Sequenz im Datenblatt habe ich als Anhang hinzugefügt.

Wenn ich das Richtig verstanden habe, dann wird für die Zeit des 
Schreibvorgangs die Funktion des MISO Pins (im Datenblatt nur "SO" 
genannt) während dieser Zeit umgeschaltet, so dass man an ihm erkennen 
kann, ob der Speicherbaustein gerade beschäftigt ist.

Gemäss Datenblatt gilt dann:
MISO = 0 : Device is busy
MISO = 1 : Device is ready for next operation

Ich habe hier exemplarisch mein Code für eine Schreibsequenz von 4* 
8Bit. Ich hoffe die Namensgebung ist einigermassen eindeutig.
1
void SPI_Write(uint32_t address, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4){
2
  uint8_t address_HIGH = (address >> 16)&0xFF;
3
  uint8_t address_MED = (address >> 8)&0xFF;
4
  uint8_t address_LOW = (address & 0xFF);
5
  SPI_EnableCS();      // Chip Select low (ist aktiv low)
6
  SPI_Transmit(EBSY);
7
  SPI_DisableCS();     // Chip Select High
8
  ;
9
  SPI_EnableCS();
10
  SPI_Transmit(WREN);
11
  SPI_DisableCS();
12
  ;
13
  SPI_EnableCS();
14
  SPI_Transmit(AAIW);// AAIW entspricht dem befehl "AD" im datenblatt
15
  SPI_Transmit(address_HIGH);
16
  SPI_Transmit(address_MED);
17
  SPI_Transmit(address_LOW);
18
  SPI_Transmit(d1);
19
  SPI_Transmit(d2);
20
  SPI_DisableCS();
21
  ;
22
  SPI_EnableCS();
23
  while(!SPDR){
24
    ;
25
  }
26
  SPI_DisableCS();
27
  ;
28
  SPI_EnableCS();
29
  SPI_Transmit(AAIW);
30
  SPI_Transmit(d1);
31
  SPI_Transmit(d2);
32
  SPI_DisableCS();
33
  ;
34
  SPI_EnableCS();
35
  while(!SPDR){
36
    ;
37
  }
38
  SPI_DisableCS();
39
  ;
40
  SPI_EnableCS();
41
  SPI_Transmit(WRDI);
42
  SPI_DisableCS();
43
  ;
44
  SPI_EnableCS();
45
  SPI_Transmit(DBSY);
46
  SPI_DisableCS();
47
  ;
48
  SPI_EnableCS();
49
  SPI_Transmit(RDSR);
50
  SPI_DisableCS();
51
}

Ob die Busy-Abfrage mit dieser While-Schlaufe so korrekt ist weiss ich 
nicht.
Müsste man eventuell noch Dummy-Bytes senden in dieser Zeit? Und wenn 
wir schon beim Thema sind: Welche Bitfolge ist als Dummy zulässig? Im 
Datenblatt wird nirgends genannt wie ein Dummy auszusehen hat. Ich habe 
mich einfach an existierenden SPI Bibliotheken orientiert. Dort sind 
Dummys entweder eine Einserfolge oder eine Nullerfolge.

Danke für die Hilfe!

mfg

von A. B. (Gast)


Lesenswert?

Der "Zukunftssicherheit" wegen ist es sinnvoll, nur die gängigen, bei 
NOR-Flash herstellerübergreifend identischen Kommandos zu verwenden.

0x03 (Read), 0x02 (Page Program), 0x05 (Read Status Reg.), 0x06 (Write 
Enable), 0x20 (Sector Erase), 0x9F (Read ID) sind weitgehend Konsens,
ebenso BUSY- und WEL-Bit im Statusregister. (Und 0x13 bzw. 0x12 statt 
0x03 bzw. 0x13 bei 4-Byte-Adressen). Auch die Page Size ist bei fast 
allen 256 Bytes.

Jede nette Spezialität, die auf den ersten Blick schön aussieht, fällt 
einem früher oder später doch auf die Füße.

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.