Skip to content

Commit d722701

Browse files
committed
Add STM32WB55 support and implement reliable cross-family software DFU entry
This update introduces STM32WB55 support and significantly improves software DFU reliability across supported STM32 families. Added STM32WB55 support via Nucleo-64 board definitions (e.g. NUCLEO WB55 USB Dongle, P-NUCLEO-WB55) dfu_boot_stm32wb.c to ensure reliable DFU entry on WB55 Extended STM32G4 testing (WeAct STM32G474) Improved Reworked software DFU entry for more consistent behaviour Added required boards.txt modifications for each supported MCU family STM32WB55 Notes Generic WBxx board variants (e.g. Generic WB55CGUx) lack a SystemClock_Config implementation in the STM32 Arduino core. Without proper PLLSAI1Q configuration, the USB peripheral does not receive a valid 48 MHz clock and will fail to enumerate. As a workaround, TinyUSB is enabled for Nucleo-64 WB55 board definitions, which include a valid USB clock configuration. This also enables TinyUSB on most Nucleo-64 boards using F1/F4/G4 MCUs, although the TinyUSB option may appear for unsupported boards due to this approach. A proper long-term solution would be adding a generic_clock.c implementation to the generic WB55 variant in the STM32 Arduino core. DFU Implementation Details On STM32F1/F4/G4, DFU entry is achieved by disabling interrupts, resetting clocks, remapping system memory, and jumping directly to the bootloader. On STM32WB55, this approach is unreliable because the bootloader is sensitive to residual peripheral and clock state. Jumping directly from a running sketch can prevent USB DFU from enumerating. The new WB55 implementation: Writes a magic value to RTC backup register BKP0R Triggers NVIC_SystemReset() Intercepts reset early via a Reset_Handler override Jumps to the bootloader in a near-clean reset state Falls through to normal startup when the magic value is absent This ensures reliable DFU entry while leaving normal boot behaviour unaffected.
1 parent 9fc837f commit d722701

File tree

6 files changed

+580
-347
lines changed

6 files changed

+580
-347
lines changed

CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Changelog
2+
3+
## [v0.3.0] - 2026-02-26
4+
5+
### Added
6+
- STM32WB55 support, tested on WeAct STM32WB55CGU6 using P-NUCLEO-WB55 / NUCLEO WB55 USB Dongle board definition
7+
- Nucleo-64 board definition support — most F1/F4/G4 Nucleo-64 variants should work as a side effect
8+
- `dfu_boot_stm32wb.c` — WB55-specific `Reset_Handler` override for reliable DFU bootloader entry
9+
- WeAct STM32G474 confirmed working
10+
- WeAct STM32F405 confirmed working
11+
12+
### Changed
13+
- DFU bootloader entry completely overhauled — previous implementation was not reliable
14+
- F1/F4/G4: now uses direct jump to ST ROM bootloader after disabling interrupts and resetting clocks
15+
- WB55: uses magic value in `BKP0R` + `NVIC_SystemReset()`, intercepted by `Reset_Handler` override before `SystemInit()` runs
16+
- `boards.txt` entries now require four lines per board (two additional lines for `use_1200bps_touch` and `wait_for_upload_port` to enable Arduino IDE auto-upload)
17+
- `boards_txt_additions.txt` updated to reflect new four-line format and added Nucleo_64 entry
18+
19+
### Known Limitations
20+
- Generic WB55 board definitions lack USB clock configuration and are not supported — use P-NUCLEO-WB55 or NUCLEO WB55 USB Dongle board definition instead. Proper generic support requires adding a clock configuration (PLLSAI1Q 48 MHz USB clock) to the generic WB55 variant in the STM32duino core.
21+
- STM32F1 DFU entry requires further validation on boards with factory bootloader.
22+
23+
---
24+
25+
## [v0.2.0] - 2026-02-21
26+
27+
### Added
28+
- STM32F1 support, tested on STM32F103 BluePill
29+
- STM32F4 support, tested on WeAct STM32F411 BlackPill
30+
- STM32G4 support, tested on WeAct STM32G431
31+
- Automatic USB initialisation via `initVariant()` — no sketch boilerplate required
32+
- Automatic task polling via `HAL_IncTick()` override and `serialEventRun()` hook
33+
- `Serial` automatically aliased to `SerialTinyUSB`
34+
- DFU bootloader entry via "touch 1200" auto-reset
35+
- `TinyUSB_Port_GetSerialNumber()` using factory UID registers for all families
36+
37+
38+
## [v0.1.1] - 2026-01-25
39+
### Fixed
40+
- Added missing redirect of `Serial` to `TinyUSBSerial`
41+
42+
## [v0.1.0] - 2026-01-25
43+
44+
### Added
45+
- Initial version with STM32F4 support, tested on WeAct STM32F411 BlackPill

