Fold bootloader documentation into the main doc/ index

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>
This commit is contained in:
Julian Appel 2026-08-05 22:49:56 +02:00
parent 3986d2effe
commit 33d9e85992
7 changed files with 153 additions and 201 deletions

View file

@ -15,6 +15,7 @@ ATSAMD21G17D mit PlatformIO und dem Arduino-SAMD-Framework.
| LEDs | 20 WS2812B mit Base-/Override-Farbe und 7 Animationsmodi | | LEDs | 20 WS2812B mit Base-/Override-Farbe und 7 Animationsmodi |
| Persistenz | Config und Makros im internen Flash | | Persistenz | Config und Makros im internen Flash |
| Recovery | Werksreset über zwei Tasten | | 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 Die drei Fader-Pins sind im Board-Variant definiert, werden von der aktuellen
Firmware aber noch nicht eingelesen. Firmware aber noch nicht eingelesen.
@ -29,7 +30,7 @@ Firmware aber noch nicht eingelesen.
| Encoder | 4× Quadratur über EIC-Interrupts | | Encoder | 4× Quadratur über EIC-Interrupts |
| LEDs | 20× WS2812B an `PB22` | | LEDs | 20× WS2812B an `PB22` |
| USB | Native USB als HID + CDC Composite Device | | USB | Native USB als HID + CDC Composite Device |
| Programmer | Atmel-ICE/CMSIS-DAP über SWD, standardmäßig ohne Bootloader | | Programmer | Atmel-ICE/CMSIS-DAP über SWD, oder USB über den UF2-Bootloader |
## Schnellstart ## Schnellstart
@ -46,9 +47,17 @@ Das Standard-Environment `versapad` baut für `boards/versapad_nobl.json`. Der
Upload wird durch `upload_openocd.py` über das von PlatformIO installierte Upload wird durch `upload_openocd.py` über das von PlatformIO installierte
OpenOCD ausgeführt. OpenOCD ausgeführt.
Das in `platformio.ini` auskommentierte Bootloader-Environment ist derzeit Alternativ lässt sich die App-Firmware nach einem einmaligen
nicht als Produktionsziel unterstützt. Details stehen unter SWD-Bootloader-Flash auch über USB aktualisieren, ganz ohne Atmel-ICE:
[bekannte Einschränkungen](doc/09_known_limitations.md).
```bash
cd bootloader && pio run -e versapad_bootloader --target upload # einmalig
pio run -e versapad_usb --target upload # danach jedes App-Update
```
Details, Speicherlayout und die Bootloader-Aktivierung (kein physischer
Reset-Taster auf dieser Platine) stehen in
[10_usb_bootloader.md](doc/10_usb_bootloader.md).
## Laufzeitmodell ## Laufzeitmodell
@ -119,9 +128,13 @@ VersaMCU/
|-- AGENTS.md # Kontext und Richtlinien für Coding-LLMs |-- AGENTS.md # Kontext und Richtlinien für Coding-LLMs
|-- README.md |-- README.md
|-- platformio.ini |-- 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 |-- boards/ # PlatformIO-Boarddefinitionen
|-- variants/versapad/ # Pinmapping und Linker-Skripte |-- variants/versapad/ # Pinmapping und Linker-Skripte
|-- doc/ # Architektur- und Protokolldokumentation |-- doc/ # Architektur- und Protokolldokumentation
|-- bootloader/ # UF2-Bootloader (eigenes PlatformIO-Projekt)
`-- src/ `-- src/
|-- main.cpp |-- main.cpp
|-- CMainController.* # Orchestrierung |-- CMainController.* # Orchestrierung
@ -157,3 +170,4 @@ Binärverträge, Änderungsregeln und die minimale Verifikation.
- [CDC-Protokoll](doc/07_serial_protocol.md) - [CDC-Protokoll](doc/07_serial_protocol.md)
- [Entwicklung und Einstieg](doc/08_development.md) - [Entwicklung und Einstieg](doc/08_development.md)
- [Bekannte Einschränkungen](doc/09_known_limitations.md) - [Bekannte Einschränkungen](doc/09_known_limitations.md)
- [USB-Bootloader](doc/10_usb_bootloader.md)

View file

