March 20, 2025

Black Pill boards - F411 USB -- running the HS usb controller

Many of the more advanced F4 chips have two usb controllers. One is the FS controller. It is just like the one in the F411. It works fine and you find it at the same address using the same interrupt numbers.

There is also a HS controller -- at a different base address and using IRQ 77 rather than IRQ 67. So far I have not had any luck getting it to work. The code I have definitely supports it.

The code has a number of compile options related to the HS controller. Before I dive into those, I should note that it is not possible to use the HS controller in HS mode without an external HS Phy chip. The F407 and F429 have an internal FS Phy for this second interface. None of my boards have an outboard HS Phy. This means I won't be able to use the second usb controller in HS mode. I can use it in FS mode using the internal Phy, and that is just what I am trying to do.

I should mention as a quick side note that this second HS controller is "better" even in FS mode. It supports more endpoints and has more FIFO ram, and probably other advantages I don't know about yet.

Many of the compile options show up in driver/usb_core.c and are referenced in USB_OTG_SelectCore (). Such as:

#ifdef USB_OTG_ULPI_PHY_ENABLED
#ifdef USB_OTG_EMBEDDED_PHY_ENABLED
We would expect one or the other of these to be defined.
I see this in driver/template.h as follows -- but we don't actually include template.h !!
 //#define USB_OTG_INTERNAL_VBUS_ENABLED
 #define USB_OTG_EXTERNAL_VBUS_ENABLED

 #ifdef USE_ULPI_PHY
  #define USB_OTG_ULPI_PHY_ENABLED
 #endif
 #ifdef USE_EMBEDDED_PHY
   #define USB_OTG_EMBEDDED_PHY_ENABLED
 #endif
 #define USB_OTG_HS_INTERNAL_DMA_ENABLED
 #define USB_OTG_HS_DEDICATED_EP1_ENABLED
What I do see is a top level file usb_conf.h where such things are defined. And it needs some work -- things from template.h need to be copied in.

Notice that now USB_OTG_HS_DEDICATED_EP1_ENABLED is defined. This means that I need to uncomment some code I thought I would never care about This will also require setting up some other interrupt vectors.

Also, there is some compile problem in vcp/usbd_cdc_core.c when I define USB_OTG_HS_INTERNAL_DMA_ENABLED. It looks for usbd_cdc_Desc, which is some missing enumeration descriptor -- so I switch this off. I can investigate this later once I get things working.

All of this is important, but as it turns out is not the issue that was causing things not to work.

Aha!! Now it works!

The trick is some fine print in the TRM. When the HS controller is in FS mode, you configure the GPIO pins with alt function 12 rather than 10. This is a footnote to the figure on page 272 of the TRM. Once I change this, it enumerates just fine! When I send data, there is a problem -- I get never ending interrupts of this sort:
USBint - OUT Endpoint
OTG ISR status: 00080008
I add the code for the special endpoint 1 interrupt handlers and now I get never ending messages:
USB HS ep1 out interrupt
This is nice progress. Actually when I get endless messages from an interrupt I need to figure out how to cancel, I am usually right on the edge of a breakthrough. Now I actually connect the calls in usb.c to the routines in driver/usb_dcd_int.c and it all works!

I can send test packets now (which of course go to endpoint 1.

Wow!

It also works on the F429 discovery board

This board has two usb ports (FS and HS) just like the F407 board I have been working with, but only the HS port is wired to a connector. Actually not a bad choice if you are only going to wire up one of the two. I could never make this work before. Now it works!

I just plugged in a plain ordinary micro-USB cable. Then I rebuilt Hydra for the disco, flashed the new code (using "make flash") and it enumerated! My test sending data worked also, but I needed to make a small change to my C program so it would write to /dev/ttyACM1 rather than 0 (the STlink port on the disco had already claimed ttyACM0.

This is a nice thing to have checked off my list, and it is nice to know that just a plain old micro-USB cable works for this OTG port. It could also act as a host, but that would be a project for another day, and not very soon.

How fast?

I sent 10M as a bunch of 512 byte writes. It took 103 seconds. So I send 1M in 10.3 seconds or 99.4 Kbytes per second. 994 Kbits/second. USB full speed should be 12 Mbits/second. We should be able to get 1.2 Mbytes per second -- so we are getting about 1/10 of the speed that FS is capable of.

On the other hand, compare our nearly 1000 Kbits per second to 115.2 Kbits using our serial port at 115200 baud.

But it works, and we had no trouble or errors sending 10M bytes of data.

Summary

One tiny thing can cost days of work. This is a lesson some people don't understand when they want to toss out tested and proven software. Certain things in that software they despise were earned, purchased by someone by way of worry, work, blood, and above all perseverance.

I could go back now and investigate this "DMA ENABLED" flag -- but I won't do that right away.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org