
Intro
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:
Features:
- 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.
And:
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:
But at the same time:
Windows 10 displays the BMP probe ports using the generic title, “USB Serial Device”, as seen below:
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
COM3
----
Hardware ID: USB VID:PID=1D50:6018 SER=6 LOCATION=1-4:x.2
Description: USB Serial Device (COM3)
COM4
----
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.
After which, this window pops up.
- 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 inBlue
and the list of boards will shrink to show only boards that start with theBlue
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.
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
; https://docs.platformio.org/page/projectconf.html
[env:bluepill_f103c8]
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:
[env:bluepill_f103c8]
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 |
---|---|
GND | GND |
VCC | 3V3 |
TMS/SWDIO | SWIO |
TCK/SWCLK | SWCLK |
Since a picture says more than a 1000 words, the following pictures might help:
Now we connect the wires for the Serial Monitor according to this table:
BMP (4 pin connector) | Blue Pill |
---|---|
GND | - |
RX | A9 (TX) |
TX | A10 (RX) |
VCC | - |
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:
This is how the complete project looks on my desk. A lovely mess of cables.
Compiling
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.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
}
void loop() {
// turn the LED on (HIGH is the voltage level)
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("ON");
// wait for a second
delay(1000);
// turn the LED off by making the voltage LOW
digitalWrite(LED_BUILTIN, LOW);
Serial.println("OFF");
// wait for a second
delay(1000);
}
Now we’re ready to build our code. If you have your PlatformIO extension open as in this
screenshot, simply click the Build
button:
A terminal pane should open and you see the code being compiled:
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.
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
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/bluepill_f103c8.html
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 -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 8 compatible libraries
Scanning dependencies...
No dependencies
Checking size .pio\build\bluepill_f103c8\firmware.elf
Memory Usage -> http://bit.ly/pio-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:
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:
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:
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:
At the top of your screen, you’ll the these set of 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).