STM32 Rust Page: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
Created page with "=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 <syntaxhighlight lang="bash"> sudo apt install curl curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh </syntaxhighlight> For stm32 we need the toolchain <syntaxhighlight lang="bash"> rustup target add thumbv7em-none-eabihf cargo install cargo-binutils cargo install c..."
 
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Introduction=
=Introduction=
This is meant to be a general page for rust when using STM32
This is meant to be a general page for rust when using STM32
==Install Rust For Embedded==
=Install Rust For Embedded=
First we install rust. This adds something to ~/.bashrc so rebooting is the quickest way
First we install rust. This adds something to ~/.bashrc so rebooting is the quickest way
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 12: Line 12:
cargo install cargo-binutils
cargo install cargo-binutils
cargo install cargo-generate
cargo install cargo-generate
</syntaxhighlight>
Install the gcc tools
<syntaxhighlight lang="bash">
sudo apt install gdb-multiarch openocd qemu-system-arm
sudo apt install gdb-multiarch openocd qemu-system-arm
</syntaxhighlight>
</syntaxhighlight>
Now we install the debugger and the gcc tools. We tried the arm download but it relied on python and shared libraries not on Ubuntu 24.04
Now we need to install the arm software. For me I went to the arm download page and took the latest version.[[https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads here]]<br>
<syntaxhighlight lang="bash">
I unzipped it and put it in the /opt directory, currently arm-gnu-toolchain-14.2, and add it to my path in .bashrc
apt install gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf gcc-arm-none-eabi 
 
=Blinky In Rust=
The new classic since hello world
<syntaxhighlight lang="rs">
#[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();
    }
}
</syntaxhighlight>
=Interrupts In Rust=
This involves creating Mutexes to make the shared resource available. First we create these Mutexes
<syntaxhighlight lang="rs">
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));
</syntaxhighlight>
Next we setup the interrupt
<syntaxhighlight lang="rs">
#[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();
    }
}
</syntaxhighlight>
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.
<syntaxhighlight lang="rs">
// 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();
    })
}
</syntaxhighlight>
=Debbugging in Rust=
==VS Code Launch Settings==
This is the current setup as of April 2024. The SVD enables to XPERIPHERALS under breakpoints.
<syntaxhighlight lang="json">
{
    "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 }
                ]
            }
        }
    ]
}
</syntaxhighlight>
 
==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
<syntaxhighlight lang="rs">
#[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");
...
</syntaxhighlight>
</syntaxhighlight>
I now use the arm download and put it in the /opt directory, currently arm-gnu-toolchain-14.2, and add it to my path in .bashrc

Latest revision as of 22:36, 5 February 2025

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");
...