STM32F411 USB module step by step - Page 1
Forum section: electronics / general

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 06:22:20 pm
content: Hi all,After takling a little the USB module in the STM32F103, I want to play with that of the big brother the STM32F411. It's a complicated beast but I project to start with the device mode only.I've taken a look at the provided ST library but I found it very difficult alas! So I'm studying the RM 0383, but I couldn't grasp some things:1- Since the RX fifo is shared, is there one POP register for all EPs? If yes, which one?If not, are the POP and PUSH registers for an EP the same? If different which is which?2- What's the minimal initialization needed to start the module aside from enabling clocks, configuring PA11 and PA12 pins to alt-func push-pull outputs with AF10 and enabling the OTG_FS interrupt Nr 67 in the NVIC?3- What to do when these interrupts fire: RESET and ENUM DONE?Please help and be indulgent with me.

author: langwadt
author_rank: Super Contributor
timestamp: February 26, 2025, 06:26:51 pm
content: just install stm cube ide ....

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 06:32:04 pm
content: Quote from: langwadt on February 26, 2025, 06:26:51 pmI prefer going the bare metal way to undersatand what's hapenning under the hood.

author: langwadt
author_rank: Super Contributor
timestamp: February 26, 2025, 06:41:53 pm
content: Quote from: DELTA67 on February 26, 2025, 06:32:04 pmso install stm cube ide and look at what it does, unless of course the idea is to make it as complicated and time consuming as possible

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 06:58:10 pm
content: Quote from: langwadt on February 26, 2025, 06:41:53 pmstm cube ide uses the libraries provided by ST which I've already tried to dig into without success.I've some spare time to spent in order to understand this module.After reading the RM, here's a part of the initialization process I'll try:Code:// Force Device modeOTG_FS->GUSBCFG |= 1<<30;// Enable O_EP, I_EP, ENUM, RESET and RXLVL InterruptsOTG_FS->GINTMSK = 1<<19 | 1<<18 | 1<<13 | 1<<12 | 1<<4;// Turn on the global interrupt switchOTG_FS->GAHBCFG |= 1<<0;// enable OTG_FS interrupt (Nr 67) in NVICNVIC->ISER[67 >> 5] = 1 << (67 & 0x1F);// deactivate power down and disable Vbus sensingOTG_FS->GCCFG = 1<<21 | 1<<16;We have to configure the clock to 48MHz and enable it in RCC_AHB2ENR before.

author: voltsandjolts
author_rank: Unknown
timestamp: February 26, 2025, 07:11:52 pm
content: I've been meaning to try porting Alex's usb implementation from samd to stm32, just for fun. And I always find Alex's code very clear and concise.https://github.com/ataradov/vcp/tree/master/samd11

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 07:27:31 pm
content: Thanks volisandjolts for the link, I'll take a look into ASAP.Is the SAMD11 using the same USB controller as the STM32F4XX??

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 07:56:43 pm
content: and here's the interrupt handler.Code:void OTG_FS_handler(){unsigned int intflags = OTG_FS->GINTSTS;if (intflags & 1<<19 ) {printf ( "Int -- OUT EP\n");}if (intflags & 1<<18 ){printf ( "Int -- IN EP\n");}if (intflags & 1<<13 ) {printf ( "Int -- Enumeration done\n");}if (intflags & 1<<12 ) {printf ( "Int -- RESET\n");}if (intflags & 1<<4 ) {printf ("Int - Rx FIFO not empty\n");}OTG_FS->GINTSTS = 0xffffffff;// clear all}

author: DELTA67
author_rank: Regular Contributor
timestamp: February 26, 2025, 08:18:40 pm
content: Tested the code above.I'm getting "Int - Rx FIFO not empty" continously at an astonishing speed!!!It seems that writing 1 to it's flag in OTG_FS_GINTSTS don't clear it!But it's a good start, at least the USB module is working and sending interrupts to us.

author: voltsandjolts
author_rank: Unknown
timestamp: February 26, 2025, 08:59:56 pm
content: Quote from: DELTA67 on February 26, 2025, 07:27:31 pmI haven't checked but I expect they are quite different.

