Adds doc/10_usb_bootloader.md as the primary reference for the UF2 bootloader (memory layout, key-based boot entry, build/flash steps, hardware test findings, known limitations), following the existing numbered-doc convention. Updates doc/INDEX.md, doc/09_known_limitations.md (bootloader is no longer "not supported", just scoped), and doc/08_development.md accordingly. Removes bootloader/TESTING.md (its findings now live in doc/10_usb_bootloader.md) and trims bootloader/README.md down to what belongs with that subproject specifically: upstream attribution/license and local build/flash commands, plus the OpenOCD manual-flashing warning since that's implementation-specific detail that would clutter the higher-level doc. Updates the top-level README.md (feature table, hardware table, quickstart, project tree, doc links) to reflect USB flashing as a supported path alongside SWD. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.6 KiB
USB-Bootloader (UF2)
Zweiter Firmware-Update-Weg neben SWD: ein UF2-Bootloader unter
bootloader/ erlaubt, die App-Firmware über USB zu aktualisieren, ohne
Atmel-ICE. Der Bootloader selbst wird einmalig per SWD geflasht; danach
reicht für App-Updates ein PlatformIO-Befehl.
Speicherlayout
| Bereich | Adresse | Größe | Environment |
|---|---|---|---|
| Bootloader | 0x00000..0x01FFF |
8 KiB | versapad_bootloader (in bootloader/) |
| App-Firmware | 0x02000..0x1FAFF |
118,75 KiB | versapad_usb |
| NVM (Makros + Config) | 0x1FB00..0x1FFFF |
1,25 KiB | — |
Gleiches NVM-Layout wie beim SWD-Ziel versapad_nobl
(siehe 06_nvm_config.md); nur die Firmware-Obergrenze
sinkt um die 8 KiB Bootloader. Linkerskript:
variants/versapad/linker_scripts/gcc/flash_with_bootloader.ld, Boarddatei:
boards/versapad.json.
Bootloader-Einstieg ohne Reset-Taster
Die Platine hat keinen dedizierten Reset-/Boot-Taster. Statt eines
physischen Pins wird die unterste rechte Cherry-MX-Taste (key_id 24,
COL4 × ROW4, siehe 01_matrix.md) beim Boot geprüft:
check_start_application() in bootloader/src/main.c treibt ROW4 kurz auf
LOW und liest COL4 zurück — noch vor der App-Adress-Validierung, funktioniert
also auch bei kaputter oder fehlender App-Firmware.
Bedienung: USB-Kabel ziehen, Taste gedrückt halten, wieder einstecken (Taste
weiter halten) → Board bootet in den Massenspeicher-Modus (VERSABOOT).
Ohne gehaltene Taste startet die App normal.
Bauen und Flashen
Bootloader einmalig per SWD (überschreibt nur 0x0000..0x1FFF):
cd bootloader
pio run -e versapad_bootloader --target upload
Danach App-Firmware per USB (Board vorher wie oben in den Bootloader-Modus versetzen):
pio run -e versapad_usb --target upload
Baut die App, wandelt firmware.bin über uf2conv.py (Repo-Root, eigene
minimale Implementierung, kein Upstream-Code) in firmware.uf2 und kopiert
es über upload_uf2.py aufs erkannte VERSABOOT-Laufwerk. Der Bootloader
erkennt den Schreibzugriff und springt selbständig in die neue App.
upload_uf2.py sucht das Laufwerk aktuell nur über die Windows-API
(GetVolumeInformationW) — keine macOS/Linux-Unterstützung.
Herkunft und Details
Der Bootloader ist abgeleitet und stark eingekürzt aus
microsoft/uf2-samdx1 (MIT), auf
den SAMD21G17D-Chip dieses Boards zugeschnitten. Herkunft, Lizenz und
vollständige Implementierungsdetails stehen in bootloader/README.md.
Hardwaretest (2026-08-05) und gefundene Bugs
Kompletter Weg auf echter Hardware verifiziert: Bootloader-Flash,
USB-Enumeration, Tastencheck-Einstieg, App-Firmware per .uf2 schreiben,
automatischer Rücksprung in die neue App. Details, Fehlerbilder und Fixes
stehen in bootloader/README.md unter "Hardwaretest"; kurz zusammengefasst:
- Fehlende Synchronisationsbarriere beim Sprung Bootloader→App. Der
Sprung (
__set_MSP→SCB->VTOR→bx) faultete bei jedem eigenständigen Boot. Fix:__DSB(); __ISB();vor dembxincheck_start_application()— von ARM für dieses Pattern vorgeschrieben, fehlte im vendorten Code. - USB-PID-Kollision. Die ursprünglich übernommene PID
0x0011ist Adafruits eigene Gemma-M0-Bootloader-PID; Windows-Rechner mit installiertem Adafruit-Treiber banden das Board fälschlich alsAdafruit Circuit Playground-COM-Port statt als Massenspeicher. Fix: eigene PID0x0043. - OpenOCD-Falle beim manuellen SWD-Debuggen:
program datei.elf verifyohne explizite Zieladresse hat wiederholt den Bootloader mit rohen ELF-Datei-Bytes überschrieben, sobald Bootloader- und App-Flash im selben OpenOCD-Aufruf kombiniert wurden. Bei manuellem Flashen beider Bereiche immer.binmit expliziter Adresse und getrennte OpenOCD-Aufrufe verwenden — Details inbootloader/README.md, "Achtung bei manuellem SWD-Flashen".
Bekannte Einschränkungen
- Bootloader-Flash-Auslastung ~90 % (7364 von 8192 Byte) — wenig Puffer für Änderungen am Bootloader selbst.
- Kein Status-LED-Feedback im Bootloader-Modus (nur WS2812-Kette, dafür ist der Original-Code nicht ausgelegt).
- USB-PID
0x0043ist kollisionsfrei verifiziert, aber kein offiziell registrierter Wert unter Adafruits VID0x239A. upload_uf2.pyist Windows-only (siehe oben).- Kein Software-Trigger, um aus der laufenden App heraus in den Bootloader
zurückzuspringen — nur der physische Kabel-raus/Taste-halten/Kabel-rein-Weg.
Wäre über einen neuen CDC-Befehl (siehe
07_serial_protocol.md) nachrüstbar, der dieselbe
DBL_TAP_MAGIC-RAM-Adresse setzt undNVIC_SystemReset()aufruft.