Rust on STM32F103 Blue Pill with Probe-run tooling

A Quick Start

3 min readDec 5, 2020
Rev 1.0 12/05/2020; initial post
Rev 1.5 10/09/2021; add reference to David OConnor

The Rust tooling for embedded is improving rapidly. I will try to update/re-write this post, reflecting the latest changed I’ve learned, through various on-line sources.

Rust for embedded software is always an interesting alternative to MCU vendor specific IDE software/frameworks, such as those provided by TI, Microchip, STM32, Nordic, etc.

Each of these vendors try to lock your source code into their “library/framework”, so that the next MCU you choose to use tends to come from the same vendor, if not the same series.

On the other hand, there are higher-level software tool-chains such as Arduino, MBED that aim to break the barrier between MCUs and provide a unified coding API/experience.

Rust embedded can be a powerful push in that direction, provided that it holds its zero-cost abstraction promises.

With the advent of probe-rs and subsequently probe-run tooling software, Rust for embedded is getting more convenient and easier to use.

We will use the cheap and ubiquitous STM32F103 “Blue Pill” as an example, but many other STM32 MCUs shall work very similarly. Well, to make the point, other MCUs shall work similarly in the near future as well, once the higher level libraries in Rust mature towards the level of Arduino’s.

So why Rust, you may ask, if we already have Arduino, Micro-python, Javascript …

Or ask the question differently, does the advantage Rust holds for computer software, namely memory safety, syntax ergonomics, cargo package manager and the eco-system, etc. still hold true for the embedded software?

This is still left to be seen.

1. Quick Start

1.1 hardware setup:

  • STM32F103C8T6 “Blue Pill” module (schematic link)
  • ST-link (v2) debugger (with device driver)

1.2 software setup:

runner = "probe-run --chip STM32F103C8"

1.3 flash and run

In a terminal window, type

cargo run --bin blue_pill_base --release

2. Raw Memory Address Access

The following is directly modified from David OCorner’s sample code in his blog post.

The “mode” register for STM32F103 is a bit different. There are CHL (bit 0 to 7) and CHR (bit 8 to 15).

This helps to explain in the “blue_pill_base” code

// Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function // in order to configure the port. For pins 0-7, crl should be passed instead.

let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);

3. Using PAC

There is one-to-one mapping from the original memory access code to this PAC way of accessing the GPIO.

original code reference (

PAC saved some effort of looking up memory addresses. But it still has not abstracted away much of STM32’s peculiarity.

4. Using HAL

The HAL code is a bit shorter than PAC ones. But the passing in/out of these parameters are quite mystery, if you haven’t read through the earlier code pieces.

In a sense, it doesn’t provide much advantage over the PAC method.

5. Using Device Driver

<to be completed>

Useful References

David OConnor’s Rust for embedded (link)

STM32 Blue Pill pinout diagram (link)

Old way with openocd + gdb (link)

New way with probe-run (link)

Rust embedded GPIO model (link)

Rust software/hardware notes (link)




memento of electronics and fun exploration for my future self