Repository navigation
flash.pl
FLASH to najniższa warstwa przechowywania danych w Core: kasuje strony, zapisuje podwójne słowa i odczytuje je przez ECC.
Na nim działają PDB i EEPROM, a FLASH_Save przechowuje jeden rekord dowolnego rozmiaru.
Jak zeszyt pisany długopisem, pamięć flash przyjmuje podwójne słowo raz, a skasować można tylko całą stronę.
- Pamięć flash dzieli się na strony:
2kBw STM32G0 i4kBw STM32WB.FLASH_PAGESpodaje ich liczbę, w WB tylko strony rdzenia aplikacji, poniżej obszaru stosu radiowego. - Skasowana strona ma same
0xFF.FLASH_Erasekasuje jedną stronę, a stronę, która już odczytuje się jako skasowana, pomija. -
FLASH_Writezapisuje jedno podwójne słowo, 8 bajtów, czyli najmniejszą jednostkę zapisu, i odczytuje je z powrotem. Zapisane podwójne słowo zostaje do skasowania strony. - Obok każdego podwójnego słowa pamięć trzyma 8 bitów ECC: pojedynczy przestawiony bit jest poprawiany przy odczycie, a dwa są wykrywane.
Zapis przerwany resetem albo zanikiem zasilania może zostawić podwójne słowo, którego ECC nie odczyta, a w STM32 taki odczyt wywołuje NMI, które zatrzymuje układ.
-
FLASH_Copykopiuje zakres do RAM i odczytuje każde podwójne słowo raz, w oknie rozpoznawanym przez obsługę NMI z Core: podwójne słowo, którego ECC nie odczyta, dajeERR, a układ pracuje dalej. -
correctedinformuje, że ECC poprawiło bit w zakresie. Przerwany zapis może też zostawić podwójne słowo, które ECC poprawia do złej wartości, dlatego magazyny nie ufają najnowszemu rekordowi odczytanemu z poprawką. -
FLASH_Checkodczytuje tak samo, ale bez kopiowania, i daje tylko werdykt: dane, które mógł rozerwać przerwany zapis, zawsze idą przezFLASH_Copy, bo podwójne słowo może przejść sprawdzenie, a przy późniejszym odczycie zawieść. -
FLASH_ReadiFLASH_Refczytają poza tym oknem, dla kodu i stałych, których żaden zapis nie przerwie.
FLASH_Save przechowuje jeden rekord, od bajtu do kilku stron: kasuje strony, zapisuje treść, a nagłówek z rozmiarem i jego dopełnieniem zapisuje na końcu.
FLASH_Load zwraca rozmiar rekordu albo 0, gdy strona go nie zawiera.
- Zapis przerwany przed nagłówkiem zostawia nagłówek skasowany, a przerwany w jego trakcie zostawia rozmiar niezgodny z dopełnieniem, więc strona nie zawiera rekordu, nigdy mieszanki starego i nowego.
- Nowy zapis najpierw kasuje stary rekord, więc przerwany w połowie traci oba. Dane, które muszą przetrwać zanik zasilania, należą do PDB albo EEPROM.
-
FLASH_Comparesprawdza, czy strona zawiera już dokładnie te bajty, więc niezmienione dane nie są zapisywane ponownie. - Rekord nie ma własnej sumy kontrolnej: treść chroni tylko ECC.
- Przy kasowaniu: strona nie jest skasowana w całości.
FLASH_Erasetraktuje bit poprawiony przez ECC jako nieskasowany, więc kasuje stronę ponownie zamiast ją pominąć. - Przy zapisie: podwójne słowo jest rozerwane. Odczyt przez
FLASH_CopydajeERRalbo wartość z poprawką, a decyzję podejmuje magazyn wyżej. - Przy
FLASH_Save: nagłówka brakuje albo jest rozerwany, więc rekordu nie ma.
Testy na komputerze przerywają zasilanie w każdym cyklu pamięci flash podczas zapisu rekordu, przed cyklem i w jego połowie, a strona zawsze zawiera stary rekord, żaden albo nowy.
Jedna strona poza programem i innymi magazynami, taka sama w każdej wersji firmware, oraz rekord, którego rozmiar nie maleje między wersjami, bo FLASH_Load zapisuje tyle bajtów, ile zapisano na stronie.
// One page outside the program and the other stores
#define RECIPE_PAGE 46
typedef struct {
uint16_t speed_rpm;
uint16_t ramp_ms;
int16_t offset;
uint16_t mode;
} Recipe_t;Na starcie rekord jest wczytywany raz, a zapisywany tylko wtedy, gdy się zmienił.
static Recipe_t recipe = { .speed_rpm = 1500, .ramp_ms = 200 };
void recipe_init(void)
{
// A page without a record keeps the defaults above
Recipe_t stored;
if(FLASH_Load(RECIPE_PAGE, (uint8_t *)&stored) == sizeof(stored)) recipe = stored;
}
void recipe_save(void)
{
// Unchanged data is not written again, which spares an erase
if(FLASH_Compare(RECIPE_PAGE, (const uint8_t *)&recipe, sizeof(recipe))) return;
FLASH_Save(RECIPE_PAGE, (const uint8_t *)&recipe, sizeof(recipe));
}