Adds uf2conv.py (minimal, dependency-free .bin -> .uf2 converter matching bootloader/inc/uf2format.h's block layout) and upload_uf2.py, a PlatformIO upload hook for env:versapad_usb that finds the mounted VERSABOOT volume and copies the converted firmware onto it. env:versapad_usb previously used upload_protocol=sam-ba, the classic Arduino/Atmel protocol -- the actual bootloader speaks UF2/mass storage, not SAM-BA, so that upload path never worked. Switched to upload_protocol=custom with the new hook, and cleaned the now-unused SAM-BA-specific fields out of boards/versapad.json. Verified end to end on real hardware: pio run -e versapad_usb --target upload builds, converts, copies to the VERSABOOT drive, and the bootloader jumps into the freshly written app on its own. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| boards | ||
| inc | ||
| include | ||
| lib | ||
| scripts | ||
| src | ||
| LICENSE-uf2-samdx1 | ||
| platformio.ini | ||
| README.md | ||
| TESTING.md | ||
VersaMCU UF2-Bootloader
USB-Bootloader für das VersaPad-v2-Makropad. Wird per Atmel-ICE/SWD einmalig
auf den ATSAMD21G17D geflasht und belegt 0x0000..0x1FFF (8 KiB). Danach
lässt sich die App-Firmware ohne SWD über USB aktualisieren: Bootloader-Modus
aktivieren, Board erscheint als USB-Laufwerk VERSABOOT, .uf2-Datei drauf
kopieren.
Der komplette Weg — Bootloader-Einstieg, .uf2-Erzeugung, Kopieren aufs
Laufwerk, automatischer Rücksprung in die neu geschriebene App — ist auf
echter Hardware verifiziert, siehe Hardwaretest.
Diese Platine hat keinen dedizierten Reset-/Boot-Taster. Bootloader-Modus aktivieren heißt hier: unterste rechte Cherry-MX-Taste (key_id 24) beim Einstecken/Reset gedrückt halten. Siehe Hardware-Bootloader-Einstieg.
Herkunft
Abgeleitet und stark eingekürzt aus
microsoft/uf2-samdx1 (MIT-Lizenz,
siehe LICENSE-uf2-samdx1). Übernommen wurden nur die
SAMD21-Bootloader-Sourcen (src/, inc/), die zugehörigen CMSIS-/ASF-Header
für genau den SAMD21G17A-Chip (lib/) sowie das Linkerskript, angepasst auf
den 128-KiB-Flash/16-KiB-RAM-Chip auf diesem Board (Original-Vorlagen zielen
auf 256-KiB-Chips wie den SAMD21G18A). Board-Vorlage war boards/gemma_m0
aus dem Original-Repo (crystalless, gleiche USB_VID wie unser Board).
Nicht übernommen: SAMD51/SAME54-Support, der Bootloader-Selbstupdate-Pfad
(selfmain.c, sketch.cpp, das python2-Tooling uf2conv.py/gendata.py)
und das Node-/Makefile-basierte Build-System — stattdessen ein eigenständiges
PlatformIO-Environment, damit dasselbe Tooling wie für die App-Firmware
ausreicht.
Für die App-Seite (nicht diesen Bootloader-Build) gibt es unter
../uf2conv.py eine eigene, minimale Python-3-Neuimplemen-
tierung des .bin→.uf2-Konverters (kein Upstream-Code, passendes
Blockformat zu inc/uf2format.h), eingebunden über
../upload_uf2.py als env:versapad_usb-Upload-Hook.
Build
cd bootloader
pio run -e versapad_bootloader
Flash (Atmel-ICE/SWD, einmalig, überschreibt ggf. vorhandene App-Firmware nicht,
da nur 0x0000..0x1FFF beschrieben wird)
pio run -e versapad_bootloader --target upload
Hardware-Bootloader-Einstieg ohne Reset-Taster
Die Platine hat keinen dedizierten Reset-/Boot-Taster (Custom-PCB-Design).
Der klassische UF2-"Doppel-Tap-Reset" braucht aber gar keinen physischen Pin —
check_start_application() in src/main.c prüft ein RAM-Flag
(DBL_TAP_PTR/DBL_TAP_MAGIC), das bei jedem Reset gesetzt wird, unabhängig
von der Reset-Quelle.
Statt eines eigenen Tasters wird die unterste rechte Cherry-MX-Taste
(key_id 24 in der App-Firmware, siehe ../src/config/pins.h) missbraucht:
key_id 24liegt auf COL4 (PA08) × ROW4 (PA15)boot_key_pressed()insrc/main.ctreibt ROW4 kurz auf LOW und liest COL4 zurück — kein voller Matrixscan nötig, nur ein Drei-Pin-Check- der Check läuft ganz am Anfang von
check_start_application(), noch vor der App-Adress-Validierung und vor der RCAUSE-/DBL-TAP-Logik: funktioniert also auch bei kaputter/gelöschter App-Firmware und bei normalem Power-On (Stecker ziehen/reinstecken), kein Software-Trigger in der App nötig - Pin-Konstanten stehen in
include/board_config.h(BOOT_KEY_ROW_PIN/BOOT_KEY_COL_PIN)
Bedienung: USB-Kabel ziehen, unterste rechte Taste gedrückt halten, Kabel
wieder einstecken (Taste dabei weiter halten) → Board bootet direkt in
VERSABOOT. Ohne gehaltene Taste startet die App normal.
App-Firmware per USB flashen (nach dem einmaligen Bootloader-Flash)
# Board zuerst in den Bootloader-Modus versetzen: USB ziehen, unterste
# rechte Taste halten, wieder einstecken (siehe oben)
pio run -e versapad_usb --target upload
Baut die App-Firmware (Repo-Root, nicht bootloader/), erzeugt firmware.uf2
und kopiert es aufs VERSABOOT-Laufwerk. Kein Atmel-ICE mehr nötig.
Hardwaretest (2026-08-05)
Erster vollständiger Hardwaretest auf einem echten VersaPad-v2-Board über Atmel-ICE/SWD. Ergebnisse:
- Bootloader-Build/-Flash/-Verify laufen sauber (89,9 % Flash, 7364/8192 Byte).
- USB-Enumeration und
VERSABOOT-Massenspeicher-Modus funktionieren nach dem PID-Fix (siehe unten) korrekt, inklusive korrektemINFO_UF2.TXT. - Der Tastencheck (oben) funktioniert wie vorgesehen: gehaltene Taste beim
Boot →
VERSABOOT, sonst normaler App-Start. - Kritischer Bug gefunden und behoben: Der Sprung vom Bootloader in die
App (
__set_MSP→SCB->VTOR→bx) führte bei jedem echten, eigenständigen Boot (auch bei komplett getrenntem Debugger) zu einem Hard-Fault/Lockup der CPU. Identische Register-/VTOR-Werte, vom Debugger bei angehaltener CPU direkt injiziert, liefen dagegen einwandfrei — das grenzte den Fehler auf die Ausführung der Sprungsequenz selbst ein, nicht auf falsche Werte. Fix:__DSB(); __ISB();zwischen demSCB->VTOR-Schreib- zugriff und dembxincheck_start_application()(src/main.c) — von ARM für genau dieses Bootloader-Pattern vorgeschrieben, hat im vendorten Code gefehlt. Nach dem Fix bootet die App-Firmware zuverlässig, mit und ohne angeschlossenen Debugger. - Kompletter USB-Flashweg getestet:
pio run -e versapad_usb --target upload(App-Firmware, Repo-Root) bautfirmware.bin, wandelt es über../uf2conv.pyinfirmware.uf2und kopiert es über../upload_uf2.pyautomatisch aufs erkannteVERSABOOT-Laufwerk. Der Bootloader erkennt den Schreibzugriff und springt danach selbständig in die neue App — kein manuelles Auswerfen oder Reset nötig. Voraussetzung: Board zuvor per gehaltener Taste (siehe oben) in den Bootloader-Modus versetzt.
Bekannte Einschränkungen
- Flash-Auslastung ~90 % (7364 von 8192 Byte). Wenig Puffer für Änderungen.
- Kein Status-LED-Feedback im Bootloader-Modus, da die Platine nur eine WS2812-Kette (kein einfaches GPIO-LED oder DotStar) hat und der Original-Code dafür nicht ausgelegt ist.
- USB_PID war ursprünglich
0x0011(Adafruit Gemma M0s eigene Bootloader-PID, unverändert aus der Vorlage übernommen) — kollidierte auf Rechnern mit installiertem Adafruit-Treiber, wurde alsAdafruit Circuit Playground-COM-Port statt als Massenspeicher gebunden. Verifiziert am 2026-08-05, seither0x0043. Bleibt ein Wert ohne echte Registrierung (kein offiziell zugeteilter PID unter Adafruits VID); ein sauber eigener VID (z. B. über pid.codes) wäre die langfristig korrekte Lösung, ist aber nicht Teil dieses Branches. ../upload_uf2.pysucht dasVERSABOOT-Laufwerk aktuell nur über die Windows-API (GetVolumeInformationW) — passend zur bisherigen Dev-Umgebung dieses Projekts, aber nicht plattformübergreifend. Für macOS/Linux müsste die Laufwerkssuche noch ergänzt werden.