author: DELTA67
author_rank: Regular Contributor
timestamp: February 27, 2025, 02:02:01 pm
content: Well, I'm experimenting with this excellent notes (thanks Tom):http://cholla.mmto.org/computers/usb/hydra/The test code:https://github.com/trebisky/stm32-hydra/blob/master/usb_411.c

author: wek
author_rank: Frequent Contributor
timestamp: March 01, 2025, 11:30:33 am
content: > 1- Since the RX fifo is shared, is there one POP register for all EPs? If yes, which one?The FIFO scheme in the Synopsys OTG module is bizarre (and probably tries to cater for memcpy() or similarly working DMAs in mcus other than STM32). There is no single PUSH or POP register, instead, a whole address range functions as such register - 4kB each endpoint's Tx, and, as you've noted, Rx is shared, so probably reading from any of these address ranges would work; but according toData FIFO (DFIFO) access register maptable you are supposed to read from the same range for given endpoint as you write to.Note, that anything related to FIFO is word-based - you must access it as aligned words, and every register/bitfield related to FIFO size or occupancy is in words (whereas data-transfer registers are in bytes).And finally, mind the erratum which mandates a packet's worth of FIFO being read/written at once, without being interrupted by accesses to some other OTG registers.> 2- What's the minimal initialization needed to start the module aside from enabling clocks, configuring PA11 and PA12 pins to alt-func push-pull outputs with AF10 and enabling the OTG_FS interrupt Nr 67 in the NVIC?You probably figured that out already - clocks have to be set up in RCC and running, beforehand. start by disabling the integrated pullup by setting USB_OTG_DCTL.SDIS, you need to enable PHY by setting USB_OTG_GCCFG.PWRDWN (very weirdly named bit, btw.) and you probably want to force the device mode by setting USB_OTG_GUSBCFG.FDMOD. You'd also need to set up the FIFO sizes. Finally you enable interrupts and the integrated pullup by clearing USB_OTG_DCTL.SDIS.For a more generic startup you may want to do things like resetting the module beforehand, in some "higher" STM32 model you may need to select FS/HS etc.> 3- What to do when these interrupts fire: RESET and ENUM DONE?You may want to read the USB (v2.0) specification. It's quite badly written and not an easy read, but at least skim it through. In this particular case, you may want to look at7.1.7.3 Connect and Disconnect Signalingand7.1.7.5 Reset Signaling.From firmware perspective, the USBRST interrupt is a good place to reset the whole software stack (i.e. remove/reset any outstanding status from previous communications) and to enable EP0 to listen to SETUP packets. The ENUMDNE fire when the FS/HS chirp handshake is over; but in 'F411 is irrelevant as the device speed is there fixed to FS.> I'm getting "Int - Rx FIFO not empty" continously at an astonishing speed!!!The mechanism behind RXFLVL interrupt is weird, more complicated than it would appear at first glance (for reasons both historical and to cater for various configurations of the module, e.g. using the internal DMA (which is not present in 'F411's incarnation of this module)), and the main problem is that it's very inadequately described in the documentation.This interrupt does not indicate "Rx *data* FIFO level". Rather, between the USB core and registers there is a small FIFO conveying events from the Rx portion of USB core to the processor. This interrupt indicates, that this FIFO has some event in it, and you pop these events from this FIFO by reading OTG_FS_GRXSTSP. Read description of this register for Device mode. Popping some of the events triggers other interrupts, see description of the PKTSTS field of this register - for example, poppingOUT transaction completedtriggers the respective OTG_FS_DOEPINTx.XFCR interrupt.JW

author: DELTA67
author_rank: Regular Contributor
timestamp: March 01, 2025, 02:46:26 pm
content: WOW!Thankswekfor your detailed reply. If you have some bare metal code to share, please, it would be a great help.I'm studying the RM while trying the code from the given link above.There are many interrupts and many flags to play with.

author: tellurium
author_rank: Frequent Contributor
timestamp: March 01, 2025, 05:33:46 pm
content: Quote from: DELTA67 on February 26, 2025, 06:58:10 pmDon't do this. It is unreadable, and unmaintainable.Use ST's cmsis header with registers and bits definittions, e.g.https://github.com/STMicroelectronics/cmsis-device-f4/blob/1777d8832e57ec2974afaa9f24dc3ac56bb4cffb/Include/stm32f411xe.h#L7172

