STM32G0B1 Timer2 interrupt not triggered

Hi,

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.

I'm not familiar with Rust, do I need to setup some borrowing mechanism?
And if yes, how?
Thanks,

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);
    });
}

I hope it helps.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.