Initial commit

This commit is contained in:
2026-03-29 14:47:13 +02:00
commit b49984b9c0
32 changed files with 2394 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# VersaMCU
Firmware für das VersaPad v2 Macro-Pad.
Läuft auf einem **ATSAMD21G17D** (Cortex-M0+), entwickelt mit PlatformIO + Arduino-Framework.
## Voraussetzungen
- [PlatformIO](https://platformio.org/) (CLI oder VS Code Extension)
- **Atmel-ICE** Debugger/Programmer (SWD-Verbindung zur PCB)
- [OpenOCD](https://openocd.org/) wird von PlatformIO automatisch installiert
## Flashen
```bash
pio run --target upload
```
Der Upload läuft via OpenOCD über SWD. Kein Bootloader nötig der Chip wird direkt programmiert.
## Projekt-Struktur
```
VersaMCU/
├── platformio.ini Build- und Upload-Konfiguration
├── upload_openocd.py Benutzerdefiniertes Upload-Script (OpenOCD)
├── boards/
│ ├── versapad.json Board-Definition mit Bootloader
│ └── versapad_nobl.json Board-Definition ohne Bootloader (aktiv)
├── variants/versapad/
│ ├── variant.h/.cpp Pin-Mapping für den SAMD21G17D
│ └── linker_scripts/ Linkerscript (kein Bootloader, NVM-Reservierung)
└── src/
├── main.cpp Arduino setup()/loop()
├── CMainController.h/.cpp Zentraler Orchestrator
├── CButton.h/.cpp Button-Modell: LED-Schichten, Action, Animationen
├── CEventQueue.h/.cpp Ring-Buffer FIFO (16 Slots, kein Heap)
├── SEvent.h Event-Typen (KEY_DOWN/UP, ENC_CW/CCW)
├── config/
│ ├── pins.h Pin-Nummern (Arduino-Nummern aus variant.h)
│ ├── action.h ActionType-Enum + SAction-Struct (packed)
│ └── nvm_config.h/.cpp NVM-Config: Laden, Speichern, CRC16, Defaults
└── hal/
├── matrix.h/.cpp 5×5-Matrix-Scan, 10 ms Debounce
├── encoder.h/.cpp Quadratur-Dekodierung via EIC-Interrupts
├── ws2812.h/.cpp WS2812-LED-Treiber (Adafruit NeoPixel, bit-bang)
├── usb_hid.h/.cpp HID Keyboard + Consumer Control
└── usb_serial.h/.cpp CDC Serial bidirektional, 8-Byte-Pakete
```
## Architektur
### Loop-Ablauf
```
loop()
├── matrix_scan() → matrix_cb() → CEventQueue.push()
│ (Encoder-ISRs laufen asynchron) → CEventQueue.push()
├── poll_vendor() → Serial-Pakete von VersaGUI verarbeiten
├── processEvents() → Queue leeren, Aktionen ausführen
└── updateLEDs() → Dirty-CButtons → WS2812-Buffer → show()
```
### CButton LED-Schichten
Jeder MX-Button hat zwei LED-Schichten:
- **base**: Konfigurierte Idle-Farbe (aus NVM)
- **override**: Temporär von VersaGUI gesetzt (Benachrichtigungen etc.)
Aktive Farbe = `override` wenn aktiv, sonst `base`. `clear_override()` kehrt sofort zu `base` zurück.
### LED-Animationen
| Animation | Verhalten |
|---|---|
| `STATIC` | Feste Farbe |
| `BLINK` | Binäres An/Aus |
| `PULSE` | Lineares Dreieck 0→255→0 |
| `FADE_IN / FADE_OUT` | Einmaliges Ein-/Ausblenden |
| `COLOR_CYCLE` | Hue-Sweep (Regenbogen), ignoriert base/override |
| `COLOR_FADE` | Crossfade zu Zielfarbe |
Alle Berechnungen in Integer-Arithmetik (kein FPU auf Cortex-M0+).
**Idle-Zustand:** Alle 20 MX-LEDs zeigen einen rotierenden Regenbogen (`COLOR_CYCLE`, 4 s/Runde, 40 % Helligkeit, gleichmäßig phasenverschoben).
### Serial-Protokoll (8 Bytes, fixed)
```
Byte 0: Command/Event-ID
Byte 1: key_id (Button 024 oder Encoder 03)
Byte 2: r / Daten-Byte A
Byte 3: g / Daten-Byte B
Byte 4: b
Byte 57: reserviert (0x00)
```
| ID | Richtung | Bedeutung |
|---|---|---|
| 0x01 | PC→Board | LED-Override setzen |
| 0x02 | PC→Board | LED-Override löschen |
| 0x03 | PC→Board | LED-Base setzen |
| 0x05 | PC→Board | Ping |
| 0x10 | PC→Board | Config-Begin (Chunks-Anzahl) |
| 0x11 | PC→Board | Config-Data (Chunk-Index + 6B Nutzdaten) |
| 0x12 | PC→Board | Config-Commit (CRC prüfen + NVM schreiben) |
| 0x13 | PC→Board | Config-Read (Board sendet NVM-Config zurück) |
| 0x81 | Board→PC | KEY_DOWN |
| 0x82 | Board→PC | KEY_UP |
| 0x83 | Board→PC | ENC_CW |
| 0x84 | Board→PC | ENC_CCW |
| 0x85 | Board→PC | Pong |
| 0x90 | Board→PC | Config-ACK |
| 0x91 | Board→PC | Config-NACK |
| 0x92 | Board→PC | Config-Begin (Dump-Start, Chunks-Anzahl) |
| 0x93 | Board→PC | Config-Data (Chunk-Index + 6B) |
| 0x94 | Board→PC | Config-End |
### NVM-Config-Layout (163 Bytes, packed)
```
Offset 0 4B Magic 0x56503202
Offset 4 1B Version 1
Offset 5 2B CRC16-CCITT (über Bytes 7162)
Offset 7 60B mx_actions[20] je 3B: type(1B) + data(2B)
Offset 67 36B enc_actions[4][3] je 3B
Offset103 20B led_r[20]
Offset123 20B led_g[20]
Offset143 20B led_b[20]
```
Gespeichert in den letzten 512 Bytes des Flashs (0x1FE00), via Linkerscript reserviert.
Bei ungültigem Magic/CRC werden Defaults geladen (alle Tasten: NONE, LEDs: warm-weiß).
## Bekannte Fallstricke
| Problem | Lösung |
|---|---|
| Kaltstart hängt (XOSC32KRDY) | `-DCRYSTALLESS` in build_flags pflicht |
| Adafruit NeoPixel ZeroDMA inkompatibel | Standard bit-bang Library verwenden |
| WS2812 Pegel 3.3V statt 5V | LED 0 marginal OK, ab LED 1 selbst-regenerierend. Fix nächste PCB-Rev: Level-Shifter |
| `ws2812_show()` blockiert ~600 µs | Dirty-Flag-Pattern: nur aufrufen wenn nötig, nie aus ISR |
| SAction muss `__attribute__((packed))` haben | Ohne packed: 4B statt 3B → CRC-Mismatch beim Config-Laden |
| Windows HID-Descriptor-Cache | Bei PID-Änderung Board neu einstecken |