Skip to content

stm32/tinyusb: Add High-Speed USB support and fix CDC/VBUS issues.#26

Draft
andrewleech wants to merge 6 commits intomasterfrom
stm32_tinyusb_hs_clean
Draft

stm32/tinyusb: Add High-Speed USB support and fix CDC/VBUS issues.#26
andrewleech wants to merge 6 commits intomasterfrom
stm32_tinyusb_hs_clean

Conversation

@andrewleech
Copy link
Copy Markdown
Owner

@andrewleech andrewleech commented Mar 15, 2026

Summary

The STM32 port's TinyUSB integration only supports the FS controller. Boards with a HS controller (PYBD_SF6, STM32F429DISC, OLIMEX_H407, or ULPI-based designs) cannot use TinyUSB — the RHPORT configuration is hardcoded to RHPORT 0 / full-speed only.

This PR adds proper RHPORT mode selection in tusb_config.h based on MICROPY_HW_USB_HS and MICROPY_HW_USB_HS_IN_FS board config, routes HS IRQ handlers to the correct RHPORT (including STM32N6 which maps HS to RHPORT 0), and adds VBUS sensing configuration for the HS-in-FS path on STM32F4/F7 (mirroring the existing FS path, with support for both VBDEN and legacy NOVBUSSENS register variants).

Additional issues found and fixed during testing (each is a separate commit and can be split into its own PR if preferred):

  • CDC reconnect stall: When a host closes and reopens the CDC serial port (e.g. repeated mpremote connect/disconnect cycles), the IN endpoint can remain stalled from a prior runtime USB disconnect. The device becomes unresponsive until reset. Fixed by clearing endpoint stall on DTR high. Additionally, on DTR low (host close) the TX FIFO is now flushed so stale data from a previous session does not accumulate and block writes on the next connection.

  • DFU bootloader serial number mismatch: The TinyUSB serial descriptor uses a raw hex dump of all 12 UID bytes (24-char lowercase), while the legacy USB stack and ST's onboard DFU bootloader use a condensed algorithm that selects 6 bytes with two additions (12-char uppercase). This causes the device to report a different serial number depending on which USB stack is active, breaking tools that identify devices by serial (udev rules, mpremote, dfu-util). Fixed by using the ST DFU bootloader algorithm.

  • TinyUSB-specific boot.py template: The factory reset boot.py template references pyb.usb_mode() which does not exist on the TinyUSB stack. A TinyUSB-specific template is added that uses machine.USBDevice with BUILTIN_DEFAULT.

Testing

Built with CFLAGS_EXTRA=-DMICROPY_HW_TINYUSB_STACK=1 for:

  • PYBD_SF6 (F7, HS-in-FS) — flashed and verified CDC serial, machine.USBDevice API, filesystem access
  • OPENMV_N6 (N6, HS-in-FS) — flashed and verified CDC serial, machine.USBDevice API, filesystem access
  • STM32F429DISC (F4, HS-in-FS) — build only
  • OLIMEX_H407 (F4, HS-in-FS) — build only
  • NUCLEO_H563ZI (H5, FS only) — build only, regression check

ULPI boards (STM32H747I_DISCO) are tested on hardware as part of the original development in micropython#18303 — this branch is split out from that work.

CDC serial throughput (OPENMV_N6, HS link at 480 Mbit/s)

Compared TinyUSB vs legacy USB stack using tests/serial_test.py methodology.

Read (device → host):

bufsize TinyUSB KB/s Legacy KB/s Ratio
256 5198 816 6.4x
512 5490 817 6.7x
1024 6050 811 7.5x
2048 7560 817 9.3x
4096 9448 811 11.6x
8192 8422 815 10.3x
16384 8086 787 10.3x

Write (host → device):

bufsize TinyUSB KB/s Legacy KB/s Ratio
256 186 464 0.4x
512 218 592 0.4x
1024 193 671 0.3x
2048 185 733 0.3x
4096 184 763 0.2x
8192 178 786 0.2x
16384 179 800 0.2x

TinyUSB read is 6-12x faster. Write is 2.5-4.5x slower due to a pre-existing limitation in the TinyUSB CDC stdin path — mp_hal_stdin_rx_chr() reads byte-by-byte through a 512-byte ringbuffer, causing backpressure on the USB OUT endpoint. This affects all TinyUSB ports, not just HS, and is being tracked separately.

Trade-offs and Alternatives

The RHPORT configuration uses #ifndef guards so boards like STM32N6 that pre-define CFG_TUSB_RHPORT0_MODE in mpconfigboard_common.h pass through unchanged. This avoids needing per-family #if chains but means any future HS-capable family (e.g. STM32U5) needs its own board-level override.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 15, 2026

Code size report:

Reference:  docs/develop/porting: Update session log for example port. [9f396bb]
Comparison: stm32/extmod: Use full path for shared/tinyusb/mp_usbd.h include. [merge of f6345b7]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:   +48 +0.012% TEENSY40
        rp2:   +32 +0.003% RPI_PICO_W
       samd:   +28 +0.010% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch 6 times, most recently from bb25aee to 4c3e95f Compare March 16, 2026 01:45
@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch from 4c3e95f to 1e59002 Compare March 27, 2026 19:35
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 27, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.46%. Comparing base (9f396bb) to head (f6345b7).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##           master      #26   +/-   ##
=======================================
  Coverage   98.46%   98.46%           
=======================================
  Files         176      176           
  Lines       22811    22811           
=======================================
  Hits        22460    22460           
  Misses        351      351           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch 3 times, most recently from 105a90a to 2fae783 Compare April 20, 2026 09:01
@andrewleech andrewleech force-pushed the master branch 7 times, most recently from ce2c0c9 to 9f396bb Compare May 1, 2026 23:21
@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch from 2fae783 to 2e5b8dc Compare May 2, 2026 05:09
pi-anl added 6 commits May 2, 2026 19:47
Implements mapping from MICROPY_HW_USB_MAIN_DEV to TinyUSB RHPORT
configuration, enabling board-specific USB PHY selection for TinyUSB
stack. Adds support for HS-in-FS mode (High Speed controller running
at Full Speed) which is the default for STM32 boards without external
ULPI PHY. Includes additional TinyUSB source files for DWC2 host
controller support.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Moves STM32F4/F7/H7 high-speed RHPORT mode selection out of the shared
tusb_config.h and into ports/stm32/tinyusb_port/tusb_config.h, following
the alif/nrf pattern. Includes py/mpconfig.h to ensure board config
macros are available when TinyUSB processes the header.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
When a host closes and reopens the CDC serial port, the IN endpoint may
remain stalled from a prior runtime USB disconnect (e.g. mpremote
connect/disconnect cycles). Clear the stall on DTR high so the
connection recovers without requiring a device reset.

On DTR low (host close), flush the TX FIFO so stale data does not
accumulate and block writes on the next connection.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
The TinyUSB serial descriptor used a raw hex dump of all 12 UID bytes
in sequential order (24-char lowercase), while the legacy USB stack and
ST's onboard DFU bootloader use a condensed algorithm that selects 6
bytes with two additions (12-char uppercase).

This mismatch caused the device to report a different serial number
depending on which USB stack was active, breaking tools that identify
devices by serial (e.g. udev rules, mpremote, dfu-util).

Use the ST DFU bootloader algorithm for consistency.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Removes the need for -I$(TOP)/shared/tinyusb/ in the stm32 Makefile
by using an explicit path in the two files that include mp_usbd.h
outside of the shared/tinyusb/ directory itself.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch from 2e5b8dc to f6345b7 Compare May 2, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants