STM32 Rust Page: Difference between revisions
(5 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= | |||
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 18: | Line 18: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
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> | 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> | ||
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 | |||
<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> |
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");
...