October 29, 2023

The STM32F103C8T6 and USB

It has been 6 years since I first got interested in this. I did a little bit of poking around back in 2017, then got busy with other things.

Nasty "Cube" code from ST

Back in 2017, the only USB source code that was available was the miserable and wretched mess offered by ST as "CubeMX" or "STM32CubeF1". It is hard to even begin to describe how miserable this code is. It was used by the DFU boot loader as well as the Maple library. Here is what the Readme in the Papoon library says:
ST's HAL and LL libraries (and their inclusion in the "CubeMX", etc. IDEs) are bloated, inefficient, convoluted disasters masquerading as official, vendor-vetted software.

As "insane" as the current ST Cube/HAL/LL code is, the older "STM32_USB-FS-Device_Lib" is much worse. Again I invite readers to see for themselves, but basically the code defies attempts at static analysis.

I find it gratifying to find someone else saying things like this about it.

No documentation from Synopsys/Designware

Beyond this disaster of bad code, another disaster, at least as severe, lurks. This is the lack of good documentation. Many of the STM32 microcontrollers use a USB subsystem designed by Synopsys, and in many places refered to as the Designware USB controller. Any device that advertises "USB OTG" almost certainly uses the Designware controller. Documentation for this is unavailable to mere mortals. Hence if you want to write your own low level code, you will need to reverse engineer existing drivers (such as drivers with names like "dw_usb.c" in the linux sources). Here "dw" is short for "DesignWare". If you are working with OTG or USB 3.0, you are almost certainly working with the designware controller. But the STM32F103 has not stepped into this arena.

The F103 USB device only interface

We may be OK, as the F103 does not seem to use a designware USB controller.

The STM32F103 has a USB-2.0 "full speed" (12 Mbps) controller that is device only. The section on this in RM0008 (the 1136 page reference manual) is section 23, from pages 622 to 651. This seems suspiciously short, but it may all be there in a mere 30 pages!

The datasheet shows the USB registers at 0x4000_5c00 and USB/CAN sram (512 bytes) at 0x4000_6000.

New USB projects in 2023

Now in 2023, there are at least 2 more civilized options for a USB library that are at least worth taking a look at:

The Papoon project (aka "not insane" USB) looks like nice code, but as it is C++ I doubt that I will ever do more than study it. It is strictly for the STM32F103.

The Tiny USB project is more general, and is all good old C. It supports a long list of microcontrollers, including quite a few different STM32 variants and the RP2040.

I am much more likely to begin my foray into low level (bare metal) USB with the RP2040. This gives me simple and easy to run and study examples using TinyUSB. The bootrom uses TinyUSB and there is full source code for that. And there is very nice documentation.

Notes from 2017

I have decided to make a project of writing USB code for this device. This will be my first foray into USB programming, and I intend to make this writeup somewhat of a tutorial. It could be entitled "USB from scratch", since I intend to write all of the code from the bottom up.

As shipped, these devices do not support USB in any way. There are USB boot loaders available for these devices, and you can flash a USB boot loader into them (using OpenOCD or the serial loader). After doing this, you can then use the USB loader to load subsequent software. I have done this (see below) not because I actually want to use the USB loader, but so that I can study it as an example of USB programming.

The way I use the STM32 is to simply use OpenOCD and a STlink-V2 to load my software via the SWD interface and find that wonderful. The Arduino gang seems to like the USB approach.

Note that the Roger Clark bootloader might be a good information source for USB programming. Or maybe not -- it is based on the wretched ST/Cube USB drivers.

Goals and first thoughts

My goal is to write code to run in the STM32 that will provide a serial console using the native USB. I intend if possible to mimic some existing device like the FTDI or CP2102 so that the STM32 won't require some kind of special driver or configuration file and will "just work" with existing drivers.

Resources

As mentioned above, there are various open source USB bootloaders that may provide code worthy of study. I have not yet looked into this, sometimes other people write wonderful code and sometimes they don't.

I also have a book that has been on my shelf for years, "USB Complete, fourth edition" by Jan Axelson. This book is now out in a fifth edition. I find the book to be OK, with my main complaint being that he talks only about the Windows operating systems as a host. This is generally not a crucial issue and can just be ignored. The real problem with this book is that it never gets down and dirty and talks about the actual traffic on the wire. You can use this book for high level concepts, then go online for nitty gritty details:

Despite the title, "USB in a nutshell" is not an O'Reilly book. Nonetheless it seems to be a superb online resource.

The reference manual for the STM32F10x series discusses the USB hardware in section 23 on pages 625 to 657.

The STM32F103C8T6

I am using the amazing boards available from China for just under $2.00 for this project. These are sometimes referred to as the "blue pill" boards. The ARM chip is a Cortex M3 that can run at 72 Mhz. There is 64K (or 128K) of flash and 20K of ram available. These boards are entirely supported by GCC and I do all of my development using vim and make from the command line. No IDE programming here, and no I am not sorry.

The datasheet says we get USB 2.0 at "full speed". The USB subsystem needs to be fed a 48 Mhz clock. Full speed is 12 Mb/s. Some USB 2.0 supports "high speed" at 480 Mb/s, but not this device. This is hardly an issue for the serial console I have in mind.

Linux USB monitoring and debug

I thought a good starting point might be to plug in some USB devices and watch the traffic as they identify themselves to the system. So I want some kind of USB debugging tool.

The word is that my old friend Wireshark can do this.

USB speeds

We have USB 1.0, 2.0, and 3.0 with different electrical issues. As we learn about the protocols, we find that some attention needs to be paid to changes with each protocol, and with different speeds. At this time, it would seem that there are four speeds to keep in mind: The STM32F103 does full speed (and presumably slow speed also). This is certainly fast enough to handle a console. Slow speed ought to handle even 115200 baud.

But consider that if we develop a "pure USB" console device, baud rates will be irrelevant and we won't be passing data through any kind of serial bottleneck as we do with a USB to serial gadget. Not that 115200 baud has ever proven to be any kind of limitation, but at "Full speed" we could be running at 12,000,000 baud - ignoring USB protocol overhead.

Lessons learning in my first week with USB

First of all, the low level USB details are taken care of by the USB interface hardware. It will be interesting at some point to learn about other USB devices, but I expect them to be much the same. I never have to deal with token packets and low level transactions, so reading all about them is more or less wasted time, although interesting from the point of view of understanding how USB works.

This helps to explain why wireshark does not show literal "on the wire" packet traffic. That sort of detail is completely hidden. The only way to see it would be to have some kind of hardware protocol analyzer, and I actually don't think that would serve any purpose for me anyway. It might be the sort of thing a person designing actual USB chips might need.

At the level we work at, we set up receive and transmit buffers for each endpoint, then wait for interrupts to inform us that data has been received or sent. The business of wrapping them up as USB transactions is taken care of by the USB silicon.

DFU boot loader

I grabbed Roger Clark's code from github and tweaked the Makefile (which was easy) to compile under my environment.

Hardware USB snooping

You can buy really expensive gadgets to do this. Since the actual USB traffic on the wire is invisible to most software (such as wireshark), this could be useful, but requires some homebuild hardware.
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org