All of the buildings, all of those cars
were once just a dream
in somebody's head
Mercy Street - Peter Gabriel


11 minutes read

Pic 1


I got interested lately in the stm32 range of micro-controllers. It’s amazing what you can get these days for only a few Euro’s.

One of the more famous ones must be the Blue Pill that gives you an STM32F103C8T6 (32 bits Cortex M3) running at 72 MHz, 64KB Flash and 20 KB RAM (compared to the 8 bits controller running at 16 MHz, 32KB Flash and 2KB RAM of the Arduino Uno). Ready to use development boards can easily be found for as cheap as 2 Euro at the usual places. That’s a lot more horsepower than a typical Arduino Uno for a considerable lower cost.

Those are typically programmed via an STLink which are equally cheap and can be found at the same places. However, I decided to try out the Black Magic Probe (BMP) from 1BitSquared. It’s considerable more expensive but it has some advantages I think over the STLink which I copied here from 1BitSquared:


  • GDB server port without the need of special PC side software.
  • TTL level serial interface.
  • SWD and JTAG support.
  • Supports 1.7V up to 5V targets.
  • Can provide 3.3V to the target (up to 100mA).
  • Semihosting support.
  • Works on Linux, Mac and Windows.
  • Works with Eclipse and other Integrated Development Environments.
  • Supports STM32, LPC11, LM3S, …
  • DroneCode compatible.

For me, the first 2 were the most interesting. Having the GDB server running on the BMP means you don’t need special drivers. And having a serial interface on the same board means I don’t need a separate serial to USB converter (and an extra USB port to plug it in). The full documentation for the BMP can be found here.

Note: It’s possible to flash a Blue Pill to run the BMP code. How to do so can e.g. be found here. So for 2 Euro, you can have your own BMP clone.

I decided to try out the PlatformIO development environment as well. Since not much could be found on this particular combination, I decided to write up how I got this to work. I’ll assume you have the platformIO extension successfully installed in Visual Studio Code.

Which ports are used by Black Magic Probe

We need to know the serial ports assignations of the GDB server and the serial port on the BMP. This is important to correctly fill in the ports later for the platformio.ini file of your project. I’m using Windows 10 here, so this will be in the form of COMxyz ports (on Linux or macOS, these will be paths to an USB port, e.g. /dev/cu.uart-1CFF4676258F4543). And here I ran into trouble. The BMP documentation states:

When connected via USB, the Black Magic probe will enumerate as a CDC-ACM device which the OS should present as a TTY device or serial port. The first interface provides the GDB server, and the second provides a USB to UART adapter.


On Windows, when you first connect, the Black Magic Probe should be detected as two COM ports. The first COM port is the GDB extended remote server and the second one is USB to Serial adapter on the back of the board. To find the allocated ports, check the Device Manager:

Device Manager.

But at the same time:

Windows 10 displays the BMP probe ports using the generic title, “USB Serial Device”, as seen below:

Device Manager on Windows 10.

PlatformIO can help us to detect which COM ports are in use as well. Open a terminal in PlatformIO and type in platformio device list. I got this:

C:\...\cocoacrumbsWebsite>platformio device list
Hardware ID: USB VID:PID=1D50:6018 SER=6 LOCATION=1-4:x.2
Description: USB Serial Device (COM3)

Hardware ID: USB VID:PID=1D50:6018 SER=6 LOCATION=1-4:x.0
Description: USB Serial Device (COM4)

Instead of typing in platformio, we can use the shorter pio. Thus pio device list will work as well

As we can see, COM3 and COM4 are in use. But again, we don’t see which port is used for the GDB server and which one is used for the serial terminal.

One could be fooled that COM3 is the first device and is used for the GDB server connection. However, this turned out to be wrong. The key is in the LOCATION part of the Hardware ID:

  • LOCATION=1-4:x.0 -> first device -> COM4 -> GDB server.
  • LOCATION=1-4:x.2 -> second device -> COM3 -> Serial terminal.

Armed with this knowledge we know enough to fill in the platformio.ini file for a first test project.

Setting up a simple project

As a simple Hello World project, we’ll just let blink the LED and write the ON/OFF state to the serial port.

First we need to create a new project. Click on the New Project button of the PIO home page.

Creating the Blinky project.

After which, this window pops up.

