STM32 Rust Page

From bibbleWiki
Jump to navigation Jump to search

Introduction

This is meant to be a general page for rust when using STM32

Install Rust For Embedded

First we install rust. This adds something to ~/.bashrc so rebooting is the quickest way

sudo apt install curl
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

For stm32 we need the toolchain

rustup target add thumbv7em-none-eabihf
cargo install cargo-binutils
cargo install cargo-generate

Install the gcc tools

sudo apt install gdb-multiarch openocd qemu-system-arm

Now we need to install the arm software. For me I went to the arm download page and took the latest version.[here]
I unzipped it and put it in the /opt directory, currently arm-gnu-toolchain-14.2, and add it to my path in .bashrc

Blinky In Rust

The new classic since hello world

#[entry]
fn main() -> ! {
    if let Some(dp) =  pac::Peripherals::take() {

        // Set up the system clock.
        let mut rcc = dp.RCC.constrain();

        // Get the GPIOB peripheral
        let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);

        // (Re-)configure PE13 as output
        let mut led = gpiob.pb13.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);

        led.set_low().unwrap();

        loop {
            led.toggle().unwrap();

            // Wait for 1_000_000 cycles
            cortex_m::asm::delay(1_000_000);

            if led.is_set_low().unwrap() {
                led.set_high().unwrap();
            } else {
                led.set_low().unwrap();
            }

            // Wait for 1_000_000 cycles
            cortex_m::asm::delay(1_000_000);
        }

    }

    loop {
        cortex_m::asm::nop();
    }
}

Interrupts In Rust

This involves creating Mutexes to make the shared resource available. First we create these Mutexes

type LedPin = gpio::PB10<Output<PushPull>>;
static LED: Mutex<RefCell<Option<LedPin>>> = Mutex::new(RefCell::new(None));

type ButtonPin = gpio::PC13<Input>;
static BUTTON: Mutex<RefCell<Option<ButtonPin>>> = Mutex::new(RefCell::new(None));

Next we setup the interrupt

#[entry]
fn main() -> ! {
...
    // Moving ownership of the led to the global LED
    critical_section::with(|cs| *LED.borrow(cs).borrow_mut() = Some(led));

    // Configuring the user button to trigger an interrupt when the button is pressed.
    let mut user_button = gpioc.pc13;

    // Make button an interrupt source
    syscfg.select_exti_interrupt_source(&user_button);

    // Set when to trigger
    user_button.trigger_on_edge(&mut exti, Edge::Rising);

    // Enable the interrupt for button
    user_button.enable_interrupt(&mut exti);

    // Get the interrupt number
    let interrupt_num = user_button.interrupt();

    // Moving ownership to the global BUTTON
    critical_section::with(|cs| *BUTTON.borrow(cs).borrow_mut() = Some(user_button));

    // Enable Interrupt
    unsafe { NVIC::unmask(interrupt_num) };

    loop {
        asm::wfi();
    }
}

After this we need to create the interrupt. The name of the function is derived from the line to NVIC. The is PC15 so EXTI15_10 is the correct name as EXI15_10 is Pin 10-15 or the GPIOs. We need PB10 so this is the correct one. If it was PB9 then this would be EXTI9_5.

// This may be called more than once per button press from the user since the button may not be debounced.
#[interrupt]
 fn EXTI15_10() {
    critical_section::with(|cs| {
        // Toggle the LED
        LED.borrow(cs)
            .borrow_mut()
            .as_mut()
            .unwrap()
            .toggle()
            .unwrap();

        // Clear the interrupt pending bit so we don't infinitely call this routine
        BUTTON
            .borrow(cs)
            .borrow_mut()
            .as_mut()
            .unwrap()
            .clear_interrupt();
    })
}

Debbugging in Rust

VS Code Launch Settings

This is the current setup as of April 2024. The SVD enables to XPERIPHERALS under breakpoints.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "openocd",
            "cwd": "${workspaceRoot}",
            "executable": "./target/thumbv7em-none-eabihf/debug/blink",
            "name": "Debug (OpenOCD)",
            "device": "STM32F302RETx",

            "showDevDebugOutput": "parsed",
            "preLaunchTask": "cargo build",
            "runToEntryPoint": "true",

            "configFiles": [
                "interface/stlink-v2-1.cfg",
                "target/stm32f3x.cfg"
            ],
            "svdFile": "${workspaceRoot}/STM32F302.svd",
            "swoConfig": {
                "enabled": true,
                "cpuFrequency": 8000000,
                "swoFrequency": 2000000,
                "source": "probe",
                "decoders": [
                    { "type": "console", "label": "ITM", "port": 0 }
                ]
            }
        }
    ]
}

Serial Wire Out SWO Debugging

You can output text using the SWO console which is like semi-hosting. Not the settings in the VSCode Launch

#[entry]
fn main() -> ! {
    if
        let (Some(dp), Some(cp)) = (
            pac::Peripherals::take(),
            cortex_m::peripheral::Peripherals::take(),
        )
    {
...
        let mut itm = cp.ITM;
        iprintln!(&mut itm.stim[0], "Hello World for the time");
...