Skip to content

eeprom.pl

Xaeian edited this page Oct 23, 2026 · 2 revisions

Emulacja EEPROM w pamięci flash

EEPROM przechowuje w pamięci flash mikrokontrolera ustawienia pod 32-bitowymi kluczami, na przykład wartość zadaną, kalibrację albo licznik. Mikrokontroler nie ma własnej pamięci EEPROM, dlatego biblioteka emuluje ją w pamięci flash. Podobnie jak PDB, niczego nie zmienia w zapisanych danych: nowa wartość trafia do nowej komórki, a odczyt bierze najnowszą.

Komórki i połowy

Połowy EEPROM

  • Magazyn zajmuje parzystą liczbę stron pamięci flash i dzieli je na dwie połowy, A i B. Do jednej trafiają zapisy, druga pozostaje skasowana.
  • Każdy zapis dodaje jedną 8-bajtową komórkę: klucz i wartość, po 4 bajty. Ponowny zapis klucza zajmuje nową komórkę, a obowiązuje najnowsza.
  • Przepisana połowa zaczyna się znacznikiem BEGIN, a kończy znacznikiem generacji, czyli licznika rosnącego przy każdym przepisaniu.
  • Cztery najwyższe klucze, od 0xFFFFFFFC w górę, należą do samego EEPROM, a zapis z którymś z nich jest odrzucany.

Przepisanie

Przepisanie pełnej połowy

Gdy aktywna połowa się zapełni, EEPROM przepisuje ją do drugiej w tej kolejności:

  1. Znacznik BEGIN.
  2. Najnowsza wartość każdego klucza, po jednej komórce.
  3. Znacznik z kolejnym numerem generacji.
  4. Dopiero wtedy pełna połowa jest kasowana.

Historia klucza skraca się do jednej komórki, a zapis trwa dalej w nowej połowie. Klucz, który nie mieści się w połowie, przepada przy przepisaniu, dlatego zbiór kluczy musi być znacznie mniejszy niż połowa.

Zanik zasilania

Zanik zasilania

Zasilanie może zaniknąć w dowolnej chwili. Przy następnym starcie EEPROM_Init odczytuje obie połowy i rozpoznaje, co było w toku:

  • Przy zapisie wartości: komórka nie jest dokończona, więc się nie liczy, a jej klucz zachowuje starą wartość.
  • Przy przepisaniu, przed znacznikiem generacji: nowa połowa ma znacznik BEGIN, ale nie ma znacznika generacji, więc jest kasowana, a przepisanie rusza od nowa.
  • Po znaczniku generacji, przed skasowaniem pełnej połowy: obie połowy są kompletne, obowiązuje wyższa generacja, a druga połowa jest kasowana.
  • Pod koniec kasowania: strona może odczytywać się jako skasowana, choć nie została skasowana do końca, więc EEPROM kasuje połowę w całości przed zapisem do niej, także taką, która odczytuje się jako skasowana.

Zanik zasilania w trakcie tego odtwarzania jest obsługiwany tak samo przy kolejnym starcie. Po zaniku zasilania w dowolnej chwili każdy klucz ma ostatnią zapisaną wartość, a klucz zapisywany w tej chwili starą albo nową. Testy na komputerze przerywają zasilanie w każdym cyklu pamięci flash długiej serii zapisów i przepisań, a także w trakcie samego odtwarzania.

Poza tą gwarancją pozostają trzy przypadki:

  • Wartość zapisana pod dwoma kluczami, na przykład 64-bitowa, to dwa zapisy, więc zanik zasilania między nimi zostawia jej części z dwóch różnych zapisów.
  • Przerwane EEPROM_Clear może zostawić stare wartości do odczytu.
  • Najnowsza komórka liczy się tylko wtedy, gdy odczytuje się bez błędu, więc klucz zapisany raz traci wartość, jeśli w tej komórce z wiekiem przestawi się bit.

Konfiguracja

Własny magazyn: dwie strony i klucz dla każdego ustawienia, takie same w każdej wersji firmware.

enum { KEY_Setpoint = 1, KEY_Hysteresis = 2 };

// Two pages outside the program and the board stores
static EEPROM_t settings = { .page_start = 50, .page_count = 2 };

Użycie

Po każdym starcie najpierw EEPROM_Init, a każdy odczyt podaje wartość domyślną dla klucza, którego jeszcze nie zapisano.

static uint32_t setpoint, hysteresis;

void settings_init(void)
{
  EEPROM_Init(&settings); // recovers whatever a power cut left
  // A key not written yet reads as its default
  setpoint = EEPROM_Read(&settings, KEY_Setpoint, 1500);
  hysteresis = EEPROM_Read(&settings, KEY_Hysteresis, 50);
}

void setpoint_set(uint32_t value)
{
  if(EEPROM_Write(&settings, KEY_Setpoint, value) == OK) setpoint = value;
}

Clone this wiki locally