Creating the Blinky project.
  • Give your project a name (like Blinky in this case).
  • Click on the pop up menu for the board and search for BluePill F103C8 (Generic). Hint: you can start typing in Blue and the list of boards will shrink to show only boards that start with the Blue prefix. Since there are over 700 boards supported at the moment of writing, this will save you quite some time.
  • For Framework, Arduino should already automatically be filled in after you have selected the board.

Then hit the Finish button. If this is your first project, it might take a while to set up your project since PlatformIO downloads and installs the needed binaries (e.g. compiler and linker) for you. After a while you should see something similar to this.

Creating the Blinky project.

A default platformio.ini file was created for you:

;PlatformIO Project Configuration File
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
; Please visit documentation for the other options and examples

platform = ststm32
board = bluepill_f103c8
framework = arduino

This is, however, not usable for my set up and we need to make some changes. We need to tell PlatformIO which COM port to use for the Debug Server and which COM port for the Serial Monitor. Since we already figured out which is which we can add this already to the platformio.ini file:

debug_port   = COM4
monitor_port = COM3

Next to that we need to tell PlatformIO that we use a Black Magic Probe instead of the default STLink to upload our code:

upload_protocol = blackmagic

And the same for the debug protocol:

debug_tool = blackmagic

While we’re at it we can increase the speed of the serial terminal from its default/slow 9600 baud to a faster 115200 baud as well.

monitor_speed = 115200

Your platformio.ini file should now look like this:

platform  = ststm32
board     = bluepill_f103c8
framework = arduino

upload_protocol = blackmagic    ; SWD interface. Will automatically detect COM port

debug_tool = blackmagic
debug_port = COM4

monitor_port  = COM3
monitor_speed = 115200          ; Default 9600

Wiring everything up

The Debug/Upload port of the Black Magic Probe is connected via a 10 wire cable to a 10 pin to 7 pin JTAG/SWD adaptor board. Since the Blue Pill interfaces via SWD we need to wire up the following pins:

BMP (7 pin connector) Blue Pill

Since a picture says more than a 1000 words, the following pictures might help:

BMP Upload connection.
Blue Pill Upload connection.

Now we connect the wires for the Serial Monitor according to this table:

BMP (4 pin connector) Blue Pill
RX A9 (TX)
TX A10 (RX)

There is no need to connect the GND pins since GND is already provided via the Debug port. Don’t connect the VCC neither. If both the Blue Pill and the BMP would provide VCC, this could lead to a short circuit possibly destroying one or both boards.

Again, since a picture says more than a 1000 words, the following pictures might help:

BMP Serial Monitor connection.
Blue Pill Serial Monitor connection.

This is how the complete project looks on my desk. A lovely mess of cables.

The complete project.


Now we’re ready to write some code. Open the main.cpp file that is located in the src folder. Copy/paste the code below into main.cpp. The code is hopefully self explanatory.

#include <Arduino.h>

#define LED_BUILTIN PC13

