case 08 · Personal · 2025

STM32 USB

A USB device implementation on the STM32F446RE — descriptor table, endpoint configuration, and enumeration over the on-chip OTG_FS peripheral.

C STM32F446RE USB OTG_FS
github.com/Prateek2174/usb

What's in it

A usb.c / usb.h pair that brings the OTG_FS peripheral up as a USB device — clock setup, endpoint allocation, and the descriptor responses that let a host enumerate the board. main.c ties it together.

What was interesting

USB enumeration is one of those rituals where the host walks the device through a fixed sequence and any mistake — wrong descriptor length, late ack on EP0, a single byte off in the configuration descriptor — looks identical from the host side: "device descriptor request failed." The fun is in narrowing that down without a USB analyzer; mostly you stare at the OTG_FS interrupt status register and a log line per packet.

Notes

  • OTG_FS clocked off the PLL with a 48 MHz target for USB.
  • Control endpoint (EP0) handles standard requests directly.
  • Designed as a starting point — easy to layer a class driver (HID, CDC) on top.
Core/Src/usb.c C


void usb_core_init(void){

	USB_OTG_FS->GAHBCFG |= USB_OTG_GAHBCFG_GINT; //Unmask core interrupts
	USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM; //Enable Rx FIFO non-empty

	//Enable and choose at what FIFO level of Tx the interrupt fires
	USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_PTXFEM;
	USB_OTG_FS->GAHBCFG |= USB_OTG_GAHBCFG_PTXFELVL;

	//2. Program the following fields in the OTG_GUSBCFG register

	USB_OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP; //Enable HNP capability
	USB_OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_SRPCAP; //Enable SRP capability

	//Set OTG_FS/OTG_HS timeout calibration field
	USB_OTG_FS->GUSBCFG &= ~USB_OTG_GUSBCFG_TOCAL; //*****change this field if using external PHY

	//Set USB turn around time field
	USB_OTG_FS->GUSBCFG &= ~USB_OTG_GUSBCFG_TRDT;
	USB_OTG_FS->GUSBCFG |= (0x6 << USB_OTG_GUSBCFG_TRDT_Pos);

	//3. The software must unmask the following bits in the OTG_GINTMSK register

	USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_OTGINT;
	USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_MMISM;


}
MCU
STM32F446RE
Peripheral
USB OTG_FS
Speed
Full-Speed (12 Mb/s)
Build
STM32CubeIDE

What's next

  • CDC virtual COM port — implement the USB Communications Device Class on top of the existing device stack so the board appears as a serial port to the host; useful for debug output without a separate UART adapter
  • DFU mode — add a Device Firmware Upgrade interface so firmware can be flashed over USB rather than a debug probe
  • USB mass storage — expose an SD card or internal flash as a USB drive; requires a bulk-only transport layer on top of the endpoint logic
  • USB HID — implement a HID descriptor to enumerate as a keyboard or gamepad; low endpoint count and straightforward report format make it a good next class to tackle