March 15, 2025

Black Pill boards - F411 USB -- USB endpoints, part 1

With Cscope now available as a tool, my intent is to look more deeply at the F411 driver code, with particular attention to USB endpoints. A look at USB endpoints could be divided into 3 topics -- initialization, writing, and reading.

Other members of the F4xx family have two USB interfaces. In devices I have looked at (the F429, F407, and even F7xx parts) one interface is OTG FS and the other is OTG HS. In the F411 we have only OTG FS. To actually use the OTG HS interface in HS mode (480 Mbit/s) requires an external Phy chip and none of my boards have such a thing. So the OTG HS interfaces is used with the internal FS Phy.

The FS and HS interfaces are quite similar, in fact the driver we are looking at supports both. But they are different. The TRM devotes a separate chapter to each. The FS interface gets 151 pages and the HS interface gets 163 pages. One interesting difference between the two regards endpoints.

The FS interface has a control endpoint and 3 IN/OUT endpoints.
The HS interface has a control endpoint and 5 IN/OUT endpoints.

The FS interface has 1.25K of dedicated FIFO ram.
The HS interface has 4K of dedicated FIFO ram.

And as a further note, the STM32F743 has only the HS controller (but now two of these HS controllers rather than a FS and a HS). This one has a 4K FIFO, but now with 8 IN/OUT endpoints.

No doubt there are other differences.

Take a look at our driver code

I find it interesting to use the linux "wc" command to look at file sizes and in particular the number of lines in each file in the "driver" directory:
tom@trona:/u1/Projects/STM32/Hydra/usbF4/driver
wc *.c *.h
  2111   6205  58172 usb_core.c
   406   1069   8428 usb_dcd.c
   794   2224  21654 usb_dcd_int.c
   234   1105   9370 usb_conf_template.h
   345    889  11419 usb_core.h
    87    197   3145 usb_dcd.h
    54    147   1898 usb_dcd_int.h
   132    327   4804 usb_defines.h
  1102   2490  19850 usb_regs.h
  5265  14653 138740 total
So we have 5265 lines of code and the biggest file is usb_core.c, followed by usb_regs.h. We see the C source files partitioned into 3 files. Let's take a look at what is in each and see if we can discover the scheme behind this.

usb_dcd_int.c

This has only one public entry point:
USBD_OTG_ISR_Handler()
It is called from "glue" routines in usb.c to handle both FS and HS interrupts.

usb_dcd.c

This contains the following public entry points:
DCD_Init ()
DCD_EP_Open ()
DCD_EP_Close ()
DCD_EP_PrepareRx ()
DCD_EP_Tx ()
DCD_EP_Stall ()
DCD_EP_ClrStall ()
DCD_EP_SetAddress ()

DCD_EP_Flush () -- never called
DCD_DevConnect () -- never called
DCD_DevDisconnect () -- never called
DCD_GetEPStatus () -- never called
DCD_SetEPStatus () -- never called
These all seem well named.
For the most part they are called by code in library/usbd_cdc_core.c, library/usbd_ioreq.c and more rarely in library/usbd_core.c
They call routines in driver/usb_core.c (or deal with hardware directly themselves).

usb_core.c

This is the big file (2111 lines)
USB_OTG_WritePacket ()
USB_OTG_ReadPacket ()
USB_OTG_SelectCore ()
USB_OTG_CoreInit ()
USB_OTG_EnableGlobalInt ()
USB_OTG_DisableGlobalInt ()
USB_OTG_FlushTxFifo ()
USB_OTG_FlushRxFifo ()
USB_OTG_SetCurrentMode ()
USB_OTG_GetMode ()
USB_OTG_IsDeviceMode ()
USB_OTG_IsHostMode () -- never called
USB_OTG_ReadCoreItr ()
USB_OTG_ReadOtgItr () -- never called
USB_OTG_InitDevSpeed ()
USB_OTG_CoreInitDev ()
USB_OTG_EnableDevInt ()
USB_OTG_GetDeviceSpeed ()
USB_OTG_EP0Activate ()
USB_OTG_EPActivate ()
USB_OTG_EPDeactivate ()
USB_OTG_EPStartXfer ()
USB_OTG_EP0StartXfer ()
USB_OTG_EPSetStall ()
USB_OTG_EPClearStall ()
USB_OTG_ReadDevAllOutEp_itr ()
USB_OTG_ReadDevOutEP_itr ()
USB_OTG_ReadDevAllInEPItr ()
USB_OTG_EP0_OutStart () -- called from library/usbd_ioreq.c and library/usbd_req.c
USB_OTG_ActiveRemoteWakeup () -- never called
USB_OTG_UngateClock () -- never called
USB_OTG_StopDevice () -- never called
USB_OTG_GetEPStatus () -- called from dcd routines that never get called
USB_OTG_SetEPStatus () -- called from dcd routines that never get called
This has a number of routines for Host mode that I commented out and do not list above. These routines get called from within usb_core.c iself, or from driver/usb_dcd_int.c and sometimes from driver/usb_dcd.c. With only one exception, these routines are all called from code within the "driver" directory.

The exception is USB_OTG_EP0_OutStart() which gets called from both library/usbd_ioreq.c and library/usbd_req.c

How about those header files?

Only 3 of the 6 are used outside the driver directory. Those 3 (usb_core.h, usb_dcd_int.h, and usb_dcd.h) are used in the library directory. The other 3 are as follows:
usb_regs.h  -- template for the hardware registers
usb_defines.h -- lots of general definitions
template.h - never used, but instructive
The include files have complex interdependencies. Beware.

Conclusions

Making lists like this is tedious, but highly instructive.

With only one exception, the file usb_dcd.c is the "outside world" interface to this driver code. The one exception is EP0_OutStart() and I would probably have placed an intermediate routine in usb_dcd.c for this.

Having composed this analysis, I find myself now eager to do the same for the vcp and library directories.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org