October 1, 2017

The STM32F103C8T6 and USB

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.

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.


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