Added config, added factory reset functionality
This commit is contained in:
+99
-78
@@ -1,107 +1,128 @@
|
||||
# NVM-Konfiguration
|
||||
|
||||
**Dateien:** `config/nvm_config.h`, `config/nvm_config.cpp`
|
||||
Dateien:
|
||||
|
||||
## Flash-Layout (5 Rows, 0x1FB00–0x1FFFF)
|
||||
- `config/nvm_config.h`
|
||||
- `config/nvm_config.cpp`
|
||||
|
||||
| Row | Adresse | Größe | Inhalt |
|
||||
## Flash-Layout
|
||||
|
||||
| Bereich | Adresse | Groesse | 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 |
|
||||
| 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 | Header + Profil 0 Anfang |
|
||||
| Config Row 1 | `0x1FE00` | 256 B | Profil 0 Rest + Profil 1 Anfang |
|
||||
| Config Row 2 | `0x1FF00` | 256 B | Profil 1 Rest + Profil 2 + Reserve |
|
||||
|
||||
Alle Rows sind im Linkerscript vom Code-Bereich ausgeschlossen. Config und Makros liegen in vollständig getrennten, zusammenhängenden Row-Blöcken.
|
||||
Makros und Config sind komplett getrennt.
|
||||
|
||||
## SDeviceConfig – Byte-Layout (740 Byte, packed)
|
||||
## `SDeviceConfig`
|
||||
|
||||
### Globaler Header (32 B, Offset 0)
|
||||
Aktueller Stand:
|
||||
|
||||
| Offset | Größe | Feld |
|
||||
- Magic: `0x56503203`
|
||||
- Version: `3`
|
||||
- Groesse: `740` Byte
|
||||
- auf 3 Config-Rows verteilt
|
||||
|
||||
### Header
|
||||
|
||||
| Offset | Groesse | 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 |
|
||||
| `0` | 4 | `magic` |
|
||||
| `4` | 1 | `version` |
|
||||
| `5` | 2 | `crc` |
|
||||
| `7` | 1 | `active_profile` |
|
||||
| `8` | 1 | `global_brightness` |
|
||||
| `9` | 4 | `enc_sensitivity[4]` |
|
||||
| `13` | 19 | Reserve |
|
||||
|
||||
### Pro Profil (236 B, Offset `32 + idx × 236`)
|
||||
### Pro Profil
|
||||
|
||||
| Offset | Größe | Feld |
|
||||
Jedes Profil belegt 236 Byte:
|
||||
|
||||
| Offset im Profil | Groesse | 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 |
|
||||
| `0` | 60 | `mx_actions[20]` |
|
||||
| `60` | 36 | `enc_actions[4][3]` |
|
||||
| `96` | 20 | `led_r[20]` |
|
||||
| `116` | 20 | `led_g[20]` |
|
||||
| `136` | 20 | `led_b[20]` |
|
||||
| `156` | 20 | `led_brightness[20]` |
|
||||
| `176` | 20 | `led_anim[20]` |
|
||||
| `196` | 40 | `led_period_ms[20]` |
|
||||
|
||||
Gesamt: 32 B Header + 3 × 236 B Profile = **740 B**.
|
||||
Gesamtrechnung:
|
||||
|
||||
`__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 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
|
||||
```text
|
||||
32 Byte Header + 3 * 236 Byte Profile = 740 Byte
|
||||
```
|
||||
|
||||
Kein Absturz bei ungültiger Config – Defaults greifen immer.
|
||||
## CRC
|
||||
|
||||
CRC16-CCITT:
|
||||
|
||||
- Polynom `0x1021`
|
||||
- Init `0xFFFF`
|
||||
- Bereich: Bytes `7..739`
|
||||
|
||||
Damit sind auch `active_profile` und globale Helligkeit abgesichert.
|
||||
|
||||
## 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`
|
||||
`nvm_config_defaults()` setzt:
|
||||
|
||||
## Schreib-Logik (nvm_config_save)
|
||||
- `active_profile = 0`
|
||||
- `global_brightness = 255`
|
||||
- `enc_sensitivity[*] = 1`
|
||||
- alle Actions auf `NONE`
|
||||
- alle `led_brightness[*] = 255`
|
||||
- Base-Farbe `R=80, G=40, B=0`
|
||||
- `led_anim = COLOR_CYCLE`
|
||||
- `led_period_ms = 4000`
|
||||
|
||||
`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).
|
||||
Praktisch sichtbares Ergebnis:
|
||||
|
||||
SAMD21 NVM: Row = 256 B = 4 Pages à 64 B. Ablauf:
|
||||
- alle MX-LEDs laufen wieder im Regenbogenmodus
|
||||
|
||||
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`
|
||||
## Laden
|
||||
|
||||
### nvm_wait() – Timeout
|
||||
`nvm_config_load()`:
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
1. 740 Byte aus NVM kopieren
|
||||
2. Magic pruefen
|
||||
3. Version pruefen
|
||||
4. CRC pruefen
|
||||
5. bei Fehlern Defaults laden und `false` zurueckgeben
|
||||
|
||||
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.
|
||||
Die Firmware faellt also immer auf einen gueltigen Zustand zurueck.
|
||||
|
||||
> `NVMCTRL->ADDR.reg = addr / 2` – NVMCTRL erwartet Wort-Adresse (16-Bit-Worte), nicht Byte-Adresse.
|
||||
## Speichern
|
||||
|
||||
> **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.
|
||||
`nvm_config_save()`:
|
||||
|
||||
1. 740-Byte-Config in einen 768-Byte-Row-Puffer kopieren
|
||||
2. Rest mit `0xFF` fuellen
|
||||
3. `MANW = 1`
|
||||
4. 3 Rows loeschen
|
||||
5. 12 Pages zu je 64 Byte schreiben
|
||||
|
||||
Rueckgabewert:
|
||||
|
||||
- `true` bei Erfolg
|
||||
- `false` bei NVM-Timeout
|
||||
|
||||
Wichtig:
|
||||
|
||||
- der Schreibpuffer muss 4-Byte-aligned sein
|
||||
- `packed` allein reicht dafuer nicht
|
||||
|
||||
## Zusammenhang mit Werksreset
|
||||
|
||||
Der Werksreset nutzt denselben Pfad:
|
||||
|
||||
- `nvm_config_defaults(cfg)`
|
||||
- `nvm_config_save(cfg)`
|
||||
|
||||
Dadurch werden auch kaputte, aber formal noch vorhandene Alt-Daten im NVM wirklich ueberschrieben.
|
||||
|
||||
Reference in New Issue
Block a user