Rusty STM32


So I’ve recently being have a play with building software in Rust for my Nucleo F7 dev board. As I didn’t really fancy writing USB drivers and wanted to have a play with ST’s STM32Cube drivers I thought the best course of action was to mix C and Rust together in one project.


For this project I decided that I would use the rust build system (Cargo) and link my C code via a static library. The Embedded rust book ( gives a good walkthrough on what is needed to build and link a project and get setup with something simple that can print ‘hello world’ via semihosting on the debugger and I did this before adding any of my C code.

As I’m using Visual Studio Code for development currently Cortex-Debug VS Code extension ( This enables debugging using OpenOCD via ST-Link SWD board on the Nucleo development board. For this kind of project having a debugging capability makes life much easier.

Creating the C setup and drivers

The first thing that can make life a lot easier is to use the STMCubeMX program from ST to build an initial setup C code project that will initialise the clocks on the CPU so they are all running at the correct frequency. This utility has some templates for the Nucleo series boards that would also be a good starting point if you are using something else like the STM32F4 Black pill board or your own custom CPU.

I used this to build a initial project with a serial port and several integrated peripherals then renamed the main method to an init method (and made it return) that can be called from the main rust code.

I did find one gotcha in this in that it looks like the rust init routines do not clear the processors ram before running. This gave the symptom that the serial port would work when the ST-Link windows utility was run but not at any other time so it’s important to manually clear the gState vairable (and probably others) before you initialise the HAL drivers. I’m currently initialising each HAL driver from the C code but this could also be done in Rust (and in that case all initialisation has to be done explicitly).

Creating the Rust code

The Embedded Rust book ( provides a good overview on how to setup the Rust side of things. I followed this and got a simple program to output ‘Hello World’ via Semihosting on the Debugger. One thing to note is that the debugger will hit a breakpoint when outputting each character if you don’t enable semihosting on OCD. Using hprintln is also recommended over itm as the later won’t work if you don’t have the SWO pin connected.

In order to link your C code to your rust code you will need to modify the I added the following lines to mine:


This will tell Cargo to link your C code with the rust code when building. For the above example I placed the C library .a file in the source root directory, though for less experminental projects it would be better to create a lib directory. You can then use an extern “C” directive to tell the rust compiler about your C functions. Here is a simple one below:

extern "C" {
pub fn c_main();
pub fn HAL_IncTick();
pub fn SystemInit();

As the Rust cortex libraries handle the Interrupt table it is very important that you define an interrupt handler for the SysTick interrupt. Not doing this will cause the board to get stuck in Rust’s DefaultHandler which will just loop forever. The SysTick interrupt is also very important for the STM32 Hal for tasks such as the delay function so the SysTick timer should at least have the following:

fn SysTick() {
    unsafe { HAL_IncTick();};

My next steps

Now I have something that works I want to have a go at getting Futures working and changing over the STM32F4 black pill for the bulk of my coding. These STM32F4 boards are much cheaper and should have plenty of ram to run most the things I’m interested in.