Added mcu recommendation and merged project-context content from earlier

This commit is contained in:
Julian Appel 2026-03-30 19:53:34 +02:00
parent 9079fefad8
commit d6ed7cb81f

224
README.md
View File

@ -3,6 +3,44 @@
Firmware für das VersaPad v2 Macro-Pad.
Läuft auf einem **ATSAMD21G17D** (Cortex-M0+), entwickelt mit PlatformIO + Arduino-Framework.
## Hardware
| Eigenschaft | Detail |
|---|---|
| MCU | ATSAMD21**G17D** (Cortex-M0+, 128 KB Flash, 16 KB RAM) |
| Taktfrequenz | 48 MHz (DFLL, intern kein externer 32 kHz-Quarz) |
| Framework | Arduino SAMD Core 1.8.14, PlatformIO |
| Programmer | Atmel-ICE via SWD (kein Bootloader) |
### Button-Matrix
- **5×5-Matrix** (COL_04 × ROW_04) = 25 logische Keys
- **20 Cherry MX Switches** (COL_14 × ROW_04), je eine WS2812-LED
- **4 Encoder-SW-Buttons** (COL_0 × ROW_03), keine LEDs
- COL_0 × ROW_4 = nicht belegt
- **Dioden**: D4148 je Taste, Anode an Switch, Kathode an ROW → Ghost-Key-freies Scannen
- **Scan-Richtung**: ROW wird OUTPUT LOW getrieben, COL-Leitungen haben 10 kΩ-Pullup nach 3V3 (werden gelesen)
### Rotary Encoder
- 4× Encoder (ENC0 = USB-nah, ENC3 = USB-fern)
- Alle A/B-Leitungen auf EIC (PA16PA23), Quadratur-Dekodierung via ISR + 4-State-Lookup
- Jeder Encoder hat drei Aktionen: **CW**, **CCW**, **SW** (alle aus NVM konfigurierbar)
### LEDs (WS2812)
- 20× WS2812B, serpentinen-verdrahtet (Reihe 0: L→R, Reihe 1: R→L, …)
- Datenleitung: **PB22 (D18)**, bit-bang via Adafruit NeoPixel Library
- **LED_INDEX-Formel**: `row * 4 + ((row & 1) ? (4 - col) : (col - 1))`
- **Pegelproblematik**: WS2812 sind 5V-Devices (HIGH-Schwelle ~3,5 V), SAMD21 liefert 3,3 V ohne Level-Shifter. LED 0 empfängt einen marginal gültigen Pegel; ab LED 1 regeneriert jede LED intern auf 5 V → alle weiteren problemlos. Fix nächste PCB-Revision: Level-Shifter oder Diode in der VDD-Leitung.
### Fader (ADC)
- 3× Linearpotentiometer: PA02 (A0), PA03 (A1, auch VREFA), PB08 (A2)
- Noch nicht implementiert (siehe Roadmap)
---
## Voraussetzungen
- [PlatformIO](https://platformio.org/) (CLI oder VS Code Extension)
@ -151,6 +189,51 @@ VersaMCU/
## Architektur
### Schichten
```
┌────────────────────────────────────────────────┐
│ main.cpp │
│ CMainController (setup / work) │
├────────────────────────────────────────────────┤
│ Modell │
│ CButton (LED 2-Layer, Action, dirty-Flag) │
│ CEventQueue (Ring-Buffer FIFO, 16 Slots) │
├────────────────────────────────────────────────┤
│ HAL │
│ hal/matrix 5×5-Scan, 10 ms Debounce │
│ hal/encoder Quadratur via EIC-ISR │
│ hal/ws2812 Adafruit NeoPixel bit-bang │
│ hal/usb_hid HID Keyboard + Consumer │
│ hal/usb_serial CDC Serial bidirektional │
├────────────────────────────────────────────────┤
│ Config │
│ config/pins.h Pin-Mapping │
│ config/action.h ActionType + SAction │
│ config/nvm_config Flash R/W, CRC16 │
└────────────────────────────────────────────────┘
```
### Datenfluss
```
HAL (matrix_scan, Encoder-ISR)
└─► matrix_cb / encoder_cb
└─► CEventQueue.push()
└─► CMainController.processEvents()
├─► CButton.on_press() / on_release()
├─► execute_action() → usb_hid_send_key / send_consumer
└─► usb_serial_send() (nur bei HOST_COMMAND)
SerialUSB (PC → Board, 8-Byte-Pakete)
└─► CMainController.poll_vendor()
└─► CButton.set_override() / set_base() / clear_override()
CMainController.updateLEDs()
└─► CButton.render_led() → ws2812_set()
└─► ws2812_show() (nur wenn dirty)
```
### Loop-Ablauf
```
@ -172,16 +255,37 @@ Aktive Farbe = `override` wenn aktiv, sonst `base`. `clear_override()` kehrt sof
### 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 |
Animationen modulieren die Helligkeit oder Farbe der aktiven Schicht (base oder override). Alle Berechnungen in **Integer-Arithmetik** (Cortex-M0+ hat keine FPU).
Alle Berechnungen in Integer-Arithmetik (kein FPU auf Cortex-M0+).
| Animation | Typ | Verhalten | `period_ms`-Semantik |
|---|---|---|---|
| `STATIC` | | Feste Helligkeit (Standardzustand) | |
| `BLINK` | Helligkeit | Binäres An/Aus, endlos | Halbperiode (An = Aus) |
| `PULSE` | Helligkeit | Lineares Dreieck 0→255→0, endlos | Vollperiode |
| `FADE_IN` | Helligkeit | Einmalig: schwarz → voll, dann STATIC | Dauer |
| `FADE_OUT` | Helligkeit | Einmalig: voll → schwarz, dann STATIC + base=schwarz | Dauer |
| `COLOR_CYCLE` | Farbe | Hue-Sweep Regenbogen, endlos, ignoriert base/override | Eine volle Runde |
| `COLOR_FADE` | Farbe | Einmalig: Crossfade akt. Farbe → Zielfarbe, dann STATIC + base=Ziel | Dauer |
**API:**
```cpp
set_anim(LEDAnim, period_ms, phase_offset_ms = 0)
// Für alle Typen außer COLOR_FADE.
// phase_offset_ms verschiebt den Startpunkt in die Vergangenheit →
// mehrere LEDs versetzt starten (z. B. Regenbogen-Welle).
set_color_fade(RGB to, period_ms)
// Startet COLOR_FADE von aktueller Farbe zu `to`.
clear_anim()
// Sofort zurück zu STATIC (volle Helligkeit).
render_led()
// Gibt true zurück solange dirty oder Animation läuft → ws2812_show() nötig.
```
**Idle-Zustand:** Alle 20 MX-LEDs laufen mit `COLOR_CYCLE` (4 s/Runde, 40 % Helligkeit). Der Phasenversatz ist gleichmäßig über alle 20 LEDs verteilt, sodass immer ein vollständiger Regenbogen auf dem Pad liegt.
**Warum Bit-Bang statt DMA?**
@ -271,3 +375,105 @@ Via Linkerscript reserviert. Bei ungültigem Magic/Version/CRC werden Defaults g
| `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 |
| `SERCOM5 CTRLB.RXEN` Sync-Bug | `while(SYNCBUSY.bit.CTRLB)` hängt vor ENABLE → nur relevant bei manueller SERCOM5-Konfiguration; nicht im Normalbetrieb |
| `PluggableUSBModule` nicht nutzbar | `USB_SendControl` / `USB_RecvControl` nicht verlinkt in dieser Core-Version → CDC Serial statt Vendor HID für PC-Kommunikation verwenden |
| HID-Descriptor vor USB-Enumeration registrieren | Registrierung via globalem Konstruktor (läuft vor `main()`), nicht in `setup()` |
---
## Next-Generation Hardware MCU-Empfehlung
### Warum der SAMD21G17D an seine Grenzen stößt
| Einschränkung | Auswirkung auf geplante Features |
|---|---|
| **128 KB Flash** | Mehrere Profile + lange Makros + OLED-Fonts füllen den Speicher vollständig |
| **16 KB RAM** | OLED-Framebuffer (128×64 px = 1 KB) + Profil-Puffer + Makro-Tabellen + Stack = kaum Luft |
| **Kein FPU** | LED-Animationen erfordern Integer-Arithmetik-Workarounds; aufwändigere Effekte unwirtschaftlich |
| **Kein Ethernet-MAC** | Ethernet nur via langsamen SPI-Chip möglich |
| **Kein USB High-Speed** | CDC bleibt auf 12 Mbit/s (Full Speed); für schnelle Konfigurationsübertragungen ausreichend, aber kein Spielraum |
| **WS2812 bit-bang blockiert 600 µs** | Mit mehr LEDs oder höherer Auflösung kritisch; kein DMA ohne SERCOM-Umbau |
---
### Empfehlung: Microchip SAME54P20A
**Primäre Empfehlung** selber Hersteller, gleicher Toolchain-Stack, deutlich mehr Reserven.
| Merkmal | SAMD21G17D (aktuell) | SAME54P20A (empfohlen) |
|---|---|---|
| Kern | Cortex-M0+, 48 MHz | Cortex-M4F, 120 MHz |
| **FPU** | ✗ | ✓ (single-precision) |
| **Flash** | 128 KB | **1 MB** |
| **RAM** | 16 KB | **256 KB** |
| **USB** | Full Speed (12 Mbit/s) | Full Speed + optionaler HS-PHY |
| **Ethernet MAC** | ✗ | ✓ (IEEE 802.3, braucht ext. PHY) |
| **DMA** | 12 Kanäle | 32 Kanäle |
| SERCOM | 6 | 8 |
| NVM (intern) | 128 KB | 1 MB (kein ext. Flash nötig) |
| Preis (LCSC ca.) | ~2 € | ~810 € |
**Warum SAME54?**
- Direkter Upgrade-Pfad: Arduino-Framework, PlatformIO, gleiche HAL-Konzepte
- Ethernet-MAC integriert → nur externer PHY nötig (z.B. **KSZ8081** oder **LAN8720A**, ~12 €)
- 1 MB Flash reicht für viele Profile, lange Makros und OLED-Font-Tabellen ohne externen Flash
- 256 KB RAM: OLED-Framebuffer, Profil-Puffer und komplexe Makro-Engines kein Problem
- FPU: sauberere LED-Animationen, kein Integer-Workaround mehr nötig
- DMA: WS2812 via SERCOM-SPI + DMA möglich → kein Bit-Bang, keine Interrupt-Sperre
---
### Alternative: Raspberry Pi RP2350
Falls Ethernet nicht zwingend auf dem MCU selbst integriert sein muss (z.B. W5500 via SPI):
| Merkmal | RP2350 |
|---|---|
| Kern | Dual Cortex-M33 oder RISC-V, 150 MHz |
| FPU | ✓ |
| RAM | **520 KB** SRAM |
| Flash | Kein interner; ext. QSPI (typ. 216 MB) |
| USB | Full Speed (Device + Host) |
| Ethernet MAC | ✗ (W5500 via SPI, ~3 €) |
| PIO | 3 × 4 PIO-Blöcke → WS2812 hardwareseitig ohne CPU |
| Preis | ~1,50 € |
**Vorteil:** PIO-Blöcke übernehmen WS2812-Timing hardwareseitig (kein bit-bang, kein DMA-Setup). Sehr viel RAM für komplexe Logik. Günstiger als SAME54.
**Nachteil:** Kein integrierter Ethernet-MAC. W5500 übernimmt TCP/IP-Stack per SPI (ausreichend für einfaches HTTP/Telnet-Protokoll), ist aber kein vollwertiger Network-Stack.
---
### System-Architektur für PoE-Betrieb
PoE erfordert unabhängig vom MCU zusätzliche Hardware:
```
RJ45-Buchse (mit integrierten Magnetics)
└─► PoE-PD-Controller (z.B. TPS2372-4, AG9800)
├─► DC/DC-Wandler → 3.3V / 5V Versorgung des Boards
└─► Ethernet-Signal → MCU-Ethernet-MAC → ext. PHY (LAN8720A / KSZ8081)
```
Der PoE-PD-Controller ist zwingend: Er verhandelt mit dem PoE-Switch (IEEE 802.3af/at), isoliert galvanisch und liefert geregelte Spannung. Typische PoE-Leistungsklasse 0 (15,4 W) reicht für MCU + LEDs + Display mehrfach aus.
**Empfohlene ICs:**
| Funktion | IC | Preis ca. |
|---|---|---|
| PoE PD Controller | AG9800 oder TPS2372-4 | 13 € |
| Ethernet PHY | LAN8720A oder KSZ8081 | 12 € |
| QSPI-Flash (falls RP2350) | W25Q128 (16 MB) | ~0,80 € |
| OLED Controller | SSD1306 (128×64, I2C/SPI) | im Modul enthalten |
---
### Empfehlung nach Szenario
| Szenario | Empfehlung |
|---|---|
| Voller Feature-Umfang (Ethernet-MAC, PoE, Profile, OLED) | **SAME54P20A** + LAN8720A + PoE-PD |
| Maximale RAM/Flash-Reserve, WS2812 ohne CPU-Last | **RP2350** + W5500 + PoE-PD |
| Minimaler Footprint, kein Ethernet | **RP2040** (günstiger als RP2350, 264 KB RAM reicht für OLED + Profile) |
Für VersaPad v3 mit allen genannten Features ist der **SAME54P20A** die solideste Wahl: gleicher Hersteller, etablierter Toolchain, integrierter Ethernet-MAC, und der Firmware-Code von v2 (HAL-Struktur, Event-Queue, CButton-Modell) ist weitgehend übertragbar.