README.md

Lines changed: 43 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,38 @@
11
# TinyUSB for STM32 (Arduino)
22

3-
> **Note:** An earlier version of this port is integrated into the official Adafruit TinyUSB library. I will submit the updated version once it is ready and hopefully get the changes made in the stm32 core files so that manual editing of boards.txt is not required.
3+
> **Note:** An earlier version of this port with partial support for the F4 family is integrated into the official Adafruit TinyUSB library. I will submit the updated version once it is ready and hopefully get the changes made in the stm32 core files so that manual editing of boards.txt is not required.
44
5-
This port adds STM32F1, STM32F4, and STM32G4 support to the [Adafruit TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino), enabling advanced USB device functionality (MIDI, HID, MSC, CDC, etc.) on STM32 microcontrollers.
5+
This port adds STM32F1, STM32F4, STM32G4, and STM32WB55 support to the [Adafruit TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino), enabling advanced USB device functionality (MIDI, HID, MSC, CDC, etc.) on STM32 microcontrollers.
66

7-
## What's New
7+
## Overview
88

9-
Whilst this port should still be considered experimental, it is now a lot more fleshed out than previous versions:
9+
The Adafruit TinyUSB library includes an initial verion of this STM32 port, currently listed as experimental. This repository extends that work — adding support for additional STM32 families, fully automatic USB initialisation and task polling, and reliable DFU bootloader entry, with no boilerplate required in your sketch. `Serial` is automatically aliased to `SerialTinyUSB`. The goal is that sketches written for officially supported cores like RP2040 and SAMD should work on STM32 without modification.
1010

11-
```cpp
12-
// Previously required in setup():
13-
if (!TinyUSBDevice.isInitialized()) { TinyUSBDevice.begin(0); }
14-
if (TinyUSBDevice.mounted()) { TinyUSBDevice.detach(); delay(10); TinyUSBDevice.attach(); }
15-
16-
// Previously required in loop():
17-
#ifdef TINYUSB_NEED_POLLING_TASK
18-
TinyUSBDevice.task();
19-
#endif
20-
```
21-
22-
This version eliminates all of that. USB initialisation, task polling, CDC flushing, and DFU bootloader entry are all handled automatically — sketches work the same way as on officially supported cores like RP2040 and SAMD. `Serial` is now also automatically aliased to `SerialTinyUSB`, so no `#define` is needed in your sketch, `Serial` commands work like they should. And most importantly no need to press the BOOT and RESET buttons when uploading anymore!
23-
24-
Support has been added and tested for three STM32 families: **F1**, **F4**, and **G4**.
11+
Support has been tested across four STM32 families: **F1**, **F4**, **G4**, and **WB55**. See the [Changelog](CHANGELOG.md) for the full history of what's been added and changed.
2512

2613
## Compatibility
2714

2815
### Tested and Working
29-
| Board | MCU | Family |
30-
|---|---|---|
31-
| WeAct STM32F411 BlackPill | STM32F411 | F4 |
32-
| STM32F103 BluePill | STM32F103 | F1 |
33-
| WeAct STM32G431 | STM32G431 | G4 |
16+
| Board | MCU | Family | Board Definition |
17+
|---|---|---|---|
18+
| WeAct STM32F411 BlackPill | STM32F411 | F4 | Generic F4 |
19+
| WeAct STM32F405 | STM32F405 | F4 | Generic F4 |
20+
| STM32F103 BluePill | STM32F103 | F1 | Generic F1 |
21+
| WeAct STM32G431 | STM32G431 | G4 | Generic G4 |
22+
| WeAct STM32G474 | STM32G474 | G4 | Generic G4 |
23+
| WeAct STM32WB55CGU6 | STM32WB55 | WB55 | Nucleo WB55 USB Dongle / P-NUCLEO-WB55 |
3424

3525
### Should Work (untested)
36-
- **STM32F4 series:** F401, F405, F407, F446 and other F4xx variants — same OTG_FS peripheral
26+
- **STM32F4 series:** F401, F407, F446 and other F4xx variants — same OTG_FS peripheral
3727
- **STM32F1 series:** Other F103 variants — same USB peripheral
38-
- **STM32G4 series:** G474, G491, G4A1 — same USB peripheral
28+
- **STM32G4 series:** G491, G4A1 — same USB peripheral
29+
- **STM32WB55 series:** Other WB55 variants with 32MHz HSE crystal using Nucleo board definition
30+
- **Nucleo-64 boards:** Most F1/F4/G4 Nucleo-64 variants should work via the Nucleo_64 board definition
3931

40-
### Not Supported (yet)
32+
### Not Supported
4133
- STM32F0, F2, F3 — different USB peripheral architecture
4234
- STM32L, STM32H7 — would need clock configuration changes
35+
- Generic WB55 board definitions — see Known Limitations
4336

4437
**If you test on other boards, please report your results!**
4538

@@ -55,12 +48,12 @@ Support has been added and tested for three STM32 families: **F1**, **F4**, and
5548

5649
## How It Works
5750

58-
The port implements the three hooks required by the STM32 Arduino core to integrate TinyUSB as a first-class citizen. `initVariant()` is called automatically before `setup()` and handles all USB hardware and stack initialisation. `HAL_IncTick()` is overridden to signal every 1ms SysTick tick, which `yield()` and `serialEventRun()` use to service the TinyUSB task in thread context — ensuring USB events are processed reliably regardless of what the sketch is doing. `TinyUSB_Port_EnterDFU()` handles the Arduino IDE's "touch 1200" auto-reset by disconnecting from USB cleanly, writing the STM32duino bootloader magic value to a backup register, and issuing a system reset.
51+
The port implements the hooks required by the STM32 Arduino core to integrate TinyUSB as a first-class citizen. `initVariant()` is called automatically before `setup()` and handles all USB hardware and stack initialisation. `HAL_IncTick()` is overridden to signal every 1ms SysTick tick, which `yield()` and `serialEventRun()` use to service the TinyUSB task in thread context — ensuring USB events are processed reliably regardless of what the sketch is doing. DFU bootloader entry is triggered by the Arduino IDE's "touch 1200" sequence: on F1/F4/G4 the port disables interrupts, resets clocks, and jumps directly to the ST ROM bootloader; on WB55 a magic value is written to a backup register and the chip resets, with a `Reset_Handler` override intercepting the reboot before `SystemInit()` runs to jump to the bootloader from a near-clean state.
5952

