Wyrównanie zużycia w pamięci EEPROM mikrokontrolera

15

Na przykład: Arkusz danych dla ATtiny2313 (podobnie jak większość arkuszy danych Atmel AVR) stwierdza:

128 bajtów Programowalna w systemie pamięć EEPROM: 100 000 cykli zapisu / kasowania

Wyobraź sobie, że program wymaga tylko dwóch bajtów do przechowywania pewnej konfiguracji, pozostałe 126 bajtów zostaje skutecznie zmarnowane. Martwi mnie to, że regularne aktualizacje dwóch bajtów konfiguracji mogą zużyć pamięć EEPROM urządzenia i uczynić go bezużytecznym. Całe urządzenie stałoby się zawodne, ponieważ w pewnym momencie po prostu nie można śledzić, które bajty w pamięci EEPROM są zawodne.

Czy istnieje sprytny sposób na wyrównywanie zużycia w pamięci EEPROM mikrokontrolera, gdy efektywnie używasz tylko jednego lub dwóch bajtów z dostępnych 128?

jippie
źródło
1
Gdyby 100 000 cykli zapisu stanowiło ograniczenie, czy miałoby sens zastosowanie innej technologii? Albo mechanizm, który obejmuje wewnętrzne poziomowanie, albo coś o rzędzie wielkości lub większej wytrzymałości?
Anindo Ghosh
1
@AnindoGhosh Po prostu nie chcę marnować mojego małego zapasu mikrokontrolerów tylko z powodu zużycia pamięci EEPROM z powodu moich testów dowodu koncepcji. Nie chcę się martwić, którego bajtu użyłem w poprzednim projekcie podczas ponownego używania kontrolera. I dobrze jest wiedzieć, że optymalnie wykorzystuję dostępny sprzęt.
jippie
1
Może rzucisz okiem na moją odpowiedź dotyczącą stackoverflow .
JimmyB
Spójrz na serię TI MSP430 FRAM ... 10 ^ 13 pisze !!!
geometrikal

Odpowiedzi:

19

Zwykle stosuję technikę poprzedzania danych 4-bajtowym ciągłym numerem sekwencyjnym, w którym największa liczba reprezentuje ostatnią / aktualną wartość. W przypadku przechowywania 2 bajtów rzeczywistych danych, które dałyby w sumie 6 bajtów, a następnie formuję się w koleinowe ustawienie kolejki, więc dla 128 bajtów EEPROM zawiera 21 wpisów i zwiększa wytrzymałość 21 razy.

Następnie podczas uruchamiania można użyć największego numeru sekwencyjnego, aby określić zarówno następny numer sekwencyjny, jaki ma zostać użyty, jak i bieżący koniec kolejki. Poniższy pseudo-kod C pokazuje, że zakłada się, że podczas początkowego programowania obszar EEPROM został skasowany do wartości 0xFF, więc ignoruję numer kolejny 0xFFFF:

struct
{
  uint32_t sequence_no;
  uint16_t my_data;
} QUEUE_ENTRY;

#define EEPROM_SIZE 128
#define QUEUE_ENTRIES (EEPROM_SIZE / sizeof(QUEUE_ENTRY))

uint32_t last_sequence_no;
uint8_t queue_tail;
uint16_t current_value;

