VersaMCU/doc/06_nvm_config.md

108 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NVM-Konfiguration
**Dateien:** `config/nvm_config.h`, `config/nvm_config.cpp`
## Flash-Layout (5 Rows, 0x1FB000x1FFFF)
| Row | Adresse | Größe | Inhalt |
|---|---|---|---|
| Macro Row 0 | `0x1FB00` | 256 B | SMacroTable Bytes 0255 |
| Macro Row 1 | `0x1FC00` | 256 B | SMacroTable Bytes 256511 |
| 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 7739 |
| 7 | 1 | `active_profile` (02) |
| 8 | 1 | `global_brightness` (0255) |
| 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 (0255) |
| 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 7739 (ab `active_profile`, nach dem `crc`-Feld selbst)
- Sichert alle Nutzdaten einschließlich `active_profile`
> **Wichtig bei PROFILE_SWITCH:** `active_profile` liegt im CRC-Bereich. Nach jeder Änderung muss `cfg.crc = nvm_config_crc(cfg)` aufgerufen werden bevor gespeichert wird — sonst lädt `nvm_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:
1. `NVMCTRL->CTRLB.bit.MANW = 1` (manueller Schreib-Modus)
2. 3 Rows löschen (`NVMCTRL_CTRLA_CMD_ER`) — bei Fehler: `return false`
3. 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`
4. `return true`
### nvm_wait() Timeout
```cpp
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_page` castet `data` zu `const uint32_t*`. Der Puffer muss `__attribute__((aligned(4)))` sein. Packed Structs sind nicht garantiert aligned → immer via lokalem `uint8_t buf[] __attribute__((aligned(4)))` + `memcpy` übergeben.