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:  github/workflows: Bump codecov/codecov-action from 5 to 6. [8c6dfa5]
Comparison: stm32: Match ST DFU bootloader serial number in TinyUSB. [merge of 2fae783]
  mpy-cross:   -48 -0.013% 
   bare-arm:   +12 +0.021% 
minimal x86:   +46 +0.025% 
   unix x64:   +56 +0.007% standard
      stm32:   -12 -0.003% PYBV10
      esp32:   +72 +0.004% ESP32_GENERIC
     mimxrt:   +32 +0.008% TEENSY40
        rp2:   +80 +0.009% RPI_PICO_W
       samd:  +104 +0.038% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:  +329 +0.072% 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

⚠️ 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 (e4920d6) to head (1e59002).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #26      +/-   ##
==========================================
+ Coverage   98.45%   98.46%   +0.01%     
==========================================
  Files         175      176       +1     
  Lines       22635    22784     +149     
==========================================
+ Hits        22286    22435     +149     
  Misses        349      349              

☔ 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 2 times, most recently from a60b5c5 to 105a90a Compare April 20, 2026 08:56
pi-anl added 6 commits April 20, 2026 19:01
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>
The MICROPY_HW_TINYUSB_RHPORTx_MODE macros from mpconfigboard_common.h
are not available when tusb_config.h is compiled, because TinyUSB's
build system includes tusb_config.h before MicroPython's full
configuration chain (mpconfigport.h).

This caused CFG_TUSB_RHPORT0_MODE and CFG_TUSB_RHPORT1_MODE to be set
incorrectly for boards using High-Speed USB with external ULPI PHY,
resulting in USB descriptors with Full-Speed packet sizes (64 bytes)
instead of High-Speed packet sizes (512 bytes).

Replicate the RHPORT selection logic directly in tusb_config.h using
board configuration macros that ARE available at this compilation stage
(MICROPY_HW_USB_MAIN_DEV, MICROPY_HW_USB_HS_ULPI, STM32 family defines).

This ensures proper RHPORT configuration for all STM32 USB configurations:
- Full-Speed only: RHPORT0 enabled, RHPORT1 disabled
- High-Speed with ULPI: RHPORT0 disabled, RHPORT1 High-Speed mode
- High-Speed internal PHY: RHPORT0 disabled, RHPORT1 Full-Speed mode

Tested on STM32H747I-DISCO with USB3320 ULPI PHY.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
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>
@andrewleech andrewleech force-pushed the stm32_tinyusb_hs_clean branch from 105a90a to 2fae783 Compare April 20, 2026 09:01
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