December 6, 2020
USB - Minimal code for the STM32F411 USB hardware
I have learned enough to try something.
Now that I know that the host will reset a USB device and
then begin firing information requests at it, I can put
together a minimal bit of code to enable the USB device
and report to me when something (anything happens).
I am doing all this in the context of my "hydra" project.
So here is the game plan to get started:
- Add 2 USB interrupt entries to locore_411.s
- Put together a skeleton usb_411.c
- Enable USB clock(s) in rcc_411.c
- Add definitions for USB hardware registers.
- Add and call usb_init().
Clocks
We scan through the reset and clock enable bits in the F411 RCC.
I miss the reset and clock control bits on my first scan
because they are labeled OTGFS not USB.
Register 0x14 has a bit to reset the OTG_FS (as they like to call it).
Register 0x34 has a bit to enable the OTG_FS clock.
Register 0x54 has a bit to enable the OTG_FS clock in low power sleep mode.
The clock diagram shows a PLL48CK signal that is controlled by a gate.
I have not yet found this control bit.
Registers
The memory map in section 2.3 of the RM shows the USB OTG FS registers
at a base address of 0x5000_0000, as though the USB silicon is a special
part of the silicon apart from the other registers. And perhaps it is.
This is unlike the F103 which has a second address for the dedicated
USB ram.
The reference manual information for the device mode registers is totally
screwed up and a mass of contradictions. The only way to sort the mess out is
to look at some reliable source code.
Give it a whirl
I put together a start at a register template, then run the code.
I read the CID register and see the following:
USB cid 5000003C 00001200
The value of 0x1200 is what is expected, so I have the addressing correct
and the clock turned on, which is a nice start.
After several hours studying registers, I was able to generate a USB interrupt.
Doing this requires the following steps, many of which are the same for any
on-chip device that needs to interrupt.
- Ensure that the processor has interrupts enabled (it does).
- Code up a small interrupt handler function in C
- Add a pointer to that routine to the vector table.
- Enable the IRQ for the USB FS device in the NVIC
- Set the global USB interrupt enable bit in the USB core config register.
- Set the bit for the chosen interrupt in the USB interrupt mask register
At this point the interrupt is fully armed (and may trigger immediately if it is
already pending). I chose the MMIS interrupt (mode mismatch) as I can trigger it
by any access to a host register. And I do trigger it by reading the host CSR
register.
At this point code will enter a furious interrupt loop unless the interrupt handler
does the appropriate thing do clear the interrupt. For the MMIS interrupt, the
appropriate thing is to write a "1" to the MMIS bit in the USB interrupt status
register. I do just that and now my test gives me a single message when
I attempt a read from the host CSR register.
Note that many USB interrupts are cleared simply by reading the interrupt status
register.
This concludes my first experiment with the STM32F411 USB hardware.
Feedback? Questions?
Drop me a line!
Tom's Computer Info / tom@mmto.org