@ -2,18 +2,11 @@
USB-Bootloader für das VersaPad-v2-Makropad. Wird per Atmel-ICE/SWD einmalig 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 auf den ATSAMD21G17D geflasht und belegt `0x0000..0x1FFF` (8 KiB). Danach
lässt sich die App-Firmware ohne SWD über USB aktualisieren: Bootloader-Modus lässt sich die App-Firmware ohne SWD über USB aktualisieren.
aktivieren, Board erscheint als USB-Laufwerk `VERSABOOT`, `.uf2`-Datei drauf
kopieren.
Der komplette Weg — Bootloader-Einstieg, `.uf2`-Erzeugung, Kopieren aufs Speicherlayout, Bootloader-Aktivierung, Bedienung und Hardwaretest-Ergebnisse
Laufwerk, automatischer Rücksprung in die neu geschriebene App — ist auf stehen in [`../doc/10_usb_bootloader.md`](../doc/10_usb_bootloader.md). Diese
echter Hardware verifiziert, siehe [Hardwaretest](#hardwaretest-2026-08-05). Datei beschreibt nur das Bootloader-Unterprojekt selbst.
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](#hardware-bootloader-einstieg-ohne-reset-taster).
## Herkunft ## Herkunft
@ -52,42 +45,6 @@ da nur `0x0000..0x1FFF` beschrieben wird)
pio run -e versapad_bootloader --target upload 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 24` liegt auf COL4 (`PA08`) × ROW4 (`PA15`)
- `boot_key_pressed()` in `src/main.c` treibt 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)
```bash
# 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.
## Achtung bei manuellem SWD-Flashen von Bootloader UND App ## Achtung bei manuellem SWD-Flashen von Bootloader UND App
`upload_openocd.py` (App, `env:versapad`) und der Bootloader-Upload (oben) `upload_openocd.py` (App, `env:versapad`) und der Bootloader-Upload (oben)
@ -111,61 +68,3 @@ verwenden (nie `.elf`) und die Zieladresse **immer explizit angeben**
App-Schreibvorgang **in getrennten OpenOCD-Aufrufen**, nicht in einer App-Schreibvorgang **in getrennten OpenOCD-Aufrufen**, nicht in einer
gemeinsamen `-c`-Kommandokette. Im Zweifel danach mit `dump_image` in einer gemeinsamen `-c`-Kommandokette. Im Zweifel danach mit `dump_image` in einer
frischen, unabhängigen Sitzung verifizieren. frischen, unabhängigen Sitzung verifizieren.
## 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 korrektem `INFO_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 dem `SCB->VTOR`-Schreib-
zugriff und dem `bx` in `check_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.
- **Zweiter Bug beim manuellen Debuggen gefunden:** Beim anschließenden
manuellen SWD-Debugging (Suche nach der Ursache des Tastencheck-Problems,
siehe oben) hat `openocd -c "program app.elf verify"` ohne explizite
Zieladresse wiederholt den Bootloader mit rohen ELF-Datei-Bytes
überschrieben, sobald Bootloader- und App-Flash im selben OpenOCD-Aufruf
kombiniert wurden — siehe "Achtung bei manuellem SWD-Flashen" oben. Nach
einem vollständigen Chip-Erase und getrennten `.bin`-Flashes mit expliziten
Adressen liefen beide Bereiche wieder zuverlässig, Tastencheck und
App-Start bestätigt funktionsfähig.
- **Kompletter USB-Flashweg getestet:** `pio run -e versapad_usb --target
upload` (App-Firmware, Repo-Root) baut `firmware.bin`, wandelt es über
[`../uf2conv.py`](../uf2conv.py) in `firmware.uf2` und kopiert es über
[`../upload_uf2.py`](../upload_uf2.py) automatisch aufs erkannte
`VERSABOOT`-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 als `Adafruit Circuit
Playground`-COM-Port statt als Massenspeicher gebunden. Verifiziert am
2026-08-05, seither `0x0043`. 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.py`](../upload_uf2.py) sucht das `VERSABOOT`-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.

View file

