Skip to content


lbuild module: modm:tinyusb

TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function.

This module provides a autogenerated port for TinyUSB, which includes the correct interrupt mapping, a serial number based on the UID of the device, as well as remapping the assertions of TinyUSB.

Autogeneration of USB Descriptors

You can select the device classes you want to use via the modm:tinyusb:config list option:

  • device.cdc: Serial connection (uses two endpoints!)
  • device.msc: Mass Storage class.
  • device.midi: MIDI device.
  • device.vendor: WebUSB device.
  • device.dfu: DFU (runtime only).

Note that you can add multiple devices at the same time, as long as there are enough endpoints and USB RAM available:

<!-- Using the CDC and MSC classes together -->
<option name="modm:tinyusb:config">device.cdc,device.msc</option>
<!-- Using two CDC ports! -->
<option name="modm:tinyusb:config">device.cdc,device.cdc</option>

modm will generate the USB descriptors automatically for the set of device classes you've chosen. You can then implement your app via TinyUSB callbacks.

Partial Customization

You can overwrite or add configurations via a <tusb_config_local.h> file, which will be included at the very beginning of the modm-generated tusb_config.h file:

// Overwrite the modm default
// Overwrite the TinyUSB default

You can also replace the following weakly linked descriptor functions and objects in case you want to update only a small part of the autogenerated descriptors:

  • const uint8_t* tud_descriptor_device_cb(void) to replace the autogenerated tusb_desc_device_t descriptor.
  • const uint8_t* tud_descriptor_configuration_cb(uint8_t index) to replace the endpoint descriptions.
  • const char* string_desc_arr[] to replace the string descriptors.

Manual USB Descriptors

If you leave the modm:tinyusb:config option empty, no descriptors are generated, so you can implement them yourself. Note that you must also manually depend on the device classes you want to implement:


Some of these classes require a lot of configuration that you must provide via the <tusb_config_local.h> file. Please consult the TinyUSB documentation and examples for their purpose.

Initializing USB

The modm:platform:usb module provides the correct way of initializing the USB peripheral, however, you must connect the right signals too:

// USB is timing-sensitive, so prioritize the IRQs accordingly

// For Device-Only USB implementations, this is enough
Usb::connect<GpioA11::Dm, GpioA12::Dp>();

// But for On-The-Go (OTG) USB implementations, you need more:
Usb::connect<GpioA11::Dm, GpioA12::Dp, GpioA10::Id>();
// Enable hardware Vbus sensing on GpioA9 (this can be tricky to get right!)

Note that depending on your specific hardware setup, you may need to fiddle around to find the right VBus sensing mechanism. Please look at the TinyUSB board definitions and examples for inspiration.

USB shares resources with CAN

Note that on STM32F1 and STM32F3 the USB interrupts and RAM are shared with CAN, thus there are conflicts in IRQ definitions as well as resource limitions in hardware. On some STM32F3, the USB IRQs can be remapped, this is done automatically by our port.

Debugging TinyUSB

Since we've made it so easy to add multiple device classes, it's also easy to run out of endpoints or RAM. Therefore we reroute TinyUSBs assertions to modm_assert, so make sure you have implemented the modm_abandon handler! See the modm:architecture:assert module for details.

A TinyUSB assertion failure in release mode is fairly cryptic:

Assertion 'tu' failed!

If you run this again in debug mode, you'll note a much more detailed assertion description. In this example you've exhaused the number of endpoints:

Assertion 'tu' failed!
  modm/ext/tinyusb/portable/st/synopsys/dcd_synopsys.c:524 -> "epnum < 4U"

To trace the TinyUSB core, you can add CFG_TUSB_DEBUG=2 to your CPP flags and the output will be forwarded to MODM_LOG_DEBUG.

<collect name="modm:build:cppdefines">CFG_TUSB_DEBUG=2</collect>

This module is only available for rp, sam{d1x/d2x/dax,d5x/e5x,g5x}, stm32{f0,f1,f2,f3,f4,f7,g4,h7,l0,l1,l4,l5,u5}.



Endpoint Configuration

Default: []
Inputs: [device.cdc, device.dfu, device.midi, device.msc, device.vendor]
Input Dependency: device.cdc -> modm:tinyusb:device:cdc
Input Dependency: device.dfu -> modm:tinyusb:device:dfu
Input Dependency: device.midi -> modm:tinyusb:device:midi
Input Dependency: device.msc -> modm:tinyusb:device:msc
Input Dependency: device.vendor -> modm:tinyusb:device:vendor


USB Port Speed

This option is only available for stm32{f2,f4,f7,h7}.

Default: full
Inputs: [full, high]
Input Dependency: full -> modm:platform:usb:fs
Input Dependency: high -> modm:platform:usb:hs


modm:tinyusb modm_tinyusb modm: tinyusb modm_architecture_atomic modm: architecture: atomic modm_tinyusb->modm_architecture_atomic modm_architecture_interrupt modm: architecture: interrupt modm_tinyusb->modm_architecture_interrupt modm_cmsis_device modm: cmsis: device modm_tinyusb->modm_cmsis_device modm_platform_usb modm: platform: usb modm_tinyusb->modm_platform_usb modm_platform_usb_fs modm: platform: usb: fs modm_tinyusb->modm_platform_usb_fs modm_platform_usb_hs modm: platform: usb: hs modm_tinyusb->modm_platform_usb_hs modm_tinyusb_device_cdc modm: tinyusb: device: cdc modm_tinyusb->modm_tinyusb_device_cdc modm_tinyusb_device_dfu modm: tinyusb: device: dfu modm_tinyusb->modm_tinyusb_device_dfu modm_tinyusb_device_midi modm: tinyusb: device: midi modm_tinyusb->modm_tinyusb_device_midi modm_tinyusb_device_msc modm: tinyusb: device: msc modm_tinyusb->modm_tinyusb_device_msc modm_tinyusb_device_vendor modm: tinyusb: device: vendor modm_tinyusb->modm_tinyusb_device_vendor

Limited availability: Check with 'lbuild discover' if this module is available for your target!