6053
## Prerequisites
6154
1. [Arduino IDE](https://www.arduino.cc/en/software)
6255
2. [STM32duino Core](https://github.com/stm32duino/Arduino_Core_STM32) (official STMicroelectronics core)
63-
3. [Adafruit TinyUSB Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino) (install via Library Manager)
56+
3. [Adafruit TinyUSB Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino) (v3.7.4 or higher - install via Library Manager)
6457

6558
## Installation
6659

@@ -73,114 +66,57 @@ Place the `stm32` folder into:
7366
libraries/Adafruit_TinyUSB_Library/src/arduino/ports/
7467
```
7568

76-
### Step 3: Edit tusb_config.h
77-
Open `Arduino/libraries/Adafruit_TinyUSB_Library/src/tusb_config.h` and find the platform detection block (around line 30–40):
78-
79-
```cpp
80-
#if defined(ARDUINO_ARCH_SAMD)
81-
#include "arduino/ports/samd/tusb_config_samd.h"
82-
#elif defined(ARDUINO_NRF52_ADAFRUIT)
83-
#include "arduino/ports/nrf/tusb_config_nrf.h"
84-
#elif defined(ARDUINO_ARCH_RP2040)
85-
#include "arduino/ports/rp2040/tusb_config_rp2040.h"
86-
#elif defined(ARDUINO_ARCH_ESP32)
87-
// do nothing since we force include "arduino/ports/esp32/tusb_config_esp32.h" in tusb_option.h
88-
#elif defined(ARDUINO_ARCH_CH32) || defined(CH32V20x) || defined(CH32V30x)
89-
#include "arduino/ports/ch32/tusb_config_ch32.h"
90-
#else
91-
#error TinyUSB Arduino Library does not support your core yet
92-
#endif
93-
```
94-
95-
Add the following lines **before** the `#else`:
96-
97-
```cpp
98-
#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
99-
#include "arduino/ports/stm32/tusb_config_stm32.h"
100-
```
101-
102-
### Step 4: Edit boards.txt
69+
### Step 3: Edit boards.txt
10370
Open your STM32 core's `boards.txt`, typically at:
10471
```
10572
Arduino15/packages/STMicroelectronics/hardware/stm32/[version]/boards.txt
10673
```
10774

108-
Find your board's section and add a TinyUSB USB menu entry. The board identifier prefix (e.g. `GenF4`, `GenF1`, `GenG4`) must match the one already used in that board's section. I have included the lines you need to add in `boards_txt_additions.txt` for ease of use. Examples for each family:
109-
110-
**STM32F4 (e.g. Generic F4):**
111-
```
112-
GenF4.menu.usb.TinyUSB=Adafruit TinyUSB
113-
GenF4.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
114-
```
75+
Refer to `boards_txt_additions.txt` (included in this repository) for the exact lines to add and instructions on finding the right prefix for your board. Each board entry now requires four lines — two to enable TinyUSB and two to enable "touch 1200" DFU upload.
11576

116-
**STM32F1 (e.g. Generic F1):**
117-
```
118-
GenF1.menu.usb.TinyUSB=Adafruit TinyUSB
119-
GenF1.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
120-
```
121-
122-
**STM32G4 (e.g. Generic G4):**
123-
```
124-
GenG4.menu.usb.TinyUSB=Adafruit TinyUSB
125-
GenG4.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
126-
```
127-
128-
### Step 5: Restart Arduino IDE
77+
### Step 4: Restart Arduino IDE
12978
Close and reopen Arduino IDE for changes to take effect.
13079

131-
### Step 6: Select TinyUSB
80+
### Step 5: Select TinyUSB
13281
In Arduino IDE, go to **Tools > USB** and select **"Adafruit TinyUSB"**.
13382

13483
## Usage
13584

136-
Sketches require no USB initialisation boilerplate. `Serial` works normally. Here is a minimal CDC echo example — the entire sketch is just application code:
137-
138-
```cpp
139-
#include <Adafruit_TinyUSB.h>
140-
141-
void setup() {
142-
Serial.begin(115200);
143-
// optional delay
144-
while (!TinyUSBDevice.mounted()) delay(1);
145-
Serial.println("Hello from STM32!");
146-
}
147-
148-
void loop() {
149-
if (Serial.available()) {
150-
Serial.write(Serial.read());
151-
}
152-
}
153-
```
154-
155-
> **Note:** `while (!TinyUSBDevice.mounted()) delay(1)` is entirely optional but useful if your sketch needs to send data immediately on startup. It works correctly because `tud_task()` is serviced inside `delay()`.
85+
Sketches require no USB initialisation boilerplate. `Serial` works normally.
15686

15787
## File Descriptions
15888

159-
- **`Adafruit_TinyUSB_stm32.cpp`** — Hardware initialisation, IRQ handlers, automatic task polling, and DFU bootloader entry for F1, F4, and G4 families
89+
- **`Adafruit_TinyUSB_stm32.cpp`** — Hardware initialisation, IRQ handlers, automatic task polling, and DFU bootloader entry for F1, F4, G4, and WB55 families
16090
- **`tusb_config_stm32.h`** — TinyUSB configuration and class enable flags for STM32
91+
- **`dfu_boot_stm32wb.c`** — WB55-specific Reset_Handler override for reliable DFU bootloader entry
92+
- **`boards_txt_additions.txt`** — Reference file with the lines to add to the STM32duino core's `boards.txt`
16193

16294
## Known Limitations
163-
- Only the F1, F4, and G4 families are currently supported — see compatibility table above
164-
- Requires the official STM32duino core — other STM32 cores are untested
165-
- DFU bootloader entry requires that your board's upload method in Arduino board settings is configured to use STM32CubeProgrammer in DFU mode
95+
- Generic WB55 board definitions (e.g. Generic WB55CGUx) are not supported — these variants lack a `SystemClock_Config` implementation in the STM32duino core, so the USB peripheral never receives a valid 48MHz clock. Use the **P-NUCLEO-WB55** or **NUCLEO WB55 USB Dongle** board definition instead. Custom WB55 boards with a 32MHz HSE crystal should also work with these definitions. A proper fix would require a `generic_clock.c` to be added to the generic WB55 variant in the STM32duino core upstream — this may be raised as a separate issue or PR.
96+
- The "Adafruit TinyUSB" option will appear in the IDE USB menu for unsupported Nucleo-64 variants — this is a side effect of the WB55 workaround.
97+
- Requires the official STM32duino core — other STM32 cores are untested.
98+
- DFU bootloader entry requires that your board's upload method is configured to use STM32CubeProgrammer in DFU mode.
16699

167100
## Troubleshooting
168101

169102
**Device not enumerating / "USB device malfunctioned":**
170103
- Verify you selected "Adafruit TinyUSB" from Tools > USB
171-
- Check that USB D+ and D- pins on your board are not used for other purposes
172-
- Try a different USB cable (or plugging directly into the PC if you are using a hub)
173-
- On G4 boards, ensure no other USB stack (e.g. the STM32duino built-in CDC) is also enabled
104+
- Check that D− and D+ pins on your board are not used for other purposes
105+
- Try a different USB cable (some cables are charge-only)
106+
- On WB55, ensure you are using the P-NUCLEO-WB55 or NUCLEO WB55 USB Dongle board definition, not a generic WB55 variant
174107

175108
**Compilation errors:**
176-
- Confirm the Adafruit TinyUSB library is installed via Library Manager
109+
- Confirm the latest version of Adafruit TinyUSB library is installed via Library Manager
177110
- Make sure USB support in board setting is set to `Adafruit TinyUSB`
178111
- Restart Arduino IDE after making changes to `tusb_config.h` or `boards.txt`
179112
- Verify the `stm32` folder is in the correct location under `ports/`
113+
- Verify `U(S)ART Support` in board options is set to `Enabled (generic 'serial')`
180114

181115
**"Touch 1200" / DFU upload not working:**
116+
- Confirm all four lines are present in `boards.txt` for your board (see `boards_txt_additions.txt`)
182117
- Confirm your board's upload method is set to STM32CubeProgrammer (DFU)
183118
- Check that the correct DFU drivers are installed on your system (use [Zadig](https://zadig.akeo.ie/) on Windows if needed)
119+
- One sketch must be uploaded using manual DFU (or other method) once the port is installed in order to enable the automated DFU push
184120

185121
## Contributing
186122
Issues and pull requests welcome! If you test this on other STM32 boards or families, please report your results.
@@ -189,10 +125,9 @@ Issues and pull requests welcome! If you test this on other STM32 boards or fami
189125

190126
Want support for a board or STM32 family not listed here? I'm happy to add it, but I can't afford to buy every STM32 dev board out there. If you send me the board (or cover the cost of it), I'll add support and test it properly.
191127

192-
Get in touch via Ko-fi — use the Commissions feature to describe what you need:
193-
**[ko-fi.com/yourname](https://ko-fi.com/yourname)**
128+
Get in touch via Ko-fi — use the Commissions feature to describe what you need: **[ko-fi.com/codefiasco](https://ko-fi.com/codefiasco)**
194129

195-
> ☕ General donations to keep the project going are also very welcome!
130+
**☕ General donations to keep the project going are also very welcome!**
196131

197132
## Credits
198-
Based on the [Adafruit TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino).
133+
Based on the [Adafruit TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino).
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,38 @@
1414
# Replace the prefix (GenF4 / GenF1 / GenG4) with the one for your board.
1515
# Add the lines alongside any existing menu.usb entries for that board.
1616
#
17+
# STM32WB55 -- NOT SUPPORTED via generic board definitions
18+
#
19+
# Generic WB55 variants lack USB clock configuration and will not enumerate.
20+
# Use the P-NUCLEO-WB55 or NUCLEO WB55 USB Dongle board definition instead
21+
# (covered by the Nucleo_64 entry above). Custom WB55 boards with a 32MHz
22+
# HSE crystal should also work with these definitions.
23+
#
24+
# Proper generic support requires upstream changes to the STM32duino core.
1725
# -----------------------------------------------------------------------
1826

19-
# STM32F4 (e.g. Generic F4 -- tested on WeAct STM32F411 BlackPill)
27+
28+
# STM32F4 (e.g. Generic F4 -- tested on WeAct STM32F411 BlackPill and WeAct STM32F405)
2029
GenF4.menu.usb.TinyUSB=Adafruit TinyUSB
2130
GenF4.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
31+
GenF4.menu.usb.TinyUSB.upload.use_1200bps_touch=true
32+
GenF4.menu.usb.TinyUSB.upload.wait_for_upload_port=false
2233

2334
# STM32F1 (e.g. Generic F1 -- tested on STM32F103 BluePill)
2435
GenF1.menu.usb.TinyUSB=Adafruit TinyUSB
2536
GenF1.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
37+
GenF1.menu.usb.TinyUSB.upload.use_1200bps_touch=true
38+
GenF1.menu.usb.TinyUSB.upload.wait_for_upload_port=false
2639

27-
# STM32G4 (e.g. Generic G4 -- tested on WeAct STM32G431)
40+
# STM32G4 (e.g. Generic G4 -- tested on WeAct STM32G431 and WeAct STM32G474)
2841
GenG4.menu.usb.TinyUSB=Adafruit TinyUSB
2942
GenG4.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
43+
GenG4.menu.usb.TinyUSB.upload.use_1200bps_touch=true
44+
GenG4.menu.usb.TinyUSB.upload.wait_for_upload_port=false
45+
46+
# Nucleo-64 boards -- added primarily for STM32WB55 support (see WB55 note above).
47+
# Also enables TinyUSB for most Nucleo-64 boards with F1, F4, or G4 MCUs.
48+
Nucleo_64.menu.usb.TinyUSB=Adafruit TinyUSB
49+
Nucleo_64.menu.usb.TinyUSB.build.usb_flags={build.extra_flags} -DARDUINO_ARCH_TINYUSB
50+
Nucleo_64.menu.usb.TinyUSB.upload.use_1200bps_touch=true
51+
Nucleo_64.menu.usb.TinyUSB.upload.wait_for_upload_port=false

0 commit comments

Comments
 (0)