Programming a Blue/Black Pill STM32 Board with a ST-Link v2

While trying to learn embedded rust, I ran up against issues connecting my black pill STM32F103 boards to the ST-Link v2. The solution is simple but badly documented, so here it is with nice illustrations.

The ST-Link v2 can be used to connect to development boards either under external power or power from the ST-Link. However, even when the ST-Link is used to provide power, and successfully powers the target from it’s VDD pin (pin 19), it is always necessary to connect ST-Link’s VAPP (pins 1 or 2) to the target 3.3v rail, as the ST-Link determines the target programming voltage from this pin. Not doing so will lead to random errors, some of which are documented below.

ST-Link v2 pinout (from

Possible Connections

To use a ST-Link to programme a blue/black pill (BP) under external power, connect it like this:

  • BP under external power (e.g. USB)
  • ST-Link GND (even-numbered pins from 4 to 20) to BP Ground
  • ST-Link SWDIO (pin 7) to BP DIO
  • ST-Link SWCLK (pin 9) to BP CLK
  • ST-Link VAPP (pins 1 or 2) to BP 3V3

The illustrations show a blue pill because that’s what fritzing had in its part library. The connections are essentially the same for the black pill.

To use a ST-Link to power and programme a blue/black pill at the same time, connect it as follows:

  • ST-Link GND (even-numbered pins from 4 to 20) to BP Ground
  • ST-Link SWDIO (pin 7) to BP DIO
  • ST-Link SWCLK (pin 9) to BP CLK
  • ST-Link VDD (pin 19) to BP 3V3
  • ST-Link VAPP (pins 1 or 2) to BP 3V3

For what it’s worth, when programmed like this, the BOOT0 and BOOT1 headers don’t seem to do anything. I can successfully flash binaries to the board with the headers in either position, or left off completely.


If you leave the VAPP pin disconnected, you’re likely to see various inconsistent errors. Here’s a few of the ones I was treated to:

$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v29 API v2 SWIM v7 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 1.529843
Info : stm32f1x.cpu: hardware has 6 breakpoints, 0 watchpoints

Here, openocd looks like it can connect, but complains about the Target voltage being 1.5v rather than the 3.3v we expect. This is a clear warning sign that something is not working!

If the noise voltage measured on an unconnected VPP happens to be low enough, openocd will complain:

Info : Target voltage: 0.000000
Error: target voltage may be too low for reliable debugging
Error: init mode failed (unable to connect to the target)
in procedure 'init' 
in procedure 'ocd_bouncer'

If you try to use cargo flash to flash a binary to the BP, you’ll likely cycle through a colourful array of different errors, each as undocumented as the last:

$ cargo flash --release --chip stm32f103c8

Finished release [optimized] target(s) in 0.04s
Flashing /Users/barnabywalters/Documents/Programming/rust/blackpill-blink/target/thumbv7m-none-eabi/release/blackpill-blink
    WARN probe_rs::probe::stlink > send_jtag_command 242 failed: SwdDpFault
   Error failed attaching to target

Caused by:
0: An error with the usage of the probe occured
1: An error specific to a probe type occured
2: Command failed with status SwdDpFault

They all look more or less like this, but the Command can fail with statuses like SwdDpFault and JtagGetIdcodeError. If you try to use probe-rs-cli, you might sometimes get SwdApError, SwdDpParityError, or what looks like a partial success, with Available Access Ports: followed by nothing.

Attempting to use stmduino, st-flash, pyocd or various other software will probably produce similar errors.


While trying to follow this rust STM32 programming guide (wayback machine as the original site is having TLS issues at the time of writing) I ran up against weird, inconsistent connection errors.

I tried researching the error messages I was getting but found nothing other than the suggestion that the STM32 was probably a Chinese counterfeit, and that was causing the problems. I initally gave up, but then decided to have another crack at the issue. Noticing that openocd was reporting low target voltages, I eventually realised that VAPP doesn’t provide power but rather detects it.