VersaMCU/doc/00_architecture.md
2026-03-30 19:52:37 +02:00

3.7 KiB
Raw Blame History

VersaMCU Architektur-Übersicht

Ziel-Hardware

Merkmal Wert
MCU ATSAMD21G17D (Cortex-M0+, 48 MHz)
Flash 128 KB (davon 512 B am Ende für NVM-Config reserviert)
RAM 16 KB
FPU Keine alle Berechnungen in Integer-Arithmetik
USB Native USB, DFLL48M via USB-SOF-Kalibrierung (-DCRYSTALLESS)
Framework Arduino + PlatformIO, kein Bootloader (Direktflash via SWD/Atmel-ICE)

Loop-Ablauf

setup()
  ├── macro_config_load()    Makro-Tabelle aus NVM in RAM laden
  ├── init_buttons()         CButton-Objekte aus NVM initialisieren
  ├── usb_hid_init()         HID-Descriptor (No-Op, läuft via global ctor)
  ├── usb_serial_init()      CDC Serial öffnen
  ├── matrix_init(cb)        5×5-Matrix + Debounce-Zustand
  └── encoder_init(cb)       EIC-Interrupts für 4 Encoder

loop()  [~20 ms Iteration]
  ├── matrix_scan()          Debounce-Zustand prüfen → Events in Queue
  ├── poll_vendor()          CDC-Pakete vom PC verarbeiten (LED-Cmds, Config, Makros)
  ├── processEvents()        Queue leeren: Aktionen ausführen, HOST_COMMAND melden
  └── updateLEDs()           Dirty-CButtons → WS2812-Buffer → show() (nur wenn dirty)

Encoder-ISRs laufen asynchron (CHANGE-Interrupt auf A und B) und schreiben direkt in die Event-Queue. Die Queue ist interrupt-sicher (keine Locks nötig auf Single-Core-M0+).

Datenfluss

HAL-Callbacks (matrix_cb, encoder_cb)
  └─► CEventQueue (16 Slots, Ring-Buffer, kein Heap)
        └─► processEvents()
              ├─► CButton.on_press() / on_release()   [Hooks, aktuell leer]
              ├─► execute_action()  →  USB HID / Makro-Ablauf
              └─► usb_serial_send() →  HOST_COMMAND-Events an PC

SerialUSB (CDC, PC → Board)
  └─► poll_vendor()
        ├─► CButton.set_override() / clear_override() / set_base()
        └─► Config/Makro-Transfer (chunked, 6 B/Paket)

Komponenten-Übersicht

Datei Verantwortung
main.cpp setup() / loop() ruft nur CMainController auf
CMainController Zentraler Orchestrator, hält alle CButton-Instanzen
CButton LED-Layering, Animations-Engine, Action-Referenz
CEventQueue ISR-sicherer Ring-Buffer, 16 Events
hal/matrix 5×5-Matrix-Scan, 10 ms Debounce
hal/encoder Quadratur-Dekodierung via EIC-ISR
hal/ws2812 Thin Wrapper um Adafruit NeoPixel (bit-bang)
hal/usb_hid HID Keyboard + Consumer Control
hal/usb_serial CDC bidirektional, 8-Byte-Pakete, Ring-Buffer
config/nvm_config SDeviceConfig: laden, speichern, CRC16, Defaults
config/macro_config SMacroTable: laden, speichern (NVM Row 1)
config/action SAction-Struct + ActionType-Enum

Key-ID-Schema

key_id 03   : Encoder-SW-Buttons (COL_0 × ROW_03), kein LED
key_id 4     : nicht belegt (COL_0 × ROW_4)
key_id 524  : MX-Buttons (COL_14 × ROW_04), je ein WS2812-LED

LED-Index folgt serpentiner Verdrahtung: LED_INDEX(col, row).

Invarianten / Constraints

  • Kein Heap: kein new/malloc alle Objekte statisch oder als Felder in CMainController.
  • Kein Float: Cortex-M0+ hat keine FPU; Float würde per Software emuliert (~1020× langsamer).
  • Packed Structs: SAction und SDeviceConfig sind __attribute__((packed)) damit die Byte-Größen mit der C#-Serialisierung in VersaGUI übereinstimmen.
  • Aligned NVM-Writes: nvm_write_page castet Pointer zu uint32_t*; Puffer müssen vor dem Aufruf in __attribute__((aligned(4)))-Variablen kopiert werden (sonst HardFault auf M0+).
  • DTR-Check: usb_serial_send() prüft ob SerialUSB aktiv ist, bevor Bytes gesendet werden.