3.7 KiB
3.7 KiB
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 0–3 : Encoder-SW-Buttons (COL_0 × ROW_0–3), kein LED
key_id 4 : nicht belegt (COL_0 × ROW_4)
key_id 5–24 : MX-Buttons (COL_1–4 × ROW_0–4), 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 (~10–20× langsamer).
- Packed Structs:
SActionundSDeviceConfigsind__attribute__((packed))damit die Byte-Größen mit der C#-Serialisierung in VersaGUI übereinstimmen. - Aligned NVM-Writes:
nvm_write_pagecastet Pointer zuuint32_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.