# NVM-Konfiguration **Dateien:** `config/nvm_config.h`, `config/nvm_config.cpp` ## Flash-Layout | Row | Adresse | Größe | Inhalt | |---|---|---|---| | Row 0 | `0x1FE00` | 256 B | SDeviceConfig (223 B genutzt, 33 B Padding) | | Row 1 | `0x1FF00` | 256 B | SMacroTable (256 B, komplett genutzt) | Beide Rows sind im Linkerscript vom Code-Bereich ausgeschlossen. ## SDeviceConfig – Byte-Layout (223 Byte, packed) | Offset | Größe | Feld | |---|---|---| | 0 | 4 | `magic` = `0x56503202` ('VP2\x02') | | 4 | 1 | `version` = 2 | | 5 | 2 | `crc` – CRC16-CCITT über Bytes 7–222 | | 7 | 60 | `mx_actions[20]` – 20 × 3 B SAction | | 67 | 36 | `enc_actions[4][3]` – 12 × 3 B SAction | | 103 | 20 | `led_r[20]` | | 123 | 20 | `led_g[20]` | | 143 | 20 | `led_b[20]` | | 163 | 20 | `led_anim[20]` – LEDAnim-Typ als uint8_t | | 183 | 40 | `led_period_ms[20]` – uint16_t, little-endian | | **223** | — | Ende des genutzten Bereichs | `__attribute__((packed))` ist zwingend. Ohne packed wäre SAction 4 B statt 3 B (Alignment-Padding), was `sizeof(SDeviceConfig)` um 32 B vergrößert und die C#-Deserialisierung in VersaGUI zerstört. ## CRC16-CCITT - Polynom: `0x1021`, Init: `0xFFFF` - Berechnet über Bytes 7–248 (ab `mx_actions`, nach dem `crc`-Feld selbst) - Sichert Datenintegrität nach NVM-Schreiben und bei Versionswechsel ## Lese-Logik ``` memcpy aus Flash-Adresse 0x1FE00 if magic != 0x56503202: Defaults laden, return false if version != 2: Defaults laden, return false if crc != crc(cfg): Defaults laden, return false return true ``` Kein Absturz bei ungültiger Config – Defaults greifen immer. ## Defaults - Alle Aktionen: `NONE` - LEDs: warm-weiß (R=80, G=40, B=0) - Animation: `COLOR_CYCLE` (Typ 5), Period 4000 ms ## Schreib-Logik (NVM-Mechanik) SAMD21 NVM: Row = 256 B = 4 Pages à 64 B. Schreiben erfordert: 1. `NVMCTRL->CTRLB.bit.MANW = 1` (manueller Schreib-Modus, kein Auto-Write) 2. Row löschen (`NVMCTRL_CTRLA_CMD_ER`) 3. Page-Buffer löschen (`NVMCTRL_CTRLA_CMD_PBC`) 4. 64 B als `uint32_t*` in Page-Buffer schreiben 5. Page programmieren (`NVMCTRL_CTRLA_CMD_WP`) 6. Schritte 3–5 viermal (für alle 4 Pages) > `NVMCTRL->ADDR.reg = addr / 2` – NVMCTRL erwartet Wort-Adresse (16-Bit-Worte), nicht Byte-Adresse. > **Aligned-Buffer-Pflicht**: `nvm_write_page` castet `data` zu `const uint32_t*`. Der Puffer muss `__attribute__((aligned(4)))` sein. Packed Structs sind nicht garantiert aligned → immer via lokalen `uint8_t buf[256] __attribute__((aligned(4)))` + `memcpy` übergeben. --- ## Geplante Erweiterung: NVM v3 ### Motivation Das bisherige Layout (2 Rows, 512 B) stößt an mehrere Grenzen: - **Makro-Steps zu kurz** — 4 Steps reichen für komplexe Shortcuts (z.B. Excel-Ribbon-Navigation: Alt → Buchstabe → Buchstabe → ...) nicht aus. Ziel: 8 Steps. - **Keine Profile** — Eine einzige Config erlaubt keine Umschaltung zwischen Layouts (z.B. Coding vs. Tabellenkalkulation). Ziel: 3 unabhängige Profile. - **Keine Helligkeitssteuerung** — Weder global noch pro LED einstellbar. Beide Ebenen sollen konfigurierbar werden. - **Encoder-Sensitivity** — Schrittweite pro Encoder soll konfigurierbar sein. Das bisherige Layout hat außerdem Config und Macros in denselben Adressbereich gemischt (`0x1FE00` Config, `0x1FF00` Macros). Das neue Layout trennt beide Bereiche sauber. ### Neues 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 (Bytes 0–255) | | Config Row 1 | `0x1FE00` | 256 B | Profil 0 (Rest) + Profil 1 (Bytes 256–511) | | Config Row 2 | `0x1FF00` | 256 B | Profil 1 (Rest) + Profil 2 + Reserve (Bytes 512–767) | Macros und Config liegen in vollständig getrennten, jeweils zusammenhängenden Row-Blöcken. ### Config-Inhalt (768 B, davon 740 B genutzt, 28 B Reserve) **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 alle Nutzdaten (ab Byte 7) | | 7 | 1 | `active_profile` (0–2) | | 8 | 1 | `global_brightness` (0–255) | | 9 | 4 | `enc_sensitivity[4]` (1 B pro Encoder) | | 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]` ← neu | | 176 | 20 | `led_anim[20]` | | 196 | 40 | `led_period_ms[20]` | ### Makro-Tabelle (512 B) 32 Slots × **8 Steps** × 2 B = 512 B. Gegenüber v2 doppelt so viele Steps (4 → 8), Slot-Anzahl und Struktur bleiben gleich. Siehe [04_macro_system.md](04_macro_system.md). ### Migration von v2 Beim Laden: wenn `magic` oder `version` nicht zu v3 passen, werden Defaults geladen (kein Migrations-Pfad von v2 → v3, da das Layout inkompatibel ist). Eine einmalige Neukonfiguration nach dem Firmware-Update ist nötig.