So far, to upload new code to a Blue Pill, you had to change a jumper, press
reset, perform the upload, change the jumper back, and press reset again -
this will quickly become very tedious!
So why not let the host computer do this?
What we need is a way to control the RESET and BOOT0 pins of the ARM µC. Reset
is easy, and can be done in way similar to the Arduino: tie it to DTR (“Data
Terminal Ready”), and have
the host computer control this “modem control” pin.
On the wiring side, all we need to do is to make a connection between the FTDI’s
DTR pin (sometimes marked “RST”) and the “R” pin on the Blue Pill header.
Controlling BOOT0 is slightly more involved. What we can do is use another
modem control output pin for this, called RTS (“Request To Send”). This is not
normally connected to an FTDI pin, but it’s not very hard to modify a
BUB to do this as well.
The problem though, is that apart from not having these pins available on all
USB serial adapters out there, we can also run into nasty issues w.r.t. platform
support. Apple’s FTDI adapter does not support RTS, and at least some
Windows and Linux versions have issues.
Getting DTR and RTS right on all platforms and making it work with various
USB serial adapters turns out to be fairly tricky, to put it mildly.
But there’s a simple solution, and it works really well.
Ingredients: one Blue Pill, one 6-pin female header, and one firmware
download.
What we can do, is to move the problem to a place where we can solve it:
an implementation of a USB-serial bridge, based on an F103. After all, from a
µC we can easily control a few pins, switch to even parity, and do whatever
else is needed to program an attached “target” board.
The trick is to stay away from everything which is specific for any platform,
and to only use the USB serial connection for just that: serial data. One way to
accomplish this is to bring in an old-but-proven serial port control mechanism,
called Telnet. This has an escape
convention to send special messages in between the normal data flow. The escape
byte is 0xFF, which does not normally occur in plain ASCII exchanges (and it’s
properly wrapped in case it does).
So the idea is that instead of this setup:
![]()
… we switch to this approach:
![]()
The Folie utility defaults to the Telnet
protocol (with the “-r” command-line option to fall back to raw mode where
needed). All we need is that “smart µC-based serial” thing to make it work.
And that’s exactly what
SerPlus
is. An implementation for F103 boards, which does the required interpretation
and translation between Telnet escapes and the real world (it only handles a
subset, not the full Telnet protocol). Folie and Serplus were made for each
other.
To create this setup, you need a spare Blue Pill (you might as well have a bunch
of them lying around anyway, if you plan to follow along with some of the
upcoming projects on JeeLabs), and you need to build a little setup similar to
this one:
![]()
The connections are (bottom view, FTDI header, top to bottom):
- GND = power, ground
- RTS = tied to a spare I/O pin, i.e. PA2
- +5V = power, 5V in
- TX = tied to µC’s TX, i.e. PA9
- RX = tied to µC’s RX, i.e. PA10
- DTR = tied to a spare I/O pin, i.e. PA3
Note that this is not the same as when connecting a male 6-pin FTDI header
to a Blue Pill to use it as target board. The connector is flipped, there’s no
RX/TX cross-over in the above connections, and the DTR/RTS are not tied to
RESET/BOOT0, but to pins which will be used as GPIO outputs to control the
target board.
Now the chicken-and-egg part of the story: to program this board, you still
need another USB-serial adapter. It could be another SerPlus board, but it’ll
most likely be some other type. Not to worry - just follow the wiring setup
instructions given in a previous article. If you plan to do
this more than once, you could also consider creating an “FTDI cross-over plug”,
like this:
![]()
Nothing fancy, just a way to streamline things. For one-off’s, four jumper wires
will be quicker.
Just to make things clear: we’re about to program a board and turn it into a
“SerPlus”, i.e. a variant of a host-side USB serial interface, just like a BUB
or some other FTDI-like board. The difference being that this one will have
slightly more smarts, to handle Folie’s Telnet escapes.
Last step: flashing the SerPlus code into the Blue Pill we just prepared:
- you can get the code from
GitHub
and build it yourself
- … or just grab the compiled binary, called
serplus.bin
- then upload that to the Blue Pill, in the same way as with the
blink
, etc.
examples
SerPlus is considerably more complex than the other examples so far, but it’s
built in exactly the same way, with the same GCC and the same libopencm3
.
There is a USB driver in there, as well as an interrupt-driven serial port, a
SysTick timer, and of course a Telnet protocol decoder (implemented as a small
finite state machine). Its main function is to get incoming data from USB to
serial, and get incoming serial data to USB - just like any USB-serial board.
But we’re not out of the woods yet. We’re only half-way in fact…
The last step is to make the target Blue Pills suitable for this new “extended”
use of the FTDI header. We need to add two things: a connection from DTR to the
F103’s RESET pin, and another one from RST to the F103’s BOOT0 pin:
![]()
Almost there! - the one remaining issue, is that we can’t control the BOOT0
pin while there’s a jumper on it, and that without the jumper, the F103 may not
start up properly in all situations.
A small “mod” will be required - in the form of an added 10 kΩ resistor where
the jumper was:
![]()
This will make sure that BOOT0 is pulled low by default, but that the RTS wire
can pull it high when needed to start an upload.
The result of all this work is TWO Blue Pills, a target (on the left), and a
SerPlus (on the right):
![]()
Plug them together, connect a USB cable on the right, and you get a very
convenient setup:
$ cd ../blink
$ folie
Folie v2.7-1-g94cba5e
Select the serial port:
1: /dev/cu.Bluetooth-Incoming-Port
2: /dev/cu.usbmodem3430DC31
? 2
Enter '!help' for additional help, or ctrl-d to quit.
[connected to /dev/cu.usbmodem3430DC31]
!u blink.bin
724b .+V22 #0410 R .W .E writing: 3/3 done.
(hit ctrl-d
to exit Folie)
Note the absence of the -r
option this time: we’re now using Folie’s default
Telnet protocol, not raw mode. Resets and uploads can now be done without
touching the Blue Pill at all.
And since the blink demo is built into Folie, you don’t even need to get
a copy of blink.bin
:
!u
These firmware images are built-in:
1: F103-BMP 50096b crc:F87A
2: F103-Blink 724b crc:4967
3: F103-Mecrisp 20500b crc:A585
4: F103-SerPlus 7052b crc:7DD0
Use '!u <n>' to upload a specific one.
!u 2
724b .+V22 #0410 R +W +E writing: 3/3 done.
Here’s another upload, of the echo
example this time:
$ cd ../echo
$ make
CXX main.cpp
CXX echo.cpp
LD echo.elf
OBJCOPY echo.bin
text data bss dec hex filename
28528 2220 64 30812 785c echo.elf
$ folie
Folie v2.7-1-g94cba5e
Select the serial port:
1: /dev/cu.Bluetooth-Incoming-Port
2: /dev/cu.usbmodem3430DC31
? 2
Enter '!help' for additional help, or ctrl-d to quit.
[connected to /dev/cu.usbmodem3430DC31]
!u echo.bin
30748b .+V22 #0410 R .W .E writing: 121/121 done.
Hit <enter> to toggle the LED (0) ...
Hit <enter> to toggle the LED (1008) ...
Hit <enter> to toggle the LED (2020) ...
!reset
Hit <enter> to toggle the LED (0) ...
Hit <enter> to toggle the LED (1008) ...
Hit <enter> to toggle the LED (2020) ...
As you can see, the upload was performed, the program was started, and since
Folie then switches to terminal mode, the output immediately appears.
Furthermore, hitting CTRL-C
generates a reset of the target board, at which
point it restarts from scratch.
For people used to the Arduino workflow, this is nothing spectacular - it’s the
way the Arduino IDE has always worked (and it’s probably partly responsible
for its runaway success).
And now, the same can be done on ARM: a single USB cable for uploads, for
talking to the running application, and for supplying 5V power. Mission
accomplished – onwards!