void setup() {
  // initialize LED digital pin as an output.


void loop() {
  // turn the LED on (HIGH is the voltage level)
  digitalWrite(LED_BUILTIN, HIGH);

  // wait for a second

  // turn the LED off by making the voltage LOW
  digitalWrite(LED_BUILTIN, LOW);

   // wait for a second

Now we’re ready to build our code. If you have your PlatformIO extension open as in this screenshot, simply click the Build button:

Click the Build button.

A terminal pane should open and you see the code being compiled:

Code starts compiling.

You might be surprised by the amount of files that are being compiled. Next to your own main.cpp file, support files from the Arduino framework (which need to be linked in as well together with our code) are compiled as well.

Compiling is finished.

At the end of the compilation/linking process we’re getting informed about the code and data size of our little program as well:

DATA:    [          ]   3.6% (used 728 bytes from 20480 bytes)
PROGRAM: [==        ]  19.2% (used 12556 bytes from 65536 bytes)

Now we can upload our code to the Blue Pill. Hit the Upload button which is just below the Build button you just used earlier. You should see text, similar to this, appearing in the terminal pane:

> Executing task in folder stm32_blinkandserial: C:\Users\cocoacrumbs\.platformio\penv\Scripts\platformio.exe run --target upload <

Processing bluepill_f103c8 (platform: ststm32; board: bluepill_f103c8; framework: arduino)
Verbose mode can be enabled via `-v, --verbose` option
PLATFORM: ST STM32 5.6.0 > BluePill F103C8
HARDWARE: STM32F103C8T6 72MHz, 20KB RAM, 64KB Flash
DEBUG: Current (blackmagic) External (blackmagic, jlink, stlink)
PACKAGES: tool-stm32duino 1.0.1, tool-dfuutil 1.9.190708, framework-arduinoststm32 3.10601.190716 (1.6.1), 
toolchain-gccarmnoneeabi 1.70201.0 (7.2.1), tool-openocd 2.1000.190707 (10.0)
LDF: Library Dependency Finder ->
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 8 compatible libraries
Scanning dependencies...
No dependencies
Checking size .pio\build\bluepill_f103c8\firmware.elf
Memory Usage ->
DATA:    [          ]   3.6% (used 728 bytes from 20480 bytes)
PROGRAM: [==        ]  19.2% (used 12556 bytes from 65536 bytes)
Configuring upload protocol...
AVAILABLE: blackmagic, dfu, jlink, mbed, stlink
CURRENT: upload_protocol = blackmagic
Looking for BlackMagic port...
Auto-detected: COM4
Uploading .pio\build\bluepill_f103c8\firmware.elf
Target voltage: 3.1V
Available Targets:
No. Att Driver
 1      STM32F1 medium density
0x08002d54 in delay ()
Loading section .isr_vector, size 0x10c lma 0x8000000
Loading section .text, size 0x2d3c lma 0x800010c
Loading section .rodata, size 0x3ac lma 0x8002e48
Loading section .init_array, size 0x10 lma 0x80031f4
Loading section .fini_array, size 0x4 lma 0x8003204
Loading section .data, size 0x24 lma 0x8003208
Start address 0x80026a0, load size 12844
Transfer rate: 11 KB/sec, 755 bytes/write.
Section .isr_vector, range 0x8000000 -- 0x800010c: matched.
Section .text, range 0x800010c -- 0x8002e48: matched.
Section .rodata, range 0x8002e48 -- 0x80031f4: matched.
Section .init_array, range 0x80031f4 -- 0x8003204: matched.
Section .fini_array, range 0x8003204 -- 0x8003208: matched.
Section .data, range 0x8003208 -- 0x800322c: matched.
Kill the program being debugged? (y or n) [answered Y; input not from terminal]
====================================== [SUCCESS] Took 7.64 seconds ======================================

Terminal will be reused by tasks, press any key to close it.

If all went well, the LED on the Blue Pill should start to blink with a 1 second interval. When you now hit the Monitor button, the terminal will now show you the output from the serial port coming from the Blue Pill:

Monitoring the serial port.

So far so good. But we wanted to be able to step through and debug our little program. Luckily, this is very simple as well.

Instead of using the Build, Upload and Monitor cycle, we now choose Start Debugging. Either from the PlatformIO extension pane or from the Debug menu at the top of the window:

Select Start Debugging.

You might be surprised that PlatformIO is now compiling all the code over again. However, with the Build task, the code is build in release mode. Meaning it’s optimized code and stripped from all the debugging information that is needed to step through the code while debugging.

At the end of the compilation process, we see the new sizes. And as expected they’re a little bit bigger than their release counterparts:

DATA:    [          ]   4.1% (used 840 bytes from 20480 bytes)
PROGRAM: [==        ]  23.8% (used 15568 bytes from 65536 bytes)

After compiling and linking, the code is uploaded as well and started. By default, a breakpoint is set by PlatformIO at the main() function. The CPU will be stopped when it reaches this main() function (which is part of the Arduino framework). PlatformIO will now present you a screen like this:

Platform (GDB) debugger stopped at the main() function.

Luckily, it’s easy to add new breakpoints. At the lower left you should see a breakpoints section. Click the + button (which will appear when you hover with your mouse over the breakpoints text) and type in loop. This function name is part of our code:

Adding a breakpoint for the loop function.

At the top of your screen, you’ll the these set of icons:

continue/stop/step icons.

Clicking the blue continue/pause icon starts the code again till it encounters a breakpoint. In our case, this will be the breakpoint set at the loop() function:


When our code has stopped, we can see the contents of variables as well (as seen in the top left of the above screen shot).

Now we can execute our code line by line, stepping in and out of functions, run it at full speed again with the continue/pause button or simply stop debugging.

Congratulations, you’ve finished your first project (BitBucket location).

Recent posts

See more