With the following code I'm trying to getting the TIM2 of the STM32G0B1RE triggering interrupt every second. But did not get any interrupt at all, I'm using the PAC crate, no HAL.
Code is building I'm able to load and execute it, but only seeing the "Hello, world!" string.
Testing on NUCLEO-G0B1RE, only board available for me.
I'm not changing the clock configuration as the "default" is HSI16 and is enough for me testing.
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use rtt_target::rtt_init_print;
use stm32g0::stm32g0b1;
use stm32g0::stm32g0b1::{interrupt, Interrupt, NVIC};
#[entry]
fn main() -> ! {
rtt_init_print!();
rtt_target::rprintln!("Hello, world!");
// Take the peripherals
let peripherals = stm32g0b1::Peripherals::take().unwrap();
// Take the Rest and Clock Control (RCC) peripheral
let rcc = &peripherals.RCC;
// Initialize the TIM2 peripheral, interrupt every second, default clock is HSI16
let tim2 = &peripherals.TIM2;
rcc.apbenr1().modify(|_, w| w.tim2en().set_bit()); // Enable TIM2 clock
tim2.cr1().write(|w| w.cen().clear_bit()); // Disable TIM2
unsafe { tim2.psc().write(|w| w.psc().bits(1_600)); } // Set prescaler to 160
unsafe { tim2.arr().write(|w| w.arr().bits(10_000)); } // Set auto-reload value to maximum
// enable interrupt
NVIC::unpend(Interrupt::TIM2);
unsafe {
NVIC::unmask(Interrupt::TIM2);
}
tim2.dier().modify(|_, w| w.uie().set_bit());
// Enable TIM2
tim2.cr1().modify(|_, w| w.cen().set_bit());
loop {
cortex_m::asm::wfi();
rtt_target::rprintln!("Some interrupt triggered!");
}
}
#[interrupt]
fn TIM2() {
let peripherals = stm32g0b1::Peripherals::take().unwrap();
let tim2 = &peripherals.TIM2;
if tim2.sr().read().uif().bit_is_set() {
rtt_target::rprintln!("TIM2 interrupt triggered!");
tim2.sr().modify(|_, w| w.uif().clear_bit()); // Clear the update interrupt flag
NVIC::unpend(Interrupt::TIM2);
}
}
you are calling Peripherals::take() multiple times, I suspect you probably got a panic inside the interrupt service, resulting the processor halted.
the Peripherals type represents the hardware resources, and is a singleton type, meaning you can only call take() once, subsequence calls will return None.
You should call Peripherals::take() exactly once in main(), and then pass the individual parts from it (like TIM2) to functions that need them, borrowing or moving as necessary.
Oh, fn TIM2() is an interrupt handler. I don't know how those are handled.
I wonder what your aim is. Using a low-level PAC is asking for trouble, given that you are not familiar with Rust. I strongly recommend that you go with a high level framework like RTIC or an embedded OS like embassy.
Anyway, look at this code I wrote for a STM32-F4 using the HAL, and TIM2:
#![no_main]
#![no_std]
use stm32f4_knurling as _; // global logger + panicking-behavior + memory layout
use stm32f407g_disc as board;
use crate::board::{
hal::delay::Delay,
hal::interrupt,
hal::prelude::*,
hal::rcc::{Clocks, Rcc},
hal::stm32,
hal::stm32::SYST,
hal::timer::{Event, Timer},
led::{LedColor, Leds},
};
use core::cell::{Cell, RefCell};
use core::ops::DerefMut;
use cortex_m::interrupt::{free, CriticalSection, Mutex};
use cortex_m::peripheral::{Peripherals, DWT};
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
stop_watch();
loop {
continue;
}
}
// Set up global state. It's all mutexed up for concurrency safety.
static ELAPSED_MS: Mutex<Cell<u32>> = Mutex::new(Cell::new(0u32));
static TIMER_TIM2: Mutex<RefCell<Option<Timer<stm32::TIM2>>>> = Mutex::new(RefCell::new(None));
fn stop_watch() {
if let (Some(mut dp), Some(cp)) = (stm32::Peripherals::take(), cortex_m::Peripherals::take()) {
let rcc = dp.RCC.constrain();
let clocks = setup_clocks(rcc);
// Create a 1ms..100us (1kHz..100kHz) periodic interrupt from TIM2
let mut timer = Timer::tim2(dp.TIM2, 100.khz(), clocks);
timer.listen(Event::TimeOut);
free(|cs| {
TIMER_TIM2.borrow(cs).replace(Some(timer));
});
stm32::NVIC::unpend(stm32::Interrupt::TIM2);
let mut delay = Delay::new(cp.SYST, clocks);
let elapsed = 0;
loop {
free(|cs| {
stopwatch_start(cs);
});
let df = waist_loops(elapsed);
let elapsed = free(|cs| ELAPSED_MS.borrow(cs).get());
free(|cs| {
stopwatch_stop(cs);
});
delay.delay_ms(sat_delay(df));
defmt::info!("Elapsed: {}", elapsed);
}
}
}
fn sat_delay(df: f32) -> u32 {
let mut dfu = df as u32;
if dfu >= 1000 {
dfu = 1000;
} else if dfu <= 500 {
dfu = 500;
}
dfu
}
fn waist_loops(t: u32) -> f32 {
let k: f32 = t as f32;
let mut op = k * 2.;
op = op + (k + 5.) / 3.;
for i in 1..400 {
op += i as f32;
}
//defmt::info!("Value in float {}", op);
op
}
fn stopwatch_start<'cs>(cs: &'cs CriticalSection) {
ELAPSED_MS.borrow(cs).replace(0);
unsafe {
stm32::NVIC::unmask(stm32::Interrupt::TIM2);
}
}
fn stopwatch_stop<'cs>(_cs: &'cs CriticalSection) {
stm32::NVIC::mask(stm32::Interrupt::TIM2);
}
#[interrupt]
fn TIM2() {
free(|cs| {
if let Some(ref mut tim2) = TIMER_TIM2.borrow(cs).borrow_mut().deref_mut() {
tim2.clear_interrupt(Event::TimeOut);
}
let cell = ELAPSED_MS.borrow(cs);
let val = cell.get();
cell.replace(val + 1);
});
}
fn setup_clocks(rcc: Rcc) -> Clocks {
return rcc
.cfgr
.hclk(48.mhz())
.sysclk(48.mhz())
.pclk1(24.mhz())
.pclk2(24.mhz())
.freeze();
}
The relevant snippet are:
// Set up global state. It's all mutexed up for concurrency safety.
static ELAPSED_MS: Mutex<Cell<u32>> = Mutex::new(Cell::new(0u32));
static TIMER_TIM2: Mutex<RefCell<Option<Timer<stm32::TIM2>>>> = Mutex::new(RefCell::new(None));
and
#[interrupt]
fn TIM2() {
free(|cs| {
if let Some(ref mut tim2) = TIMER_TIM2.borrow(cs).borrow_mut().deref_mut() {
tim2.clear_interrupt(Event::TimeOut);
}
let cell = ELAPSED_MS.borrow(cs);
let val = cell.get();
cell.replace(val + 1);
});
}