# Pico Database PDB _(pico database)_ przechowuje w pamięci flash mikrokontrolera małe rekordy, na przykład mailbox bootloadera, dziennik zdarzeń albo dane fabryczne. Pamięć flash działa jak zeszyt pisany długopisem: zapisanej linii nie da się poprawić, a skasować można tylko całą stronę. Dlatego PDB niczego nie zmienia, tylko dopisuje kolejne rekordy. ## Strona i rekord ![Strona PDB](.img/pdb-page-PL.svg) - Każda strona zaczyna się nagłówkiem zapisywanym zaraz po jej skasowaniu, więc strona bez nagłówka nie zawiera danych. - Dalej są rekordy o jednakowym rozmiarze, a numery nadaje im sam PDB: 1, 2, 3. - Na końcu rekordu jest suma kontrolna CRC, zapisywana jako ostatnia. Rekord bez poprawnego CRC nie został dokończony i przy odczycie jest pomijany. ## Dwie strony w pierścieniu ![Dwie strony w pierścieniu](.img/pdb-ring-PL.svg) Gdy nowsza strona się zapełni, PDB kasuje starszą i zapisuje dalej na niej, więc zawsze zostaje jedna pełna strona historii. Dane, których nie wolno stracić, na przykład dane fabryczne, zajmują jedną stronę bez pierścienia: po jej zapełnieniu zapis jest odrzucany, a obowiązuje najnowszy poprawny rekord. ## Zanik zasilania ![Zanik zasilania](.img/pdb-power-cut-PL.svg) Zasilanie może zaniknąć w dowolnej chwili, a PDB obsługuje każdy z trzech przypadków: - Przy zapisie rekordu: rekord nie ma CRC i przy odczycie jest pomijany. - Przy kasowaniu strony: strona nie ma nagłówka i przed zapisem jest kasowana od nowa. - Na samym początku zapisu: miejsce wygląda na puste, choć nie jest, więc pierwszy zapis po każdym starcie zeruje pierwsze wolne miejsce, a rekord trafia do następnego. Odczyt niczego nie zapisuje, a PDB nigdy nie kasuje całej bazy z powodu błędu. ## Wykorzystanie ![Wykorzystanie PDB](.img/pdb-uses-PL.svg) Każde wykorzystanie to zwykła baza PDB z własnym rekordem i trybem: - Mailbox: bootloader i aplikacja zapisują historię slotów na dwóch stronach w pierścieniu. - Dziennik zdarzeń: aplikacja dopisuje zdarzenia na dwóch stronach w pierścieniu. - Dane fabryczne: jedna strona bez pierścienia. Znaczenie rekordu określa jego użytkownik, a PDB obsługuje każdy rekord tak samo. ## Konfiguracja Rekord o stałym rozmiarze: pierwsze 4 bajty to numer, a ostatnie 4 bajty to CRC, oba uzupełnia PDB. Baza zajmuje dwie strony w pierścieniu, a jej rodzaj odróżnia ją od innej bazy na tych stronach. ```c typedef struct { uint32_t number; // first 4 bytes: the number PDB gives the record uint32_t time; uint16_t code; uint16_t value; uint32_t crc; // last 4 bytes: the CRC PDB writes } Event_t; // Two pages outside the program, a ring; `.no_wrap = true` stops when they are full static PDB_t events = { .page_start = 48, .page_count = 2, .record_size = sizeof(Event_t), .kind = 0x5645 }; ``` ## Użycie Po każdym starcie najpierw `PDB_Init`; rekord dopisuje się w całości, a odczyt przechodzi po rekordach pasujących do zapytania. ```c void events_init(void) { PDB_Init(&events); // reads the headers and finds where to write, writes nothing } void event_add(uint16_t code, uint16_t value) { Event_t event = { .time = (uint32_t)RTC_Timestamp(), .code = code, .value = value }; PDB_Insert(&events, &event); // PDB fills in the number and the CRC } void events_print(void) { PDB_Query_t query = { .limit = 10, .dir = PDB_Desc }; // the newest ten PDB_Iter_t iter; Event_t event; PDB_IterInit(&events, &iter, &query); while(PDB_IterNext(&iter, &event) == OK) { LOG_INF("%u time:%u code:%u value:%u", event.number, event.time, event.code, event.value); } } ```