STM32U5 series MCUs HAL, PAC and peripheral access.
โ Rust ๐ 2025-12-08 ๐ค surdeus ๐๏ธ 1Hello
After a good 10 hours in the embedded rust world, I am satisfied to have a test program running on a NUCLEO-U545RE-Q, using probe-rs, based on the embeded rust book (wich seems to be work in progress), and this template: GitHub - knurling-rs/app-template: Quickly set up a `probe-rs` + `defmt` + `flip-link` embedded project
I modified src/bin/hello.rs to my needs by trying out the SysTick and then attempting to turn on the user LED connected to PA5 by using the PAC directly, before learning that that is not the right way (Initialize stm32 clock and write leds - #3 by nerditation). Strugling to find a HAL for the used MCU STM32U545RE before, I set out again using less specific search terms and found this: stm32_hal2 - Rust
The STM32U5 series of MCUs is not supported yet, therefore another dead end...
In src/bin/hello.rs i try to troubleshoot what I am doing by:
- Reading gpio bits showing what they should be. This already shows that my approach fails.
- Modifying MODEr to make PORTA pin 5 push-pull and and setting pin 5 high.
- Reading and showing what they should be again. Still futile.
- (Systick is intended to blink the LED.)
#![no_main]
#![no_std]
use app as _; // global logger + panicking-behavior + memory layout
// Added semihosting functions.
use cortex_m_semihosting::{debug, hprintln};
// Added PAC (Peripheral Access crate):
use stm32u5::stm32u545 as pac;
// Added general cortex PAC (Peripheral Access crate):
use cortex_m as mpac;
//
use core::ops::Deref;
#[cortex_m_rt::entry]
fn main() -> ! {
// Cortex M Peripherals API.
let mut cp = mpac::Peripherals::take().unwrap();
// Peripherals API.
let mut p = pac::Peripherals::take().unwrap();
//defmt::println!("Hello, world!");
// PortA (for blinking LED with PA5).
let mut gpioa = p.GPIOA.deref();
hprintln!("Before regmod:\n\
moder : {:b}\n\
should: 10101011111111111111111111111111\n\n\
outreg: {:b}\n\
should: 00000000000000000000000000000000\n\n\
inreg : {:b}\n\
should: 0000000000000000XXXXXXXXXXXXXXXX",
gpioa.moder().read().bits(),
gpioa.odr().read().bits(),
gpioa.idr().read().bits()
);
gpioa.moder().modify(
|r, w| unsafe { w.bits(
r.bits() /* Existing bits. */
& !(1 << 11) /* Set bit 11 zero. */
| (1 << 10)) /* Set bit 10 one. */
}
);
gpioa.bsrr().write(|w| unsafe { w.bits(1 << 5) } );
hprintln!("\n\nAfter regmod:\n\
moder : {:b}\n\
should: 10101011111111111111011111111111\n\n\
outreg: {:b}\n\
should: 00000000000000000000000000100000\n\n\
inreg : {:b}\n\
should: 0000000000000000XXXXXXXXXX1XXXXX",
gpioa.moder().read().bits(),
gpioa.odr().read().bits(),
gpioa.idr().read().bits()
);
// Trying out systicks.
let mut systick = cp.SYST;
systick.set_clock_source(mpac::peripheral::syst::SystClkSource::Core);
systick.set_reload(10_000);
systick.clear_current();
systick.enable_interrupt();
systick.enable_counter();
loop {}
}
// https://docs.rust-embedded.org/book/start/exceptions.html
// "Note that the exception attribute transforms definitions of static variables
// inside the function by wrapping them into unsafe blocks and providing us with
// new appropriate variables of type &mut of the same name. Thus we can
// dereference the reference via * to access the values of the variables without
// needing to wrap them in an unsafe block."
#[cortex_m_rt::exception]
fn SysTick() {
static mut TICK: bool = true;
static mut COUNT: usize = 0;
if *TICK {
hprintln!("Tick");
*TICK = false;
} else {
hprintln!("Tack");
*TICK = true;
}
*COUNT += 1;
if *COUNT > 5 {
app::exit()
}
}
Output from cargo run --bin hello (Omitted warnings form unused debug import and variables not needing to be mut):
Before regmod:
moder : 10101011111111111111111111111111
should: 10101011111111111111111111111111
outreg: 10101011111111111111111111111111
should: 00000000000000000000000000000000
inreg : 10101011111111111111111111111111
should: 0000000000000000XXXXXXXXXXXXXXXX
After regmod:
moder : 10101011111111111111111111111111
should: 10101011111111111111011111111111
outreg: 10101011111111111111111111111111
should: 00000000000000000000000000100000
inreg : 10101011111111111111111111111111
should: 0000000000000000XXXXXXXXXX1XXXXX
Tick
Tack
Tick
Tack
Tick
Tack
Firmware exited successfully
Does anybody know of a HAL for the STM32U5 series MCUs and I was just not able to find it?
If not, any chance of making this work with the PAC? I have the impression that stm32u5 - Rust is finished as intended (as it seems auto generated). If that is not the case, what gives the clue? The Cargo.toml of the above linked STM32-HAL2 does not list the STM32U5 series either, but that might just be a delay in timing: stm32-hal/Cargo.toml at main ยท David-OConnor/stm32-hal ยท GitHub.
If even that is not possible, I would be (until a HAL is available) happy to do this with C style unsafe pointers. Hints how to do that, would be appreciated.
2 posts - 2 participants
๐ท๏ธ Rust_feed