# 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).