author: tellurium
author_rank: Frequent Contributor
timestamp: March 01, 2025, 05:39:45 pm
content: This is an example how to pull ARM core CMSIS and ST's peripheral CMSIS into your project (you need to pull F4, not L4):https://github.com/cpq/bare-metal-programming-guide/blob/35c8cc5c2efdf7dcdc19fbd899ebe93ba6486087/templates/blinky/nucleo-l432kc/Makefile#L26-L30Set include paths, e.g.:https://github.com/cpq/bare-metal-programming-guide/blob/35c8cc5c2efdf7dcdc19fbd899ebe93ba6486087/templates/blinky/nucleo-l432kc/Makefile#L3And from then on, you can include ST peripheral header, e.g.:https://github.com/cpq/bare-metal-programming-guide/blob/35c8cc5c2efdf7dcdc19fbd899ebe93ba6486087/templates/blinky/nucleo-l432kc/hal.h#L10

author: DELTA67
author_rank: Regular Contributor
timestamp: March 01, 2025, 06:04:49 pm
content: Quote from: tellurium on March 01, 2025, 05:33:46 pmThanks tellurium for the remark and the links.Yes, you are right, my writing style is horrible, but I prefer doing so when learning.when writing "OTG_FS->GUSBCFG |= 1<<30; ", later I know immediatly that it's bit30 in OTG_FS_GUSBCFG register.When using a header file the same bit is defined some thing like:#define OTG_FS_GUSBCFG_FDMOD 1<<30;In the 2 cases consulting the RM is necessary. I don't see the difference.

author: langwadt
author_rank: Super Contributor
timestamp: March 01, 2025, 06:22:35 pm
content: Quote from: DELTA67 on March 01, 2025, 06:04:49 pmwith the former you have no idea what bit 30 does, with the latter you get the name of the bit and in a sane editor hovering over the constant with show you that is it bit 30 ..

author: voltsandjolts
author_rank: Unknown
timestamp: March 01, 2025, 06:30:34 pm
content: Quote from: DELTA67 on March 01, 2025, 06:04:49 pmNobody cares what bit it is, because we can't remember what each bit does (and neither will you in a few weeks).Reading and understanding firmware with hardcoded bits is a nightmare.The defined name for that bit gives some information as to the function of it, and in a click you can find the bit number if you so wish.

author: DELTA67
author_rank: Regular Contributor
timestamp: March 01, 2025, 06:52:52 pm
content: voltsandjolts, langwadt and tellurium, sure you are right. My coding style is bad.some times I put more comments like this:Code:RCC->CFGR &= ~(1<<23); // I2SSRC (Bit23) = 0 => PLLI2S used as I2S clockBut it still a bad habit!!

author: tellurium
author_rank: Frequent Contributor
timestamp: March 01, 2025, 10:39:21 pm
content: Quote from: DELTA67 on February 26, 2025, 06:58:10 pmIt is quite easy to adopt. For the above example:Code:#include <stm32f411xx.h>...OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; // Force Device mode- More readable- Reduced chances of mistaking bit / shift count- Reduced changes of making a mistake with bit operations, cause CMSIS bit names are properly parenthesisedThose CMSIS headers exists specifically for that reason, to spare people from writing "1 << 30" things. But maybe you think those bit shifts look sexy, then yeah, the more the merrier

author: peter-h
author_rank: Super Contributor
timestamp: March 03, 2025, 07:39:30 am
content: Nothing wrong with using bit 30 etc so long as you comment it - which you should be doing anyway!

author: wek
author_rank: Frequent Contributor
timestamp: March 03, 2025, 03:37:27 pm
content: > If you have some bare metal code to shareI write most code for money, so I can't "share" it. But there's code galore out there, except that rarely is it in depth explained/commented (mine is not, either, btw.) So, what you should/can do is, to pick any and try to comprehend. Experiment a lot. If in doubts with the details, ask. Don't be afraid to make errors.A USB protocol analyzer is a very instructional tool in such endeavour, I urge you to get some if you mean it seriously. There are cheap LAs and/or scopes with USB analyzer option around; I believe there is a related thread somewhere here.And be prepared to read a lot, even if you won't understand most of that initially, so you are going to re-read it multiple times. Start with the USB chapter in RM and the USB specification.JW

