4.2 KiB
NVM-Konfiguration
Dateien: config/nvm_config.h, config/nvm_config.cpp
Flash-Layout (5 Rows, 0x1FB00–0x1FFFF)
| Row | Adresse | Größe | Inhalt |
|---|---|---|---|
| Macro Row 0 | 0x1FB00 |
256 B | SMacroTable Bytes 0–255 |
| Macro Row 1 | 0x1FC00 |
256 B | SMacroTable Bytes 256–511 |
| Config Row 0 | 0x1FD00 |
256 B | Globaler Header + Profil 0 (teilweise) |
| Config Row 1 | 0x1FE00 |
256 B | Profil 0 (Rest) + Profil 1 (teilweise) |
| Config Row 2 | 0x1FF00 |
256 B | Profil 1 (Rest) + Profil 2 + 28 B Reserve |
Alle Rows sind im Linkerscript vom Code-Bereich ausgeschlossen. Config und Makros liegen in vollständig getrennten, zusammenhängenden Row-Blöcken.
SDeviceConfig – Byte-Layout (740 Byte, packed)
Globaler Header (32 B, Offset 0)
| Offset | Größe | Feld |
|---|---|---|
| 0 | 4 | magic = 0x56503203 ('VP2\x03') |
| 4 | 1 | version = 3 |
| 5 | 2 | crc – CRC16-CCITT über Bytes 7–739 |
| 7 | 1 | active_profile (0–2) |
| 8 | 1 | global_brightness (0–255) |
| 9 | 4 | enc_sensitivity[4] (1 B pro Encoder, Default 1) |
| 13 | 19 | Reserve |
Pro Profil (236 B, Offset 32 + idx × 236)
| Offset | Größe | Feld |
|---|---|---|
| 0 | 60 | mx_actions[20] – 20 × 3 B SAction |
| 60 | 36 | enc_actions[4][3] – 12 × 3 B SAction |
| 96 | 20 | led_r[20] |
| 116 | 20 | led_g[20] |
| 136 | 20 | led_b[20] |
| 156 | 20 | led_brightness[20] – per-LED Helligkeit (0–255) |
| 176 | 20 | led_anim[20] – LEDAnim-Typ als uint8_t |
| 196 | 40 | led_period_ms[20] – uint16_t little-endian |
Gesamt: 32 B Header + 3 × 236 B Profile = 740 B.
__attribute__((packed)) ist zwingend. Ohne packed wäre SAction 4 B statt 3 B, was sizeof(SDeviceConfig) um 32 B vergrößert und die C#-Deserialisierung zerstört.
CRC16-CCITT
- Polynom:
0x1021, Init:0xFFFF - Berechnet über Bytes 7–739 (ab
active_profile, nach demcrc-Feld selbst) - Sichert alle Nutzdaten einschließlich
active_profile
Wichtig bei PROFILE_SWITCH:
active_profileliegt im CRC-Bereich. Nach jeder Änderung musscfg.crc = nvm_config_crc(cfg)aufgerufen werden bevor gespeichert wird — sonst lädtnvm_config_load()die Defaults.
Lese-Logik
memcpy aus Flash-Adresse 0x1FD00 (740 B)
if magic != 0x56503203: Defaults laden, return false
if version != 3: Defaults laden, return false
if crc != crc(cfg): Defaults laden, return false
if active_profile >= 3: active_profile = 0
return true
Kein Absturz bei ungültiger Config – Defaults greifen immer.
Defaults
- Alle Aktionen:
NONE - LEDs: warm-weiß (R=80, G=40, B=0),
led_brightness=255 - Animation:
COLOR_CYCLE(Typ 5), Period 4000 ms active_profile = 0,global_brightness = 255,enc_sensitivity = 1
Schreib-Logik (nvm_config_save)
nvm_config_save() gibt bool zurück. false bedeutet NVM-Timeout — der NVM-Controller hat nicht rechtzeitig READY gemeldet (beobachtet nach bestimmten Bootloader/Flash-Zyklen auf SAMD21).
SAMD21 NVM: Row = 256 B = 4 Pages à 64 B. Ablauf:
NVMCTRL->CTRLB.bit.MANW = 1(manueller Schreib-Modus)- 3 Rows löschen (
NVMCTRL_CTRLA_CMD_ER) — bei Fehler:return false - Für jede der 12 Pages à 64 B:
- Page-Buffer löschen (
NVMCTRL_CTRLA_CMD_PBC) - 64 B als
uint32_t*in Page-Buffer schreiben - Page programmieren (
NVMCTRL_CTRLA_CMD_WP) — bei Fehler:return false
- Page-Buffer löschen (
return true
nvm_wait() – Timeout
static bool nvm_wait()
{
uint32_t timeout = 48000000UL / 4 * 400 / 1000; // ≈ 4 800 000 Iterationen ≈ 400 ms
while (!NVMCTRL->INTFLAG.bit.READY) {
if (--timeout == 0) return false;
}
return true;
}
Der Timeout verhindert ein dauerhaftes Einfrieren des Boards wenn NVMCTRL aus unbekanntem Grund nicht READY meldet. Bei Timeout sendet das Board CONFIG_NACK statt zu hängen.
NVMCTRL->ADDR.reg = addr / 2– NVMCTRL erwartet Wort-Adresse (16-Bit-Worte), nicht Byte-Adresse.
Aligned-Buffer-Pflicht:
nvm_write_pagecastetdatazuconst uint32_t*. Der Puffer muss__attribute__((aligned(4)))sein. Packed Structs sind nicht garantiert aligned → immer via lokalemuint8_t buf[] __attribute__((aligned(4)))+memcpyübergeben.