Hallo zusammen!
Ich möchte gerne für ein kleines Projekt (RGB-Beleuchtung) mehrere
Presets im EEPROM des AVR ablegen.
Das ist soweit auch kein Problem, die Presets werden einfach an feste
Stellen im EEPROM geschrieben (EEMEM-Variablen und fertig).
Da die Presets zwar vom Benutzer per Fernbedienung konfigurierbar sind
aber vermutlich eher selten geändert werden sollten da die garantierten
100.000 Schreib-/Löschzyklen des AVR ausreichen.
Mein Problem besteht nun darin, dass ich auch speichern will welches
Preset als letztes aktiv war.
Und da könnte es dann schon irgendwann mal eng werden mit den 100.000...
Daher nun meine Frage: Wie implementiert man am elegantesten einen
Ringpuffer im EEPROM?
Mein Ansatz sieht etwa so aus: Ich lege einen Block von z.B. 100
Byte-Werten im EEPROM ab (wieder als EEMEM-Variable).
Nach dem Programmieren des Chips (bzw. Chip-Erase) sollten alle Werte in
dem Array auf 0xFF stehen.
Nun kann ich das Array per eeprom_read_block auslesen und der letzte
Wert im Array der NICHT 0xff ist sollte dann meiner sein.
Soweit so gut...
Wie schreibe ich das Ding aber nun weg? Soweit ich das verstanden habe
würde eeprom__write_block den kompletten Block ins EEPROM schreiben,
d.h. jede Zelle innerhalb des Blocks würde neu beschrieben oder?
(Das würde den Vorteil des Ringpuffers ja wieder zunichte machen.)
Die einzige Lösung die mir einfällt wäre: Im Array nach dem letzten Wert
<0xFF suchen und nur diesen Wert per eeprom_write_byte schreiben.
Geht das nicht auch "einfacher"? (Nicht dass es wirklich schwer wäre,
ich habe nur das Gefühl das Rad ein zweites Mal zu erfinden...)
z.B. so:
Hm, dann könnte man um den gleichen Effekt zu erzielen wie in der
AppNote auch gleich einen doppelt so großen Ringbuffer nehmen. Jedes
Byte im Buffer besteht aus 7 Bit Index des letzten Presets und das 7'te
Bit ist das Flag das anzeigt das dies das "Ende/Anfang" des Ringbuffers
ist. Wird ein neuer Preset geschrieben suchst du den Eintrag mit
gesetztem 7'ten Bit und löscht dieses Bit durch überscheiben. Das Byte
danach wird mit dem neuen Presetindex geschrieben und dabei das 7'te Bit
gesetzt.
Somit werden pro Schreibvorgang 2 EEPROM Bytes geschrieben. Der
Gesamt-EEPROM Verbrauch und die Häufigkeit des Schreibens ist gleichhoch
wie in dem Verfahren aus der AppNote. Nur ist der Unterschied das das
Verfahren aus der AppNote viel kompliziter/umständlicher ist.
Vorteil mit der Bitkodierung ist das beim Überschreiben von zb. 0xFF
nach 0x7F nur 1 Bit sich geändert hat und im EEPROM geschrieben wird.
Alternativ kannst du auch gleich das letzte Presetindex-byte mit 0xFF
überschreiben und danach den neuen Presetindex (< 255) schreiben. Dh.
dein EEPROM Buffer besteht im Grunde immer aus 0xFF gefüllt und nur 1
Byte enthält den Presetindex. In jedem Falle wirst du doppelt soviel
EEPROM verbrauchen um auf deinen berechneten Faktor zu kommen.
Gruß Hagen
@fabs: Danke für den Hinweis, das sieht interessant aus. Den Pointer
muss ich mir ja nur einmal am Anfang rauskramen, das eine Byte SRAM zum
Speichern davon wird noch übrig sein... ;)
Allerdings ist der doppelte Speicherverbrauch irgendwie nicht so prall,
aber ich werde die Appnote mal im Hinterkopf behalten.
@Hagen: Ich muss gestehen, ich kann Deinen Ausführungen spontan nicht so
richtig folgen, Du verwirrst micht etwas... ^^
Soweit ich Dich verstehe, habe ich aber trotz allem noch den doppelten
Speicherverbrauch und auch zwei Schreibzugriffe pro geschriebenem Byte,
richtig?
D.h. die Anzahl der möglichen Lösch-/Schreibzyklen beträgt sowohl bei
Dir als auch bei der Atmel-Appnote dann "nur" "50.000 * verbrauchtes
EEPROM in Byte"?
Wäre da nicht meine Methode (s.o.) vorzuziehen?
Es würde zwar theoretisch länger dauern da die letzte Speicherstelle
jedesmal neu rausgesucht wird, aber da kann man ja Abhilfe schaffen.
(Letzten Index einmal am Anfang laden und gut ist's...
Und ich bemerke gerade, dass ich euch eine wichtige Information
vorenthalten habe: Es wird in der Applikation maximal 10
unterschiedliche Presets geben, mehr ist sowieso nicht drin. Eure
Methoden würden mir ja einen Wertebereich von 0..255 ermöglichen, nur
brauche ich den ja gar nicht, 0..9 reicht bei mir aus...
Eine grundsätzliche Verständnisfrage hätte ich noch: Wenn ich eine
Stelle im EEPROM mit 0xFF "beschreibe" entspricht das ja im Prinzip dem
Löschen des entsprechenden Bytes im EEPROM. Beschreibe ich diese Stelle
hinterher nochmal mit einem anderen Wert, wird die dann direkt nochmal
gelöscht? Falls das so wäre hätte ich ja auch wieder "nur" 50.000
nutzbare Schreibzugriffe pro Byte im EEPROM, richtig?
Ich danke euch auf jeden Fall schonmal für die Hilfe! :)
Christian T. schrieb:
> Mein Problem besteht nun darin, dass ich auch speichern will welches> Preset als letztes aktiv war.> Und da könnte es dann schon irgendwann mal eng werden mit den 100.000...
Ehe dein Benutzer 100000 mal den Preset umgeschaltet hat, ist er längst
eines natürlichen Todes gestorben, wenn wir eine einigermasse normale
Benutzung voraussetzen.
ZUm anderen ist es ja nicht so, dass Atmel da einen Counter eingebaut
hat, der nach exakt 100000 Schreibzyklen die Zelle abschaltet. Die
garantierte Zahl an Zyklen lautet 100000. In der Praxis hält das Teil
aber länger.
Die neueren AVRs erlauben getrenntes Löschen und Schreiben.
Dann kannst Du hochzählen, den neuen Wert schreiben ohne Löschen und
dann den vorherigen Wert nur löschen.
Peter
Karl heinz Buchegger schrieb:
> Christian T. schrieb:>>> Mein Problem besteht nun darin, dass ich auch speichern will welches>> Preset als letztes aktiv war.>> Und da könnte es dann schon irgendwann mal eng werden mit den 100.000...>> Ehe dein Benutzer 100000 mal den Preset umgeschaltet hat, ist er längst> eines natürlichen Todes gestorben, wenn wir eine einigermasse normale> Benutzung voraussetzen.
Da könntest Du Recht haben... ^^
Mir geht es da eher um's Prinzip, ich find's halt schöner wenn ich dem
Kumpel der das Teil kriegt sagen kann "darfst aber maximal 10.000.000
umschalten!" ;-)
> ZUm anderen ist es ja nicht so, dass Atmel da einen Counter eingebaut> hat, der nach exakt 100000 Schreibzyklen die Zelle abschaltet. Die> garantierte Zahl an Zyklen lautet 100000. In der Praxis hält das Teil> aber länger.
Das ist klar. Aber es ist eben nicht garantiert. ^^
Edit: Frage beantwortet von Peda, vielen Dank! :)
Grüße,
Christian
Christian T. schrieb:
> Karl heinz Buchegger schrieb:>> Christian T. schrieb:>>>>> Mein Problem besteht nun darin, dass ich auch speichern will welches>>> Preset als letztes aktiv war.>>> Und da könnte es dann schon irgendwann mal eng werden mit den 100.000...>>>> Ehe dein Benutzer 100000 mal den Preset umgeschaltet hat, ist er längst>> eines natürlichen Todes gestorben, wenn wir eine einigermasse normale>> Benutzung voraussetzen.>> Da könntest Du Recht haben... ^^> Mir geht es da eher um's Prinzip,
Ist schon ok.
> ich find's halt schöner wenn ich dem> Kumpel der das Teil kriegt sagen kann "darfst aber maximal 10.000.000> umschalten!" ;-)
Vor kurzem haben wir hier im Forum eine ähnliche Rechnung gemacht.
Wenn dein Benutzer alle 10 Minuten den Preset wechselt und dass 24
Stunden am Tag, 7 Tage die Woche, 52 Wochen im Jahr ... kurz gesagt,
wenn er nichts anderes tut als alle 10 Minuten den Preset zu wechseln,
dann hält das EEPROM gute 2 Jahre.
Nun kann ich mir nicht vorstellen, dass dein Kumpel jeden Tag nichts
anderes tut als mit dem RGB Licht zu spielen. Tagsüber wird es wohl aus
lassen, was die Lebensdauer schon mal auf ~4 Jahre erhöht. In der Nacht
wird er wohl die halbe Zeit schlafen und wir sind bei 8 Jahren. Und dass
er seine Abende damit verbringt, alle 10 Minuten den Preset zu wechseln,
kann wohl getrost als illusorisch abgetan werden. Zumindest dann, wenn
er irgendwann in den nächsten 8 Jahren Familie hat. Und seine Freundin
wird ihm was husten, wenn ihm abends nichts besseres einfällt als mit
der Lichtsteuerung zu spielen.
Fazit: Viel Lärm um nichts. Du brauchst deinem Kumpel überhaupt nicht
sagen, dass es da eine Beschränkung der Lebensdauer gibt, weil diese
Beschränkung ganz einfach nicht relevant ist.
> Kannst Du vllt. noch was zu meiner Frage bzgl. dem Löschen und> Wiederbeschreiben des EEPROMs sagen? (s. Post vor Deinem, letzter> Absatz)
Wenns denn sein muss :-)
Du könntest zb im EEPROM 3 oder 4 Zähler/PresetId-Kombinationen
benutzen, die erst mal alle auf 0 gesetzt werden. Ist ein Zähler bei
0xFFFF angelangt, benutzt du einfach den nächsten Zähler/PresetId.
>Soweit ich Dich verstehe, habe ich aber trotz allem noch den doppelten>Speicherverbrauch und auch zwei Schreibzugriffe pro geschriebenem Byte,>richtig?
Ja, wirst du in jedem Falle immer haben.
>D.h. die Anzahl der möglichen Lösch-/Schreibzyklen beträgt sowohl bei>Dir als auch bei der Atmel-Appnote dann "nur" "50.000 * verbrauchtes>EEPROM in Byte"?
Ja.
>Wäre da nicht meine Methode (s.o.) vorzuziehen?
Wenn du nur einmalig 100 Presetsindizes speichern möchtes dann ja. Dein
Buffer mit 100 Bytes ist am Anfang mit 0xFF gefüllt. Nach dem ersten
Preset steht an Adresse 0x00 des Buffers also dein letzter Presets. Dein
Buffer hat noch 99 Werte mit 0xFF gefüllt. Was machst du nach 100
gespeicherten Prestes ? Wenn alle 100 zellen <> 0xFF sind wie möchtest
du dann den aktuell letzten Preset finden ?
Du musst immer zu den eigentlichen Daten auch eine
Verwaltungstabelle/buffer speichern. Ergo minimal doppelt soviel EEPROM
wie dein Ziel der Anzahl der Schreibzugriffe dir vorgibt. Wichtig ist
eben das diese Verwaltungsinformationen (Zeiger, Indizes) ebenfalls im
EEPROM gespeichert werden müssen und dann den gleichen Bedingungen
unterliegen wie deine eigentlichen Daten. Ergo: auch dafür eine
Ringbuffer benutzen.
>Methoden würden mir ja einen Wertebereich von 0..255 ermöglichen, nur>brauche ich den ja gar nicht, 0..9 reicht bei mir aus...
Ändert nichts wesentlich an der Sache. Du musst denoch minimal 2 EEPROM
Bytes schreiben. Das neue Byte und das alte mit dem aktuellen Preset
musst du verändern, oder eben das Datenbyte und das Indexbyte so wie in
der AppNote.
Du könntest mit dem Benutzten der 2 Nibbles eines Bytes das Waerout um
Faktor 1.6 verbesseren. Denn wenn du in einem Byte 2 Nibbles = 2 Presets
hast so musst du nur jedes zweite Mal 2 Bytes und ansonsten nur 1 Byte
neu schreiben.
Also Buffer besteht aus zb. 16 Bytes = 32 Presets. Alles mit 0xFF
gefüllt. Beim ersten Schreiben in Byte 0 des Buffers steht also dann zB.
0xF1 also Presetindex #1, und 1 Bytes wurde geschrieben. Beim zweiten
Schreiben wieder im Byte 0 und drinnen steht zb. 0x21, also Preset #2.
Wieder nur 1 Bytes geschrieben. Beim 3'ten Scheiben wird nun Byte 0
gelöscht = 0xFF und Byte 1 mit zb. 0xF3, also Preset #3 geschrieben.
Diesesmal also 2 Schreibzugriffe. Auf 4 Presets also 5 Bytes
geschrieben. Normalerweise würdest du für 4 Presets 8 Bytes statt 5 neu
schreiben. Faktor 1.6 besser.
Damit also daraus ein Ringbuffer wird musst du den vorherigen Preset mit
0xFF überschreiben ansonsten weist du ja nach 100 Presets bei 100 Bytes
im Buffer nicht mehr wo der Anfang ist und du kannst den Buffer nur
einmalig mit 100 Presets befüllen. Das wäre dann kein Ringbuffer.
Gruß Hagen
Hagen Re schrieb:
>>Soweit ich Dich verstehe, habe ich aber trotz allem noch den doppelten>>Speicherverbrauch und auch zwei Schreibzugriffe pro geschriebenem Byte,>>richtig?>> Ja, wirst du in jedem Falle immer haben.>>>D.h. die Anzahl der möglichen Lösch-/Schreibzyklen beträgt sowohl bei>>Dir als auch bei der Atmel-Appnote dann "nur" "50.000 * verbrauchtes>>EEPROM in Byte"?>> Ja.>>>Wäre da nicht meine Methode (s.o.) vorzuziehen?>> Wenn du nur einmalig 100 Presetsindizes speichern möchtes dann ja. Dein> Buffer mit 100 Bytes ist am Anfang mit 0xFF gefüllt. Nach dem ersten> Preset steht an Adresse 0x00 des Buffers also dein letzter Presets. Dein> Buffer hat noch 99 Werte mit 0xFF gefüllt. Was machst du nach 100> gespeicherten Prestes ? Wenn alle 100 zellen <> 0xFF sind wie möchtest> du dann den aktuell letzten Preset finden ?
Wenn alle 100 Werte != 0xFF sind dann ist mein gesuchtes Preset die
#100, beim nächsten Schreiben würde ich das Feld dann komplett wieder
löschen und Zelle #1 beschreiben.
>>Methoden würden mir ja einen Wertebereich von 0..255 ermöglichen, nur>>brauche ich den ja gar nicht, 0..9 reicht bei mir aus...>> Ändert nichts wesentlich an der Sache. Du musst denoch minimal 2 EEPROM> Bytes schreiben. Das neue Byte und das alte mit dem aktuellen Preset> musst du verändern, oder eben das Datenbyte und das Indexbyte so wie in> der AppNote.
Deshalb fragte ich ja nach dem getrennten Löschen und Beschreiben eines
EEPROM-Bytes. Aber so wie's aussieht wird das von der avr-libc nicht
unterstützt. Zumindest in der Online-Hilfe steht da nix drüber. Ich
könnte höchstens mal in den Code schauen ob die sowas getrennt behandeln
oder nicht.
(Ich hege allerdings leise Zweifel ob ich da durchsteigen würde... ^^)
Da ich eine Assembler-Niete bin werde ich einfach mal kbuchegg's
Argumentation folgen und mich darauf verlassen, dass der Kumpel
vermutlich vor dem EEPROM das Zeitliche segnen wird... ;)
Ich danke euch allen für die Diskussion/Hinweise.
Grüße,
Christian
Christian T. schrieb:
> Da ich eine Assembler-Niete bin werde ich einfach mal kbuchegg's> Argumentation folgen und mich darauf verlassen, dass der Kumpel> vermutlich vor dem EEPROM das Zeitliche segnen wird... ;)
Ja, ja. Dann bin ich wieder schuld :-)
> Ich danke euch allen für die Diskussion/Hinweise.
Diesmal mit runterzählen, da ja das EEPROM jungfräulich mit 0xFF besetzt
ist :-)
1
#define NR_EEPROM_PRESET 4
2
3
uint16_tUsageCounter[NR_EEPROM_PRESET]EEMEM;
4
uint8_tactPreset[NR_EEPROM_PRESET]EEMEM;
5
6
.....
7
8
9
uint8_tactPreset;
10
11
....
12
13
uint8_tGetPreset(void)
14
{
15
uint8_ti;
16
17
for(i=0;i<NR_EEPROM_PRESET;++i){
18
if(eeprom_read_word(&UsageCounter[i])!=0)
19
returneeprom_read_byte(&actPreset[i]);
20
}
21
22
// all NR_EEPROM_PRESET*100000 EEprom Write Cycles have been used :-(
Karl heinz Buchegger schrieb:
> Christian T. schrieb:>>> Da ich eine Assembler-Niete bin werde ich einfach mal kbuchegg's>> Argumentation folgen und mich darauf verlassen, dass der Kumpel>> vermutlich vor dem EEPROM das Zeitliche segnen wird... ;)>> Ja, ja. Dann bin ich wieder schuld :-)
Das Los des Moderators... 8D
> Diesmal mit runterzählen, da ja das EEPROM jungfräulich mit 0xFF besetzt> ist :-)>>
1
>#defineNR_EEPROM_PRESET4
2
>
3
>uint16_tUsageCounter[NR_EEPROM_PRESET]EEMEM;
4
>uint8_tactPreset[NR_EEPROM_PRESET]EEMEM;
5
>
6
>.....
7
>
8
>
9
>uint8_tactPreset;
10
>
11
>....
12
>
13
>uint8_tGetPreset(void)
14
>{
15
>uint8_ti;
16
>
17
>for(i=0;i<NR_EEPROM_PRESET;++i){
18
>if(eeprom_read_word(&UsageCounter[i])!=0)
19
>returneeprom_read_byte(&actPreset[i]);
20
>}
21
>
22
>// all NR_EEPROM_PRESET*100000 EEprom Write Cycles have been used :-(
// kbuchegg hat gesagt er ist Schuld wenn hier Daten verloren gehen... ^^
39
>if(counter!=0){
40
>eeprom_write_byte(&actPreset[i],preset);
41
>return;
42
>}
43
>}
44
>}
45
>}
46
>
Aber vielen Dank für die Grundidee, ich denke ich werde das so (ähnlich)
umsetzen. Ringpuffer ist an der Stelle dann doch eher die berühmte
Kanone für die Spatzen...
Grüße,
Christian
Wenn du maximal 255 Presets hast (0-254), ist es doch ganz einfach. Du
schreibst einfach das Byte in den nächsten Platz des Ringbuffers, und
löschst danach den vorherigen (0xff = 255).
Michael L. schrieb:
> Wenn du maximal 255 Presets hast (0-254), ist es doch ganz einfach. Du> schreibst einfach das Byte in den nächsten Platz des Ringbuffers, und> löschst danach den vorherigen (0xff = 255).
Jup, so ähnlich hatte ich das anfangs ja auch geplant.
Nur hätte ich den kompletten Buffer bei jedem "Überlauf" gelöscht und
nicht bei jedem Schreibzugriff...
Mittlerweile ist mir aber klar geworden, dass ein Ringpuffer bei der
geplanten Anwendung überdimensioniert wäre...
Daher bleibt's dann wohl eher bei 1-10 diskreten Speicherstellen im
EEPROM die der Reihe nach 65535x beschrieben werden... ;)
>Wenn alle 100 Werte != 0xFF sind dann ist mein gesuchtes Preset die>#100, beim nächsten Schreiben würde ich das Feld dann komplett wieder>löschen und Zelle #1 beschreiben.
Ja und, also doch wieder 2 Schreibvorgänge pro Zelle. 100x Preset und
dann 1x alle 100 Preset alle 100 Zellen löschen. Macht bei mir 2x
Schreiben pro Zelle und Preset.
Einziger Unterschied ist das vom Timing her bei der kontinuierlichen
Methode jeweils 2x Schreibzeit des EEPROMs gleichmäßig verteilt entsteht
und bei deiner alle 100 Preset ein langandauender Schreibzyklus von 100
Bytes.
Gruß Hagen
@Hagen: Jein. :)
Ich dachte, es sei möglich die Lösch- und Schreibzugriffe getrennt
vorzunehmen. Das wäre dann nur ein kompletter Lösch-/Schreibzyklus. Und
obwohl das bei neueren(?) AVRs laut Peter wohl machbar ist wird es
anscheinend von der avr-libc nicht unterstützt.
(Bei der Gelegenheit mal die Frage an Peter: Was genau sind denn
diesbezüglich die "neueren" AVRs?!?)
Da mir diese Möglichkeit also nicht offen steht (ich programmiere in C)
hast Du also Recht, es werden 2 Schreibzugriffe sein. Und ja, mein
Timing wäre da ungeschickt gewesen... :D
Christian T. schrieb:
> (Bei der Gelegenheit mal die Frage an Peter: Was genau sind denn> diesbezüglich die "neueren" AVRs?!?)
Z.B. ATtiny25, ATmega48 und neuer.
> Da mir diese Möglichkeit also nicht offen steht (ich programmiere in C)
Ach Quatsch.
EEPROM schreiben ist kein Hexenwerk, im Datenblatt ist sogar ein
C-Beispiel drin.
Ich dachte, C-Programmierer können auch programmieren und nicht nur
fertige Legosteine zusammenschieben.
Peter
Peter Dannegger schrieb:
@Peter: Hast recht, im Datenblatt ist es gut erklärt.
(C-Beispiel hab ich für den Mega48 jetzt nicht gefunden, aber das sollte
kein Problem sein.)
> Ich dachte, C-Programmierer können auch programmieren und nicht nur> fertige Legosteine zusammenschieben.
Nun muss ich nur erstmal nen Mega48 bzw. 88 ordern, momentan werkelt
noch ein "alter" Mega8 in dem Teil und der kann soweit ich das DB
verstehe nicht getrennt löschen und schreiben.
Dann nehme ich Deine "Herausforderung" aber gerne mal an... ^^
>@Hagen: Jein. :)>Ich dachte, es sei möglich die Lösch- und Schreibzugriffe getrennt>vorzunehmen. Das wäre dann nur ein kompletter Lösch-/Schreibzyklus. Und>obwohl das bei neueren(?)
Falsch. Das spielt beim Vergleich beider Methoden keine Rolle. Wenn ich
mich in meinem Postings auf "Scheiben" bezog dann ist damit natürlich
Schreiben, Löschen oder beides in Einem gemeint. Aus Sicht eines
Vergleiches beider Methoden ist das aber egal da beide Methoden die
gleichen Operationen benötigen. Der einzige Unterschied ist wie gesagt
die zeitmäßige Verteilung. Wenn du 100 Zellen auf einmal
löschst/scheibst dauert das länger als nur 1 oder 2 zellen zu
bearbeiten. Beim EEPROM ist das schon eine merkliche Zeitspanne.
Ich stimme aber mit der Meinung der Anderen fast überein. 2 Jahre alle
10 Minuten wird keiner den Preset ändern. Ich vertrete aber auch immer
die Aufassung das ein guter Programmierer alle Eventualitäten mit
einkalkuliert. Wenn es nur wenig Mehraufwand in Software kostet, dann
schadet es sicherlich nicht wenn der Anwender alle 10 Sekunden 10 Jahre
lang die Presets ändern könnte ohne das ein Problem entsteht.
Davon abgesehen ist es auch eine gute Übung für einen Programmierer wenn
er sowas mal programmiert hat.
Gruß Hagen
Mit getrenntem Löschen und Schreiben kannst du die Lebensdauer mit
meinem Vorschlag sogar voll ausnutzen, hat also bei einem 10-Byte-Puffer
1 Mio. Schreibzugriffe, statt nur 0,5 Mio. wenn Löschen+Schreiben in
einem Zyklus erfolgen.
Wobei ich denke, dass beim Schreiben von 0xff nur gelöscht, und nicht
wirklich was geschrieben wird, es der Speicherzelle also praktisch kaum
schadet.
Würde nicht 1x schreiben (ohne separates Löschen) pro neuem Wert
ausreichen, wenn 7 Bit für die Kodierung des Zustands ausreichen?
Vorgehen wie folgt, Speicherbereich beliebiger Größe und Lage im EEPROM:
Initialisieren (einmalig):
1. Speicher löschen (0xFF)
2. an letzte Speicherstelle 7 Datenbits (0-6) mit gesetztem Bit 7
Schreiben
Schreiben:
1. erste Speicherzelle auslesen,
Zustand von Bit 7 merken
2. im gewählten Speicherbereich die
letzte Speicherzelle suchen, deren
Bit 7 dem gespeicherten Wert entspricht
3a. das Datum mit identischem Bit 7 an der
Folgeadresse eintragen falls das Ende
des Bereichs nicht überschritten wird
3b. andernfalls das Datum mit invertiertem
Bit 7 am Anfang des Speicherbereichs
eintragen
Lesen:
1. erste Speicherzelle auslesen,
Zustand von Bit 7 merken
2. im gewählten Speicherbereich die
letzte Speicherzelle suchen, deren
Bit 7 dem gespeicherten Wert entspricht,
die Bits 0-6 enthalten das letzte Datum
D.h. das Bit 7 wird bei jedem Umlauf des Puffers gewechselt und dient
dem Erkennen des zuletzt eingetragenen Werts.
HI
Anderer Vorschlag: Abschalterkennung einbauen. Beim Einschalten alles in
den Ram einlesen und damit arbeiten. Erst beim Abschalten den EEProm
bemühen.
MfG Spess
>Würde nicht 1x schreiben (ohne separates Löschen) pro neuem Wert>ausreichen, wenn 7 Bit für die Kodierung des Zustands ausreichen?
Wenn du einmal den Ringbuffer gefüllt hast dann stellt sich die Frage
nach welchem Bit 7 Zustand du entscheiden sollst. Also der Ringbuffer
enthält die Hälfte an Zellen mit Bit 7 = 0 und die andere mit Bit 7 = 1.
Die Frage ist nun: muß man das letzte Byte mit gesetztem oder gelöschtem
Bit 7 lesen ?
Dein System benutzt also nur relative Informationen und keine
Absolutaussagen. Beim nächsten Start kann der Controller auf Basis von
nur relativen Infomationen keine Absolutaddresse der nächsten
Speicherzelle berechnen.
Angenommen du entscheidest dich für Bit 7 = 1. Nach nochmal eine Runde
den Ringbuffer gefüllt kippt der Zustand von Bit 7 = 1 auf 0. Nun muß
der Controller also nach Bit 7 = 0 suchen. Somit toggelt das Bit 7 immer
zwischen 0 und 1 nach jedem Ringbufferdurchgang. Der Controller muß nun
wissen nach welchem Zustand für Bit 7 er suchen muß. Denn dieser
Suchzustand muß ja auch toggeln. Du musst also den Suchzustand für Bit 7
ebenfalls im EPPROM abspeichern und das am besten in einem Ringbuffer.
Du könntest damit nur das Ratio verändern, also den Anteil an Indexbits
in Relation zu Datenbits. Schreiben/Löschen müsstest du dann doch wieder
maximal 2 Bytes pro Preset. In deinem Falle zb. ein Datenringbuffer mit
256 Bytes. Zugehörig ein Suchbit-Ringbuffer mit 256/8= 32 Bytes. Dieser
Ringbuffer speichert den aktuellen Suchbiut-Zustand der ja nur alle 256
Preset toggelt. Also musst du auch nur alle 256 Presets diesen
Suchbitringbuffer aktualisieren. Nach 256 Presets im Datenringbuffer
toggelt Bit 7. Diesen neuen Bit 7 Zustand musst du im anderen 32Bytes
Ringbuffer bitcodiert speichern. Das Ratio sinkt auf 256 zu 1, also 256
1x Datenschreiben + 1 x Bitzustand schreiben.
Gruß Hagen
Besse wäre es dann so vorzugehen: ein 256 Bytes Ringbuffer. Die obersten
2 Bits eines jeden Bytes enthält einen Zähler. Dieser geht immer nur von
0 bis 2, also 3 Elemente. Nun haben wir folgende Konstellation: 256
Bytes / 3 = teilerfremd. Es muß also in einem gefüllten Ringbuffer immer
eine Stelle geben bei der die 3 aufeinandrfolgenden Bytes in deren
obersten 2 Bits eine Indexfolge haben die nicht {0,1,2} mod 3 ist. Nach
dieser Stelle suchen wir beim Laden. Beim Speichern vervollständigen wir
diese Stelle mit der richtigen Indexreihenfolge, also {0,1,2} mod 3. Zb.
ist unsere "Bruchstelle" mit 0,1,1 befüllft. Die dritte Stelle enthält 1
müsste aber 2 in den Indexbits stehen haben. Ergo der vorherige
Datenwert ist unser letzter Preset. Der nächste zu scheibende Preset muß
Indexbits = 2 bekommen. Es entsteht folgedes Speicherbilf der Indexbits:
Aktueller Zustand = 0,1,2,0,1,*1,2,0,1,2,
Mit * unsere Stelle in der die Indixbits unterbrochen sind und somit
exakt die Speihcerstelle in der der nächste Preset gespeichert werden
muß. Die Speicherzelle davor ist unser letzter benutzter Preset.
Neuer Zustand = 0,1,2,0,1,2,*2,0,1,2,..
Die Stelle mit * ist die nächste zu beschreibende Zelle die davor
enthält den gerade geschriebenen Preset.
Das funktioniert weil wir 256 Zellen im Ringbuffer haben aber nur 3
verschiedene Indizes. Damit ist unser Indexzähler immer teilerfremd zur
Ringbuffergröße.
Vereinfacht man dies so gilt auch: Ringbuffergröße zb. 7 = Primzahl,
oder jede ungerade Zahl. Index nur 1 Bit groß, also 0 oder 1.
Unsere Indexbits sehen so aus 0 1 0 1 0 0 1.
Wir suchen nach der Stelle in den Indexbits die nicht abwechselnd
toggelt. Also hier die unterstrichene 0. Das ist die Speicherstelle in
der wir den nächsten Preset speichern müssen. Davor die Zelle in der der
letzte Preset steht. Da 7 teilerfremd zu 2 ist, also 7 Bytes im
Ringbuffer ist teilerfremd zu 2 Indexbits geht das. Wir finden immer
eine Stelle deren Indexbits zwei gleiche Werte haben müssen, entweder 00
oder 11. Es wird nach der Intialisierung immer nur eine eineindeutige
Stelle geben in der dieser Zustand auftaucht alle anderen sind immer
abwechseln 0 1 oder 1 0.
Diese Methode verbraucht 1 Indexbit pro Datenbyte und wirklich nur 1
Schreib/Löschzyklus pro Datenbyte.
Gruß Hagen
Zunächst mal eins vorweg: Ich hab die Geschichte jetzt nach Peter's
Vorschlag implementiert, tut wunderbar.
Es werden 30 Byte im EEPROM für das "Merken" des zuletzt verwendeten
Presets verwendet, davon zehn für die tatsächliche Id und 20 als
"Zugriffscounter".
Bei einem angenommenen "Umschaltintervall" von 10min sollte dieses
System für die nächsten 125 Jahre funktionieren... ;)
Die Lösung ist für mich vollkommen zufriedenstellend und ausreichend.
Die Idee mit dem Togglebit finde ich trotzdem klasse!
Hagen, ich bin übrigens der Meinung dass es funktionieren sollte, es ist
imho nicht notwendig den letzten "Toggle-Zustand" zu merken.
Begründung: Der Ringbuffer wird immer in der gleichen Richtung
beschrieben, von "vorne" nach "hinten". Physikalisch ist es ja nunmal
ein linearer Speicherbereich.
Beim Neustart des Systems ist es daher ein leichtes, den letzten
geschriebenen Wert zu finden: Dieser Wert muss das gleiche Togglebit wie
der erste Wert im Puffer haben. Haben alle Werte im Puffer das gleiche
Togglebit dann ist der letzte Wert im Puffer auch der aktuell gültige
(bzw. zuletzt geschriebene).
Somit kriegt man problemlos den Index des letzten aktuellen Wertes raus,
damit sind dann auch weitere Schreiboperationen kein Problem.
P.S.: Ich möchte auch nochmal allen Beteiligten für die rege Diskussion
danken. Ihr liefert mir ne Menge prima Ideen für die Zukunft!
>Die Idee mit dem Togglebit finde ich trotzdem klasse!>Hagen, ich bin übrigens der Meinung dass es funktionieren sollte, es ist>imho nicht notwendig den letzten "Toggle-Zustand" zu merken.
Korrekt, unter der Annahme: Ringbuffergröße = 2x+1 ist teilerfremd zu
Indexgröße = 2^y. Das habe ich oben schon erläutert. Bei zb. Indexgröße
= 2 also 1 Bit groß und 2 Zustände 0 oder 1 muß der Ringbuffer nur eine
ungerade Anzahl von Elementen haben. Im Grunde simpelste modulare
Arithmetik.
Gruß Hagen
@Hagen: Imo ist die Puffergröße egal, siehe mein letzter Post.
>Begründung: Der Ringbuffer wird immer in der gleichen Richtung>beschrieben, von "vorne" nach "hinten". Physikalisch ist es ja nunmal>ein linearer Speicherbereich.>>Beim Neustart des Systems ist es daher ein leichtes, den letzten>geschriebenen Wert zu finden: Dieser Wert muss das gleiche Togglebit wie>der erste Wert im Puffer haben. Haben alle Werte im Puffer das gleiche>Togglebit dann ist der letzte Wert im Puffer auch der aktuell gültige>(bzw. zuletzt geschriebene).>>Somit kriegt man problemlos den Index des letzten aktuellen Wertes raus,>damit sind dann auch weitere Schreiboperationen kein Problem.
Du gehst von einem "echten" Ringpuffer aus, d.h. der Beginn des Puffers
ist unbekannt, man kann im Prinzip immer nur das "nächste" Element aus
dem Puffer ziehen.
Dem ist ja aber nicht so, wir haben es mit einem linearen
Speicherbereich zu tun. Somit haben wir ja eine absolute Information von
der wir ausgehen können: Den Beginn des Speicherbereichs des
Ringpuffers.
Und das aktuell gültige Togglebit (d.h. das Bit mit dem auch der letzte
Wert geschrieben wurde) ist immer das Togglebit des ersten Eintrags im
Puffer, d.h. des Eintrags bei Index 0.