STM32 to Blinky with Rust – Part 3 – HAL and Blinky

In the first part of this series, we connected the dev board and installed the probe-rs tool. In part 2, we created a basic project that compiled and built successfully, flashed it to the chip and watched it print “Hello, world!” over the debug interface back to the host computer. This time we’ll blink the LEDs on the STM32F407G-DISC1 Discovery board, starting with a single LED and finishing with all four in a pattern.

A photograph of an STM32F4 dev board with the east and west LEDs illuminated.

As before, you can follow along with the code snippets here, or see the completed single LED blinky project under the naive-blinky tag in my repo. The first thing we need to do is add the STM32 HAL to our Cargo.toml file. This provides all the interfaces we need to access the hardware features of our STM32F4. To specify the unique set of hardware that makes up the STM32F407, we opt in to the F4 runtime feature and the F407 feature.

[dependencies]
# ...
hal = { package = "stm32-hal2", version = "1.8.3", features = ["f407", "f4rt"] }Code language: TOML, also INI (ini)

In the main.rs file, we always need to import the cortex_m package in some fashion to ensure the linker can find a critical-section but, whereas we previously threw away the import, this time we want to pull out Delay so we can use it later to pause between each blink.

- use cortex_m as _;
+ use cortex_m::delay::Delay;Code language: Diff (diff)

We import Clocks from our renamed stm32-hal2 package which will be used later to configure the pause between blinks, and several parts of the gpio module to allow us to toggle the onboard LEDs.

use hal::{
    clocks::Clocks,
    gpio::{Pin, PinMode, Port},
};Code language: CSS (css)

To work out which pins on the chip are connected to which LEDs on the board, we can refer to the Discovery board’s user manual.

A screenshot from the dev board’s user manual indicating the connections for the four LEDs.

The orange LED, labelled LD3 on the board, is connected to PD13 on the chip, so we set it up as an output pin.

let mut north_led = Pin::new(Port::D, 13, PinMode::Output);Code language: Rust (rust)

We then set up the MCU’s clock configuration, to be used later when we pause between blinks.

let clock_cfg = Clocks::default();
clock_cfg.setup().unwrap();
let mut delay = Delay::new(
    cortex_m::Peripherals::take().unwrap().SYST,
    clock_cfg.systick(),
);Code language: Rust (rust)

Now we’ve prepared the LED’s pin for toggling on and off and a delay timer that can be used to pause between blinks, we can do just that. In a loop. Forever!

loop {
    north_led.toggle();
    delay.delay_ms(1_000);
}Code language: Rust (rust)

At this point cargo run will be compile and flash the above code to the STM32, and one of the LEDs will flash slowly.

The north, orange, LED blinks on and off slowly.

That’s cool, but if you’re reading this blog post and have followed along so far, there’s a chance you’re a massive nerd, just like me. Given 4 LEDs, we could flash a pattern such as displaying the binary numbers from 0 to 15 but that would be too easy. Instead we can flash the Gray coded pattern of those numbers instead. The completed code for this stage can be found under the gray-blinky tag in my repo.

First we need to create an array with all 16 possible iterations of a 4-bit Gray code. We then need a similarly sized 4-bit bitmask to represent each LED.

const GRAY: [u32; 16] = [
    0b0000,
    0b0001, // Least-significant bit 0 appears first
    0b0011, // LSb 1 appears second
    0b0010,
    0b0110, // LSb 2 appears third
    0b0111,
    0b0101,
    0b0100,
    0b1100, // LSb 3, or MSb, appears fourth
    0b1101,
    0b1111,
    0b1110,
    0b1010,
    0b1011,
    0b1001,
    0b1000,
];

const NORTH: u32 = 0b0001;
const EAST: u32 = 0b0010;
const SOUTH: u32 = 0b0100;
const WEST: u32 = 0b1000;Code language: Rust (rust)

If we perform a bitwise AND between a position in the Gray array and one of those cardinal directions’ bitmasks, we’ll be left with a positive value indicating when that LED should be illuminated. We can then make the same test for the other three directions and we’ll know whether to light each of our LEDs on every cycle through the loop.

let mut counter: usize = 0;
loop {
    let bits = GRAY[counter];

    if bits & NORTH > 0 {
        north_led.set_high();
    } else {
        north_led.set_low();
    }
    // ...
}Code language: Rust (rust)

At the end of every loop we need to increment our position in the Gray list, but we must not overflow the end of the array and instead jump back to the start.

loop {
    // ...
    counter += 1;
    if counter > 15 {
        counter = 0
    }
}Code language: Rust (rust)

Compiling and flashing the board with these changes will make the LEDs on the board flash according to the current position in the Gray code array.

The four LEDs blink on and off slowly, in the pattern of a 4-bit Gray code.

There we go! STM32 to Blinky with Rust complete. We can now use this project as a worked example to base any other STM32 embedded Rust projects from and give them a hopefully head-ache free kickstart.


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *