pio run -e versapad --target upload writes the app starting at 0x0000 and silently destroyed the installed UF2 bootloader twice today during testing -- no warning, no error, just a board that stopped responding to the boot-key hold. upload_openocd.py now checks for the bootloader (verify_image against the locally built bootloader/.pio/build/versapad_bootloader/firmware.bin) before an env:versapad upload and refuses if one is present, pointing at env:versapad_usb instead. Fails closed: an inconclusive check (e.g. bootloader not built locally, SWD not responding) blocks rather than proceeding on a guess -- confirmed necessary the hard way, since a "fail open" first attempt let the destructive upload through silently. Scoped to PIOENV == "versapad" only, since bootloader/platformio.ini's own upload reuses this same script and must always be allowed to write 0x0000. A new erase-bootloader-and-flash custom target remains as the explicit, deliberate override. Documented the workflow (bootloader is its own PlatformIO project, flashed once via SWD; versapad_usb is the normal path afterward; versapad's upload is now guarded) in README.md and doc/10_usb_bootloader.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| boards | ||
| bootloader | ||
| doc | ||
| src | ||
| variants/versapad | ||
| .gitignore | ||
| AGENTS.md | ||
| platformio.ini | ||
| README.md | ||
| uf2conv.py | ||
| upload_openocd.py | ||
| upload_uf2.py | ||
VersaMCU
Firmware für das VersaPad-v2-Makropad. Das Projekt läuft auf einem ATSAMD21G17D mit PlatformIO und dem Arduino-SAMD-Framework.
Aktueller Funktionsumfang
| Bereich | Stand |
|---|---|
| Eingaben | 20 MX-Tasten, 4 Encoder-Taster und 4 Quadratur-Encoder |
| USB | Keyboard-HID, Consumer-HID und CDC Serial |
| Aktionen | HID-Key, Consumer-Key, Host-Event, Makro, Profilwechsel |
| Makros | 32 Slots mit je bis zu 8 HID-Schritten |
| Profile | 3 Profile in Config v3 |
| LEDs | 20 WS2812B mit Base-/Override-Farbe und 7 Animationsmodi |
| Persistenz | Config und Makros im internen Flash |
| Recovery | Werksreset über zwei Tasten |
| Firmware-Update | SWD (Atmel-ICE) oder USB (UF2-Bootloader, bootloader/) |
Die drei Fader-Pins sind im Board-Variant definiert, werden von der aktuellen Firmware aber noch nicht eingelesen.
Hardware
| Eigenschaft | Detail |
|---|---|
| MCU | ATSAMD21G17D, Cortex-M0+, 48 MHz |
| Flash / RAM | 128 KiB / 16 KiB |
| Matrix | logisch 5×5: 20 MX, 4 Encoder-SW, 1 unbelegt |
| Encoder | 4× Quadratur über EIC-Interrupts |
| LEDs | 20× WS2812B an PB22 |
| USB | Native USB als HID + CDC Composite Device |
| Programmer | Atmel-ICE/CMSIS-DAP über SWD, oder USB über den UF2-Bootloader |
Schnellstart
Voraussetzungen sind PlatformIO Core oder die PlatformIO IDE sowie für den Upload ein angeschlossener Atmel-ICE beziehungsweise kompatibler CMSIS-DAP-Adapter.
pio run -e versapad
pio run -e versapad --target upload
Das Standard-Environment versapad baut für boards/versapad_nobl.json. Der
Upload wird durch upload_openocd.py über das von PlatformIO installierte
OpenOCD ausgeführt.
Alternativ lässt sich die App-Firmware nach einem einmaligen SWD-Bootloader-Flash auch über USB aktualisieren, ganz ohne Atmel-ICE:
cd bootloader && pio run -e versapad_bootloader --target upload # einmalig
pio run -e versapad_usb --target upload # danach jedes App-Update
bootloader/ ist ein eigenständiges PlatformIO-Projekt (eigene
platformio.ini, kein Arduino-Framework). Für die PlatformIO-IDE-Buttons in
VS Code müsste der Ordner separat als eigener Workspace geöffnet werden; über
die Kommandozeile reicht cd bootloader && pio run ... im selben Fenster.
Sobald der Bootloader installiert ist, verweigert versapad --target upload den normalen Upload — dieser Weg schreibt ab 0x0000 und würde den
Bootloader sonst kommentarlos überschreiben (upload_openocd.py prüft das
vorher automatisch). Bauen und Debuggen über versapad bleiben uneingeschränkt
möglich, nur der Upload ist betroffen. Für App-Updates danach versapad_usb
verwenden; bewusst zurück zu reinem SWD-Betrieb geht über
pio run -e versapad -t erase-bootloader-and-flash.
Details, Speicherlayout und die Bootloader-Aktivierung (kein physischer Reset-Taster auf dieser Platine) stehen in 10_usb_bootloader.md.
Laufzeitmodell
main.cpp besitzt genau einen CMainController. Nach einem roten Startsignal
initialisiert er NVM, USB, Matrix und Encoder. Die Hauptschleife ist:
matrix_scan()
poll_vendor()
processEvents()
check_factory_reset()
updateLEDs()
- Matrix und Encoder legen
SEvents in eine feste Queue. - Der Controller setzt Events in HID-Aktionen, Makros, Host-Events oder Profilwechsel um.
poll_vendor()verarbeitet feste 8-Byte-Pakete über CDC.- LEDs werden nur neu übertragen, wenn ein Zustand dirty ist oder eine Animation läuft.
- Makros, Encoder-Taps, NVM-Zugriffe und visuelles Reset-Feedback blockieren den Loop kurzzeitig; es gibt keinen Scheduler.
Wichtige Datenverträge
Config v3
- Magic
0x56503203 SDeviceConfig: 740 Byte- CRC16-CCITT über Bytes
7..739 - 3 Profile
- globale und LED-spezifische Helligkeit
- 124 Chunks mit je 6 Nutzbytes beim CDC-Transfer
Makros
SMacroTable: 512 Byte- 32 Slots × 8 Schritte × 2 Byte
- 86 Chunks mit je 6 Nutzbytes beim CDC-Transfer
Flashzugriffe
| Bereich | Adresse | Größe |
|---|---|---|
| Makros | 0x1FB00..0x1FCFF |
512 B |
| Config | 0x1FD00..0x1FFFF |
768 B, davon 740 B genutzt |
Das aktive Linker-Skript und boards/versapad_nobl.json begrenzen das
Firmware-Image auf 0x00000..0x1FAFF. Damit sind alle fünf NVM-Rows gegen
Firmwarewachstum geschützt.
Werksreset
Unteren linken und unteren rechten MX-Button gleichzeitig fünf Sekunden halten:
- die Tasten werden während des Haltens rot markiert,
- ihre normalen Aktionen werden unterdrückt,
- bei Erfolg blinken alle LEDs kurz rot,
- Config und Makrotabelle werden auf Defaults zurückgesetzt.
Ein normaler SWD-Reflash löscht diese NVM-Daten nicht automatisch.
Projektstruktur
VersaMCU/
|-- AGENTS.md # Kontext und Richtlinien für Coding-LLMs
|-- README.md
|-- platformio.ini
|-- upload_openocd.py # SWD-Upload-Hook (env:versapad)
|-- uf2conv.py # .bin -> .uf2 Konverter (App-Firmware)
|-- upload_uf2.py # USB-Upload-Hook (env:versapad_usb)
|-- boards/ # PlatformIO-Boarddefinitionen
|-- variants/versapad/ # Pinmapping und Linker-Skripte
|-- doc/ # Architektur- und Protokolldokumentation
|-- bootloader/ # UF2-Bootloader (eigenes PlatformIO-Projekt)
`-- src/
|-- main.cpp
|-- CMainController.* # Orchestrierung
|-- CButton.* # Actions und LED-Zustand
|-- CEventQueue.* # feste Event-Queue
|-- config/ # Binärformate, NVM und Pins
`-- hal/ # Matrix, Encoder, HID, CDC, WS2812
Einstieg für Entwickler und LLMs
Für einen neuen Kollegen:
- Entwicklung und Einstieg
- Architektur
- die zum Task passende Fachdokumentation im Dokumentationsindex
- bekannte Einschränkungen
Für einen Coding-Agent zusätzlich AGENTS.md als
Repository-Anweisung mitgeben. Die Datei enthält Quellenhierarchie,
Binärverträge, Änderungsregeln und die minimale Verifikation.