// Called at startup
void load_queue()
{
  int i;

  last_sequence_no = 0;
  queue_tail = 0;
  current_value = 0;
  for (i=0; i < QUEUE_ENTRIES; i++)
  {
    // Following assumes you've written a function where the parameters
    // are address, pointer to data, bytes to read
    read_EEPROM(i * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
    if ((QUEUE_ENTRY.sequence_no > last_sequence_no) && (QUEUE_ENTRY.sequence_no != 0xFFFF))
    {
      queue_tail = i;
      last_sequence_no = QUEUE_ENTRY.sequence_no;
      current_value = QUEUE_ENTRY.my_data;
    }
  }
}

void write_value(uint16_t v)
{
  queue_tail++;
  if (queue_tail >= QUEUE_ENTRIES)
    queue_tail = 0;
  last_sequence_no++;
  QUEUE_ENTRY.sequence_no = last_sequence_no;
  QUEUE_ENTRY.my_data = v;
  // Following assumes you've written a function where the parameters
  // are address, pointer to data, bytes to write
  write_EEPROM(queue_tail * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
  current_value = v;
}

W przypadku mniejszej pamięci EEPROM sekwencja 3-bajtowa byłaby bardziej wydajna, chociaż wymagałaby nieco fragmentowania bitów zamiast korzystania ze standardowych typów danych.

PeterJ
źródło
+1, niezłe podejście. Czy przechowywanie można zoptymalizować nieco przy użyciu mniejszej liczby „tagów” ​​i być może w zależności od jakiejś formy mechanizmu segmentu mieszającego, aby zapewnić dodatkową dystrybucję? Hybryda pomiędzy brakiem niwelacji a twoim podejściem?
Anindo Ghosh,
@AnindoGhosh, tak, wierzę, że może. Zazwyczaj stosowałem to podejście w małych mikroskach dla uproszczenia kodu, a osobiście stosowałem je głównie na większych urządzeniach, takich jak DataFLASH. Innym prostym pomysłem, który przychodzi na myśl, jest to, że numery sekwencji można okresowo obniżać, aby utrzymać je na mniejszych wartościach.
PeterJ
Nota aplikacyjna Atmel wspomniana przez @ m.Alin ma sprytne uproszczenie: po RESETIE można następnie przejrzeć [...] bufor, znajdując ostatni [...] element bufora zmieniony poprzez znalezienie lokalizacji, w której różnica między elementem buforowym a następnym elementem buforowym jest większa niż 1 .
jippie
Czy write_value () nie powinien umieszczać wpisu w kolejce_tail * sizeof (QUEUE_ENTRY)? za pierwszym razem będę poprawny, ale czy nie powinien kontynuować, jeśli jest wiele zapisów? i nie jest zwiększany poza load_queue ().
Marshall Eubanks
2
@ DWORD32: Tak, to technicznie poprawne, ale nieistotne w praktyce. Do tego czasu limit zużycia pamięci EEPROM zostanie przekroczony 2000 razy!
Dave Tweed
5

Poniżej znajduje się metoda wykorzystująca segmenty i około jeden bajt napowietrzny na segment. Bajty łyżki i bajty napowietrzne zużywają się mniej więcej tak samo. W omawianym przykładzie, biorąc pod uwagę 128 bajtów EEPROM, ta metoda przydziela 42 2-bajtowe segmenty i 44 bajty statusu, zwiększając zdolność zużycia około 42-krotnie.

Metoda:

Podziel przestrzeń adresową EEPROM na k segmentów, gdzie k = ⌊ E / ( n +1) ⌋, gdzie n = rozmiar tablicy danych konfiguracji = rozmiar segmentu, a E = rozmiar EEPROM (lub, bardziej ogólnie, liczba EEPROM komórki, które zostaną poświęcone tej strukturze danych).

Zainicjuj katalog, tablicę m bajtów ustawioną na k , przy czym m = En · k . Podczas uruchamiania urządzenie czyta katalog, dopóki nie znajdzie bieżącego wpisu, który jest bajtem różnym od k . [Jeśli wszystkie wpisy katalogu są równe k , zainicjuj pierwszy wpis katalogu na 0 i kontynuuj odtąd.]

Gdy bieżący wpis katalogu zawiera j , segment j zawiera bieżące dane. Kiedy musisz napisać nowy wpis danych konfiguracji, zapisujesz j +1 w bieżącym wpisie katalogu; jeśli daje to wartość k , zainicjuj następny wpis katalogu na 0 i kontynuuj od tego momentu.

Zauważ, że bajty katalogów zużywają się mniej więcej tak samo jak bajty segmentu, ponieważ 2 · k > mk .

(Dostosowałem powyższe z mojej odpowiedzi na pytanie 34189 w Arduino SE , w jaki sposób zwiększyć żywotność pamięci EEPROM? ).

James Waldby - jwpat7
źródło
2

Użyłem do tego ruchomego numeru sekwencyjnego (podobnego do odpowiedzi Piotra). Numer sekwencyjny może faktycznie wynosić zaledwie 1 bit, pod warunkiem, że liczba elementów w pamięci jest nieparzysta. Głowa i ogon są następnie oznaczane przez 2 kolejne 1 lub 0

Na przykład, jeśli chcesz przewinąć przez 5 elementów, numerami sekwencji będą:

{01010} (zapis do 0) {11010} (zapis do 1) {10010} (zapis do 2) {10110} (zapis do 3) {10100} (zapis do 4) {10101} (zapis do 5)

Mick Clift
źródło
1

Istnieje kilka opcji w zależności od rodzaju posiadanej pamięci EEPROM i rozmiaru danych.

  1. Jeśli twoja pamięć EEPROM ma indywidualnie kasowane strony i używasz 1 strony (lub więcej), po prostu usuń wszystkie strony z wyjątkiem tych, które są w użyciu, i ponownie wykorzystuj strony w okrągły sposób.

  2. Jeśli używasz tylko części strony, którą należy natychmiast usunąć, podziel tę stronę na wpisy danych. Używaj czystego wpisu za każdym razem, gdy piszesz, i usuń, gdy zabraknie czystych wpisów.

W razie potrzeby użyj „brudnego” bitu, aby odróżnić czyste i brudne wpisy (zwykle masz co najmniej jeden bajt, który z pewnością różni się od 0xFF, którego można użyć do śledzenia brudnych wpisów).

Jeśli twoja biblioteka EEPROM nie ujawnia funkcji kasowania (jak Arduino), oto fajna sztuczka dla algorytmu nr 2: ponieważ twój pierwszy wpis EEPROM jest zawsze używany, możesz określić wartość „brudnego” bitu, czytając go. Następnie, gdy zabraknie Ci czystych wpisów, zacznij od nowa od pierwszego wpisu, odwracając bit „brudny”, a reszta Twoich wpisów zostanie automatycznie oznaczona jako „czysta”.

Numery sekwencji i katalogi to strata miejsca, chyba że chcesz mieć możliwość samodzielnego śledzenia złych stron lub aktualizowania różnych części danych EEPROM.

Dmitrij Grigoriew
źródło