USB serial in Forth, progress! May 2016

A while back, an article was posted about the lack of USB on STM32F103 µCs, when it comes to Mecrisp Forth, that is. Unfortunately, getting the built-in USB device-mode hardware working is quite a challenging task. Not only is the USB protocol fairly complex - the actual USB interface hardware on that particular model STM µC is in fact ridiculously messy!

It has all the appearances of a rushed-to-market design, just to get that USB feature shipping!

Fortunately, there are a number of solid working implementations for this hardware, including the libopencm3 and ChibiOS code (both in C), as well as an older (tentative?) implementation in Forth by Eckhart Köppen. This last one has turned out to be the catalyst to move forward on implementing a serial-over-USB driver for (and in) Mecrisp Forth.

First the good news - it works! - even though there are still a few quirks:

$ folie -p /dev/cu.usbmodemC934CC31
Connected to: /dev/cu.usbmodemC934CC31
1 2 + . 3  ok.

USB output appears to work flawlessly, and is a lot faster than the normal serial console’s 115,200 Baud setting (this is not surprising, given that the USB full-speed rate is 12 MHz).

A few important bugs still remain as of this writing (May 2016):

The first problem is very unfortunate: it means that Folie can’t be used to upload source code over USB yet. The second problem is more benign, but will become important for unattended use (i.e. powering up and running when no USB host is listening).

The problem with the dropped input is probably related to the lack of back-pressure, i.e. Mecrisp has to let the host know when it can or cannot accept more incoming data. The stalled output is probably solvable by keeping track of the attach state, and then simply dropping all output when there is no active connection (just like a serial port, there is not much else we could do anyway).

It’s important to note that USB is a host-driven protocol. Devices may only respond when the host polls them - including data from device to host! Luckily, the hardware takes care of that.

The new USB driver implementation is 100% pure Forth and can be found on GitHub. At ≈ 350 lines of code, it’s quite long and far from readable or optimised. The main tasks of this code are:

The result is the session shown above. The current implementation, with some boilerplate code not strictly required for USB, needs ≈ 9 KB of extra flash memory. With the extended Mecrisp “RA” core image, the entire build fills just under 29 KB of flash memory. That still leaves over 32 KB of flash for application use, on even the smallest and very common STM32F103C8 boards.

Note that there is a small dependency on the type of board, since each board has a different way of tying the 1.5 kΩ resistor onto the USB lines, as needed for the reset phase of USB signalling. On a HyTiny, the PA0 pin is handled by this code. Slight mods will be needed for other boards.

With a warm thank you to Matthias Koch, Mecrisp’s author, for his tips, support, and continued encouragement to get all this going. Although the current code is not quite ready for prime time and still needs a major cleanup round, that “only-USB” moment sure is getting a lot closer now!

Weblog © Jean-Claude Wippler. Generated by Hugo.