Rusty STM32

Introduction

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.

Setup

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 (https://docs.rust-embedded.org/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 (https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). 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 (https://docs.rust-embedded.org/book/start/qemu.html) 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 build.rs I added the following lines to mine:

    println!("cargo:rustc-link-lib=static=<stm32-c-library>");
    println!("cargo:rustc-link-search=native=<src-dir>");

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:

#[exception]
fn SysTick() {
    unsafe { HAL_IncTick();};
    //hprint!(".").unwrap();
}

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.

Adding a custom USB socket to the STM32 NUCLEO-F746

I’m currently building a design on the STM32F7 which I want to later port to a F4-NUCLEO board and eventually my own design so I want to test the USB host without the supplied electronics.

In order to do this I built a small USB breakout adapter and tried directly connecting this to 5V (for power) and PA11/PA12 (for USB).  The breakout was made by simply soldering some header pins to a USB socket.

I then attached the USB socket to CN12 as shown below.

The complete pinout for CN12 can be found here https://developer.mbed.org/platforms/ST-Nucleo-F746ZG/.

You will need to connect the pins on the usb connector as shown below:

  1. VBUS (+5v)
  2. PA11
  3. PA12
  4. GND

STM32 Nucleo boards and blackmagic probe.

I recently purchased a STM32F7 Nucleo boards to play around with Chibios.  These boards feature a integrated STlink debugger, I however do most of my development using a custom built debugger based on the blackmagic probe.  This has the advantage of being easily connectable to GDB via emulating gdbserver on it’s virtual serial port.

Unfortunately there doesn’t look like there is a documented way of connecting a 3rd party debugger to the Nucleo board but reading the schematics showed an answer in the form of the CN4 jumper block.

In order to connect a the black magic probe to the Nucleo board simply remove the jumpers from CN4 on the ST-Link .  You can then access the SWDIO pins on the main MCU as shown in the list below.

  1. SWCLK/TCK
  2. NO-CONNECT
  3. SWDO/TMS
  4. NO-CONNECT

You will also need to connect the ground pin of the blackmagic probe to a ground pin on the Nucleo board (I used one of the ground pins which have jumpers on them which are used for feet on the board.

And here is a picture of the board.