127 lines
3.5 KiB
Markdown
127 lines
3.5 KiB
Markdown
# VersaMCU – Architekturübersicht
|
||
|
||
## Zielplattform
|
||
|
||
| Merkmal | Wert |
|
||
|---|---|
|
||
| MCU | ATSAMD21G17D, Cortex-M0+, 48 MHz |
|
||
| Flash | 128 KB |
|
||
| RAM | 16 KB |
|
||
| FPU | keine; LED-/Timingpfade verwenden Integer-Arithmetik |
|
||
| USB | HID Keyboard + Consumer + CDC Serial |
|
||
| Toolchain | PlatformIO + Arduino Core |
|
||
|
||
## Setup und Loop
|
||
|
||
```text
|
||
Arduino setup()
|
||
delay(500)
|
||
ws2812_init()
|
||
rotes Startsignal für 1 s
|
||
CMainController::setup()
|
||
macro_config_load()
|
||
init_buttons() -> nvm_config_load()
|
||
Queue-Bridge setzen
|
||
usb_hid_init()
|
||
usb_serial_init()
|
||
matrix_init(cb)
|
||
encoder_init(cb)
|
||
|
||
Arduino loop()
|
||
matrix_scan()
|
||
poll_vendor()
|
||
processEvents()
|
||
check_factory_reset()
|
||
updateLEDs()
|
||
```
|
||
|
||
Die Reihenfolge ist absichtlich simpel:
|
||
|
||
- Eingaben einsammeln
|
||
- CDC-Kommandos vom Host verarbeiten
|
||
- Event-Queue leeren
|
||
- Sonderlogik fuer den Werksreset pruefen
|
||
- LED-Frame nur bei Bedarf rendern
|
||
|
||
Makros, Encoder-Taps, NVM-Schreiben sowie Start- und Reset-Feedback verwenden
|
||
blockierende Delays. Währenddessen werden Matrix und CDC nicht bearbeitet.
|
||
|
||
## Datenfluss
|
||
|
||
```text
|
||
matrix_scan / encoder ISR
|
||
-> EventQueue
|
||
-> processEvents()
|
||
-> execute_action_down / execute_action_up
|
||
-> usb_hid_*
|
||
-> usb_serial_send() fuer HOST_COMMAND
|
||
|
||
CDC Serial
|
||
-> poll_vendor()
|
||
-> Config/Makros einlesen oder dumpen
|
||
-> LED-Overrides setzen/loeschen
|
||
|
||
LED-Render
|
||
-> CButton.render_led()
|
||
-> ws2812_set()
|
||
-> ws2812_show() nur wenn dirty
|
||
```
|
||
|
||
## Zentrale Komponenten
|
||
|
||
| Datei | Aufgabe |
|
||
|---|---|
|
||
| `main.cpp` | startet den Controller |
|
||
| `CMainController.*` | Orchestrator fuer Inputs, Actions, Serial, LEDs |
|
||
| `CButton.*` | LED-Zustand, Animationen, Action-Referenz |
|
||
| `CEventQueue.*` | fester Ringbuffer mit 16 nutzbaren Slots |
|
||
| `config/nvm_config.*` | Config v3 laden, speichern, Defaults |
|
||
| `config/macro_config.*` | Makros laden, speichern |
|
||
| `hal/matrix.*` | 5x5-Matrixscan mit Debounce |
|
||
| `hal/encoder.*` | Encoder-ISR und Drehrichtung |
|
||
| `hal/usb_hid.*` | Keyboard- und Consumer-HID |
|
||
| `hal/usb_serial.*` | CDC-Paketpfad |
|
||
| `hal/ws2812.*` | WS2812-Treiber |
|
||
|
||
Die drei Fader sind nur im Variant und in `config/pins.h` definiert. Es gibt
|
||
aktuell keinen Fader-HAL und keine Verarbeitung im Controller.
|
||
|
||
## Key-ID-Schema
|
||
|
||
```text
|
||
0..3 = Encoder-SW
|
||
4 = unbenutzt
|
||
5..24 = MX-Buttons
|
||
```
|
||
|
||
Die beiden Werksreset-Tasten sind:
|
||
|
||
- `key_id 9` = unten links
|
||
- `key_id 24` = unten rechts
|
||
|
||
## Werksreset im Ablauf
|
||
|
||
Der Werksreset ist keine PC-Funktion, sondern Teil der Firmware:
|
||
|
||
- sobald beide Reset-Tasten gleichzeitig gehalten werden, werden ihre normalen Actions unterdrueckt
|
||
- falls bereits ein HID-Hold aktiv war, wird er sofort freigegeben
|
||
- nach 5 Sekunden gemeinsamer Haltezeit wird Default-Config + leere Makro-Tabelle in NVM geschrieben
|
||
- danach folgt ein kurzes rotes Feedback-Blinken
|
||
|
||
## Invarianten
|
||
|
||
- Projektcode vermeidet dynamische Allokation
|
||
- Integer-Arithmetik in zeitkritischen LED-/ISR-Pfaden
|
||
- `packed` fuer serielle und NVM-relevante Structs
|
||
- NVM-Schreibpuffer muessen 4-Byte-aligned sein
|
||
- `usb_serial_send()` sendet nur bei aktiver CDC-Verbindung
|
||
|
||
## Nebenläufigkeit
|
||
|
||
Encoder-Callbacks laufen im EIC-Interrupt, Matrixcallbacks im Loop. Beide
|
||
schreiben in dieselbe `CEventQueue`; `processEvents()` liest im Loop. Die Queue
|
||
besitzt aktuell keinen Interruptschutz für den Matrix-Push und verwirft Events
|
||
bei Überlauf. Das ist eine bekannte Einschränkung, keine garantierte
|
||
Multi-Producer-Sicherheit; siehe
|
||
[09_known_limitations.md](09_known_limitations.md).
|