author: DELTA67
author_rank: Regular Contributor
timestamp: March 03, 2025, 04:11:36 pm
content: Quote from: wek on March 03, 2025, 03:37:27 pmThanks wek for the encouragement . I started looking at the code here:https://github.com/trebisky/stm32-hydra/tree/master/usbF4Quote from: wek on March 03, 2025, 03:37:27 pmI'm simulating the STM32F401CE in PROTEUS which has a decent USB protocol analyzer.Quote from: wek on March 03, 2025, 03:37:27 pmI'm reading the RM and taking notes.

author: grantb5
author_rank: Regular Contributor
timestamp: July 24, 2025, 04:31:21 pm
content: I've used stm32_libusb on STM32F103 with good success. I plan on trying it out on STm32F411 as well, since it is also supported.https://github.com/dmitrystu/libusb_stm32

author: Doctorandus_P
author_rank: Super Contributor
timestamp: July 25, 2025, 04:23:18 pm
content: Getting USB to work on any microcontroller from just reading documentation and writing your own code is much more likely to drive you stark raving mad then get you to something that works. And this is regardless to what uC you use. USB is a quite complicated protocol and there are many details which all have to be dust right before anything works. From the initial startup, to "registering" your uC on the USB bus, and getting descriptors and endpoints for higher level protocols.I have dabbed a little toe in this once. I compiled and flashed the serial demo from Satoshinm. It is a bridge that uses an STM32F103 to create 3 UART's, and the project is on github. I flashed it, saw my Linux box recognised three serial ports, and I started a few terminal emulators and echoed data back from one port to another by adding some jumpers on the breadboard with the good old Blue Pill (From back when they were still real). But that was in 2018, and after that... I got distracted.

author: peter-h
author_rank: Super Contributor
timestamp: July 26, 2025, 07:33:01 am
content: I spent weeks of my life making the Cube IDE STM USB code to work properly, with flow control in the VCP.And some aspects were never resolved e.g. when implementing MSC (removable storage device) how do you handle the FLASH programming time? The host performs a 512 byte sector write which takes 15ms to program. The supposed correct way - some sort of SCSI device emulation - is to return a Busy status, which the host will poll (at 1kHz, IIRC) but this doesn't actually work (with Windows), and it turns out that all FLASH sticks just hang in there for the programming time. That seems to have the best compatibility across host operating systems. So I just hang for the 15ms in the ISR...I posted a lot of detail on this a few years ago.

author: paulca
author_rank: Super Contributor
timestamp: July 26, 2025, 10:33:21 am
content: Quote from: peter-h on July 26, 2025, 07:33:01 amWelcome to the "Wizard of Oz" effect of modern "consumer" electronics.The fantasy: Datasheets and technical manuals.The reality: Dirty unfinished, but cheap hacks and the consumer has no idea.Anyone can build an electronic circuit, but it takes an electronics engineer to make one that barely work, but can no longer be budget squeezed into any more dirty hacks and still work.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 26, 2025, 03:26:47 pm
content: Quote from: peter-h on July 26, 2025, 07:33:01 amYep. As a more or less general rule, never consider any code provided by MCU vendors as production-ready. It very, very rarely is.Did you consider switching to TinyUSB? Maybe not perfect, but I think it's already eons better than the USB "middleware" provided by ST.

author: peter-h
author_rank: Super Contributor
timestamp: July 26, 2025, 05:21:22 pm
content: I did but by then I had the ST version pretty well working.I think the main problem is that ST has no contact with users, other than huge automotive customers with direct accounts.In the 1980s I had direct accounts with a few firms (e.g. Zilog) and saw just how differently that works.The ST forum is almost totally dysfunctional, with a few dedicated individuals helping people (but mostly with one-liners), with some very clever but extremely rude people (the famous "Piranha" being one) not helping anybody except by accident or by posting "treasure hunt" clues, and with with ST employees on it having no apparent influence in the company. And of course most commercial (as opposed to hobby or academic) users are just leechers who never feed back working code, so most of the stuff supplied with e.g. Cube IDE will never be ready to use. Exceptions were FatFS and FreeRTOS.