@ -1,65 +0,0 @@
# Bootloader-Testflash -- Ergebnis
Der UF2-Bootloader (siehe [README.md](README.md)) wurde am 2026-08-05 auf
einem echten VersaPad-v2-Board über Atmel-ICE/SWD getestet. Zusammenfassung
der Ergebnisse steht im [Hardwaretest-Abschnitt der README](README.md#hardwaretest-2026-08-05).
Dieses Dokument hält den Testablauf und die Antworten auf die ursprünglichen
Prüffragen fest.
**Sicherheitsrahmen, der eingehalten wurde:** Der Bootloader-Flash schreibt
nur `0x0000..0x1FFF` (8 KiB). Die App-Firmware ab `0x2000` blieb dabei
unangetastet. Über SWD war der Chip jederzeit neu beschreibbar; ein Brick war
zu keinem Zeitpunkt möglich, solange der Atmel-ICE-Zugriff funktionierte.
## Voraussetzungen
- Board per Atmel-ICE/SWD angeschlossen, wie beim normalen
App-Firmware-Flashen (`pio run -e versapad --target upload`,
siehe `../doc/08_development.md`)
- PlatformIO Core (hier: PlatformIO-IDE-penv unter
`%USERPROFILE%\.platformio\penv\Scripts\pio.exe`)
## Ablauf
1. `git checkout feature/usb-bootloader`
2. `cd bootloader && pio run -e versapad_bootloader` — Build sauber
(89,9 % Flash, 7364/8192 Byte)
3. `pio run -e versapad_bootloader --target upload` — Flash + Verify über
Atmel-ICE/SWD
## Ursprüngliche Prüffragen und Antworten
- **Ist der Flash-Befehl ohne Fehler durchgelaufen?** Ja, `** Verified OK **`
bei jedem Flash.
- **Erscheint nach dem Bootloader-Einstieg ein Laufwerk `VERSABOOT`?** Ja.
Getestete Board-Revision hat **keinen physischen Reset-/Boot-Taster**
(Custom-PCB) — der ursprünglich geplante Doppel-Tap-Reset-Test war damit
nicht durchführbar. Stattdessen wurde ein Hardware-Bootloader-Einstieg über
eine gehaltene Cherry-MX-Taste ergänzt, siehe
[README.md](README.md#hardware-bootloader-einstieg-ohne-reset-taster).
Mit dieser Ergänzung: gehaltene Taste beim Power-On → `VERSABOOT`.
- **Was steht in `INFO_UF2.TXT`?**
```
UF2 Bootloader versapad-1 SFHWRO
Model: VersaPad v2
Board-ID: SAMD21G17D-VersaPad-v2
```
- **Reagiert das Board normal als App-Firmware, wenn man es ohne gehaltene
Taste ansteckt?** Ja, nach dem in der README beschriebenen DSB/ISB-Fix.
Vor dem Fix: nein, siehe unten.
- **Ungewöhnliches?** Ja, ein echter Bug: siehe
[Hardwaretest-Abschnitt der README](README.md#hardwaretest-2026-08-05) für
die Fehlersuche (Bootloader-eigener Sprung faultete zuverlässig, obwohl vom
Debugger injizierte identische Register-/VTOR-Werte einwandfrei liefen) und
den Fix (fehlende `__DSB()`/`__ISB()` vor dem `bx` in
`check_start_application()`).
- **USB-Identität korrekt, kein Fremdtreiber-Konflikt?** Nach PID-Wechsel von
`0x0011` auf `0x0043` ja (siehe README, "Bekannte Einschränkungen" zum
ursprünglichen Adafruit-PID-Konflikt).
## Noch NICHT getestet
Firmware tatsächlich über das `VERSABOOT`-Laufwerk flashen. Der
Firmware-Build der App erzeugt noch keine `.uf2`-Datei (nur `.bin`/`.elf` für
den SWD-Weg) — das ist der nächste Schritt, siehe README, "Bekannte
Einschränkungen".

View file

@ -26,9 +26,9 @@ pio run -e versapad --target upload
Der Upload nutzt `upload_openocd.py`, das das von PlatformIO installierte Der Upload nutzt `upload_openocd.py`, das das von PlatformIO installierte
OpenOCD mit `interface/cmsis-dap.cfg` und `target/at91samdXX.cfg` startet. OpenOCD mit `interface/cmsis-dap.cfg` und `target/at91samdXX.cfg` startet.
Das in `platformio.ini` nur als Beispiel enthaltene Environment Für App-Updates über USB (nach einem einmaligen Bootloader-Flash) gibt es
`versapad_usb` ist auskommentiert und mit dem aktuellen NVM-/Linker-Layout zusätzlich `env:versapad_usb`, siehe
nicht als unterstützt anzusehen. [10_usb_bootloader.md](10_usb_bootloader.md).
## Was beim Start passiert ## Was beim Start passiert
@ -63,6 +63,7 @@ und NVM-Schreibvorgängen. Es gibt keinen Scheduler und keine Threads.
| LEDs | `05_led_system.md` | `CButton.*`, `hal/ws2812.*` | | LEDs | `05_led_system.md` | `CButton.*`, `hal/ws2812.*` |
| Persistente Config | `06_nvm_config.md` | `config/nvm_config.*`, Linker-Skripte | | Persistente Config | `06_nvm_config.md` | `config/nvm_config.*`, Linker-Skripte |
| Host-Protokoll | `07_serial_protocol.md` | `hal/usb_serial.*`, Controller | | Host-Protokoll | `07_serial_protocol.md` | `hal/usb_serial.*`, Controller |
| USB-Bootloader | `10_usb_bootloader.md` | `bootloader/`, `variants/versapad/linker_scripts/gcc/flash_with_bootloader.ld`, `boards/versapad.json`, `uf2conv.py`, `upload_uf2.py` |
## Verifikation ## Verifikation

View file

@ -3,36 +3,33 @@
Diese Liste beschreibt den aktuellen Implementierungsstand nach den Diese Liste beschreibt den aktuellen Implementierungsstand nach den
Robustheitskorrekturen. Sie ist keine Liste bereits umgesetzter Features. Robustheitskorrekturen. Sie ist keine Liste bereits umgesetzter Features.
## Bootloader-Ziel bleibt nicht unterstützt ## USB-Bootloader hat einen eingeschränkten Gültigkeitsbereich
Das aktive Ziel `versapad_nobl` reserviert den kompletten Bereich Der UF2-Bootloader (`bootloader/`, App-Environment `env:versapad_usb`,
`0x1FB00..0x1FFFF` für Makros und Config. Details in [10_usb_bootloader.md](10_usb_bootloader.md)) ist auf echter
Hardware verifiziert und ergänzt den SWD-Weg, ersetzt ihn aber nicht: der
Bootloader selbst muss weiterhin einmalig per Atmel-ICE/SWD geflasht werden.
Ein USB-Bootloader-Pfad wird im Branch `feature/usb-bootloader` aufgebaut Verbleibende Einschränkungen:
(`bootloader/`, App-Environment `env:versapad_usb`). Der komplette Weg ist
dort auf echter Hardware verifiziert: Bootloader-Flash, USB-Enumeration,
Tastencheck-Einstieg (kein physischer Reset-Taster auf diesem Board, siehe
`bootloader/README.md`, "Hardware-Bootloader-Einstieg"), App-Firmware per
`.uf2` über `env:versapad_usb --target upload` schreiben, automatischer
Rücksprung in die neue App. Details und ein gefundener/behobener
Hard-Fault-Bug beim Sprung Bootloader→App (fehlende `__DSB()`/`__ISB()` vor
dem `bx`) stehen in `bootloader/README.md`, "Hardwaretest".
`env:versapad_usb`s Linkerskript
(`variants/versapad/linker_scripts/gcc/flash_with_bootloader.ld`) reserviert
inzwischen denselben NVM-Bereich wie `flash_without_bootloader.ld`.
Trotzdem noch kein Merge-fertiges Produktionsziel: Der `.bin``.uf2`-Weg - `uf2conv.py`/`upload_uf2.py` (Repo-Root) suchen das `VERSABOOT`-Laufwerk
(`uf2conv.py`, `upload_uf2.py`, Repo-Root) sucht das `VERSABOOT`-Laufwerk nur über die Windows-API, keine macOS/Linux-Unterstützung.
bisher nur über die Windows-API, keine macOS/Linux-Unterstützung. Die - Die Bootloader-USB-PID (`0x0043`) ist kollisionsfrei verifiziert, aber kein
Bootloader-USB-PID (`0x0043`) ist zwar kollisionsfrei verifiziert, aber kein
offiziell registrierter Wert unter Adafruits VID `0x239A`. offiziell registrierter Wert unter Adafruits VID `0x239A`.
- Kein Software-Trigger, um aus der laufenden App heraus in den
Bootloader-Modus zu wechseln — nur der physische Weg (Kabel ziehen, Taste
halten, wieder einstecken).
- Bootloader-Flash-Auslastung ~90 % (7364 von 8192 Byte), wenig Puffer für
Änderungen am Bootloader selbst.
Die aktive Boarddatei benennt die MCU als `samd21g17d`, setzt für den Sowohl `boards/versapad_nobl.json` (SWD-Ziel) als auch `boards/versapad.json`
(USB-Bootloader-Ziel) benennen die MCU als `samd21g17d`, setzen für den
Arduino-Core aber weiterhin das Kompatibilitätsmakro `__SAMD21G18A__`. Der Arduino-Core aber weiterhin das Kompatibilitätsmakro `__SAMD21G18A__`. Der
PlatformIO-Build meldet korrekt 128 KiB physischen Flash, 16 KiB RAM und PlatformIO-Build meldet korrekt 128 KiB physischen Flash, 16 KiB RAM und
129.792 Byte nutzbaren Firmwarebereich. Vor device-spezifischen 129.792 Byte (`versapad_nobl`) beziehungsweise 121.600 Byte
Core-Änderungen sollte die historische Makro-Abweichung trotzdem geprüft (`versapad`, abzüglich 8 KiB Bootloader) nutzbaren Firmwarebereich. Vor
werden. device-spezifischen Core-Änderungen sollte die historische Makro-Abweichung
trotzdem geprüft werden.
## Event-Queue hat eine feste Kapazität ## Event-Queue hat eine feste Kapazität

104
doc/10_usb_bootloader.md Normal file
View file

@ -0,0 +1,104 @@
# 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](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](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`):
```bash
cd bootloader
pio run -e versapad_bootloader --target upload
```
Danach App-Firmware per USB (Board vorher wie oben in den Bootloader-Modus
versetzen):
```bash
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](https://github.com/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 dem `bx` in
`check_start_application()` — von ARM für dieses Pattern vorgeschrieben,
fehlte im vendorten Code.
- **USB-PID-Kollision.** Die ursprünglich übernommene PID `0x0011` ist
Adafruits eigene Gemma-M0-Bootloader-PID; Windows-Rechner mit
installiertem Adafruit-Treiber banden das Board fälschlich als
`Adafruit Circuit Playground`-COM-Port statt als Massenspeicher. Fix:
eigene PID `0x0043`.
- **OpenOCD-Falle beim manuellen SWD-Debuggen:** `program datei.elf verify`
ohne 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 `.bin` mit expliziter Adresse und getrennte OpenOCD-Aufrufe
verwenden — Details in `bootloader/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 `0x0043` ist kollisionsfrei verifiziert, aber kein offiziell
registrierter Wert unter Adafruits VID `0x239A`.
- `upload_uf2.py` ist 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](07_serial_protocol.md)) nachrüstbar, der dieselbe
`DBL_TAP_MAGIC`-RAM-Adresse setzt und `NVIC_SystemReset()` aufruft.

View file

@ -17,6 +17,7 @@ Bereiche stehen ausdrücklich in
| [07_serial_protocol.md](07_serial_protocol.md) | 8-Byte-Protokoll, Config-/Makro-Transfer, ACK/NACK | | [07_serial_protocol.md](07_serial_protocol.md) | 8-Byte-Protokoll, Config-/Makro-Transfer, ACK/NACK |
| [08_development.md](08_development.md) | Setup, Build, Einstieg nach Änderungstyp, Verifikation | | [08_development.md](08_development.md) | Setup, Build, Einstieg nach Änderungstyp, Verifikation |
| [09_known_limitations.md](09_known_limitations.md) | Aktuelle technische Einschränkungen und Risiken | | [09_known_limitations.md](09_known_limitations.md) | Aktuelle technische Einschränkungen und Risiken |
| [10_usb_bootloader.md](10_usb_bootloader.md) | UF2-Bootloader, Speicherlayout, Tastencheck-Einstieg, USB-Flashweg |
Die Repository-weiten Richtlinien und der kompakte LLM-Kontext stehen in Die Repository-weiten Richtlinien und der kompakte LLM-Kontext stehen in
[`../AGENTS.md`](../AGENTS.md). [`../AGENTS.md`](../AGENTS.md).
@ -29,3 +30,4 @@ Die Repository-weiten Richtlinien und der kompakte LLM-Kontext stehen in
- Action-Semantik und HID-Hold: [03_action_engine.md](03_action_engine.md) - Action-Semantik und HID-Hold: [03_action_engine.md](03_action_engine.md)
- CDC-Protokoll und Chunk-Zahlen: [07_serial_protocol.md](07_serial_protocol.md) - CDC-Protokoll und Chunk-Zahlen: [07_serial_protocol.md](07_serial_protocol.md)
- bekannte Risiken vor strukturellen Änderungen: [09_known_limitations.md](09_known_limitations.md) - bekannte Risiken vor strukturellen Änderungen: [09_known_limitations.md](09_known_limitations.md)
- Firmware per USB statt SWD flashen: [10_usb_bootloader.md](10_usb_bootloader.md)