How to use stm32f3::stm32f303 to configure the timer interrupt

Hi. I'm trying to configure the timer interrupt with stm32f3 crate. But the interrupt is never occurred. I have enabled TIM7 interrupt with NVIC and enable the update interrupt in tim7.dier register. But it seems something is still lacked. I have failed to find useful information in the reference book of stm32f303. I also tried interrupt in cortex-m, but it didn't work either.

The code in this topic works well. But in compared with the code there, I didn't miss anything.

#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f3::stm32f303;
use stm32f303::{interrupt, Interrupt, NVIC};

#[entry]
fn main() -> ! {
    let dp = stm32f303::Peripherals::take().unwrap();
    let rcc = &dp.RCC;
    // init TIM7 timer
    let tim7 = &dp.TIM7;
    rcc.apb1enr.modify(|_, w| w.tim7en().set_bit());
    tim7.cr1.modify(|_, w| w.cen().clear_bit());
    tim7.psc.write(|w| w.psc().bits(7_999));
    tim7.arr.write(|w| w.arr().bits(50));

    // enable interrupt
    NVIC::unpend(Interrupt::TIM7);
    unsafe {
        NVIC::unmask(Interrupt::TIM7);
    }
    tim7.dier.modify(|_, w| w.uie().set_bit());
    // enable the counter
    tim7.cr1.modify(|_, w| w.cen().set_bit());
    loop {}
}

#[interrupt]
fn TIM7() {
    let dp = stm32f303::Peripherals::take().unwrap();
    let rcc = &dp.RCC;
    rcc.ahbenr.modify(|_, w| w.iopeen().set_bit());
    let gpioe = &dp.GPIOE;
    gpioe.moder.modify(|_, w| w.moder9().output());
    gpioe.odr.modify(|_, w| w.odr9().set_bit());
    let tim7 = &dp.TIM7;
    tim7.sr.modify(|_, w| w.uif().clear_bit());
    NVIC::unpend(Interrupt::TIM7);
}

Aha, I have found the error. Nothing is missed in the configuration of the timer and interrupt. But let dp = stm32f303::Peripherals::take().unwrap(); in TIM7 function causes an error. So when I set a breakpoint at the next line, it cannot be reached.

I have finished a simple horse race lamp program. I am very curious about how to share peripheral register structs with interrupt handlers in a better way. Thank you for helping me.

#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f3::stm32f303::{self, rcc::ahbenr::IOPGEN_R};
use stm32f303::{interrupt, Interrupt, NVIC};

static mut TIMER: Option<stm32f303::TIM7> = None;
static mut GPIOE: Option<stm32f303::GPIOE> = None;
static mut i: u8 = 0;

unsafe fn get_gpioe() -> &'static mut stm32f303::GPIOE {
    if let Some(ref mut gpioe) = GPIOE {
        &mut *gpioe
    } else {
        panic!()
    }
}

unsafe fn get_timer() -> &'static mut stm32f303::TIM7 {
    if let Some(ref mut timer) = TIMER {
        &mut *timer
    } else {
        panic!()
    }
}

#[entry]
fn main() -> ! {
    let dp = stm32f303::Peripherals::take().unwrap();
    let rcc = &dp.RCC;

    // enable interrupt
    NVIC::unpend(Interrupt::TIM7);
    unsafe {
        NVIC::unmask(Interrupt::TIM7);
    }

    // init LED
    rcc.ahbenr.modify(|_, w| w.iopeen().set_bit());
    {
        let gpioe = &dp.GPIOE;
        gpioe.moder.modify(|_, w| {
            w.moder8()
                .output()
                .moder9()
                .output()
                .moder10()
                .output()
                .moder11()
                .output()
                .moder12()
                .output()
                .moder13()
                .output()
                .moder14()
                .output()
                .moder15()
                .output()
        });
    }

    // init TIM7 timer
    {
        let tim7 = &dp.TIM7;
        rcc.apb1enr.modify(|_, w| w.tim7en().set_bit());
        tim7.cr1.modify(|_, w| w.cen().clear_bit());
        tim7.psc.write(|w| w.psc().bits(7_999));
        tim7.arr.write(|w| w.arr().bits(50));

        tim7.dier.modify(|_, w| w.uie().set_bit());
        // enable the counter
        tim7.cr1.modify(|_, w| w.cen().set_bit());
    }

    // set the value of static TIMER and GPIOE
    unsafe {
        GPIOE = Some(dp.GPIOE);
        TIMER = Some(dp.TIM7);
    }
    loop {}
}

#[interrupt]
fn TIM7() {
    let tim7 = unsafe { get_timer() };
    NVIC::unpend(Interrupt::TIM7);
    tim7.sr.modify(|_, w| w.uif().clear_bit());
    let gpioe = unsafe { get_gpioe() };
    unsafe {
        if i == 0 {
            gpioe.odr.modify(|_, w| w.odr9().set_bit());
            gpioe.odr.modify(|_, w| w.odr8().clear_bit());
        } else if i == 1 {
            gpioe.odr.modify(|_, w| w.odr10().set_bit());
            gpioe.odr.modify(|_, w| w.odr9().clear_bit());
        } else if i == 2 {
            gpioe.odr.modify(|_, w| w.odr11().set_bit());
            gpioe.odr.modify(|_, w| w.odr10().clear_bit());
        } else if i == 3 {
            gpioe.odr.modify(|_, w| w.odr12().set_bit());
            gpioe.odr.modify(|_, w| w.odr11().clear_bit());
        } else if i == 4 {
            gpioe.odr.modify(|_, w| w.odr13().set_bit());
            gpioe.odr.modify(|_, w| w.odr12().clear_bit());
        } else if i == 5 {
            gpioe.odr.modify(|_, w| w.odr14().set_bit());
            gpioe.odr.modify(|_, w| w.odr13().clear_bit());
        } else if i == 6 {
            gpioe.odr.modify(|_, w| w.odr15().set_bit());
            gpioe.odr.modify(|_, w| w.odr14().clear_bit());
        } else if i == 7 {
            gpioe.odr.modify(|_, w| w.odr8().set_bit());
            gpioe.odr.modify(|_, w| w.odr15().clear_bit());
        }
        i = if i == 7 { 0 } else { i + 1 };
    }
}

Maybe the code below is more simple when you have multiple peripherals to use.

#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f3::stm32f303;
use stm32f303::{interrupt, Interrupt, NVIC};

static mut PERIPHERALS: Option<stm32f303::Peripherals> = None;
static mut INDEX: u8 = 0;

unsafe fn get_peripheral() -> &'static mut stm32f303::Peripherals {
    if let Some(ref mut peripheral) = PERIPHERALS {
        &mut *peripheral
    } else {
        panic!()
    }
}

#[entry]
fn main() -> ! {
    unsafe {
        let dp = stm32f303::Peripherals::take().unwrap();
        PERIPHERALS = Some(dp);
    }

    let dp = unsafe { get_peripheral() };

    let rcc = &dp.RCC;

    // enable TIM7 interrupt
    NVIC::unpend(Interrupt::TIM7);
    unsafe {
        NVIC::unmask(Interrupt::TIM7);
    }

    // init LED
    rcc.ahbenr.modify(|_, w| w.iopeen().set_bit());
    let gpioe = &dp.GPIOE;
    gpioe.moder.modify(|_, w| {
        w.moder8()
            .output()
            .moder9()
            .output()
            .moder10()
            .output()
            .moder11()
            .output()
            .moder12()
            .output()
            .moder13()
            .output()
            .moder14()
            .output()
            .moder15()
            .output()
    });

    // init TIM7 timer
    let tim7 = &dp.TIM7;
    rcc.apb1enr.modify(|_, w| w.tim7en().set_bit());
    tim7.cr1.modify(|_, w| w.cen().clear_bit());
    tim7.psc.write(|w| w.psc().bits(7_999));
    tim7.arr.write(|w| w.arr().bits(50));

    tim7.dier.modify(|_, w| w.uie().set_bit());
    // enable the counter
    tim7.cr1.modify(|_, w| w.cen().set_bit());
    loop {}
}

#[interrupt]
fn TIM7() {
    let dp = unsafe { get_peripheral() };
    let tim7 = &dp.TIM7;
    // clear interrupt's pending state
    NVIC::unpend(Interrupt::TIM7);
    // clear the update interrupt flag of TIM7
    tim7.sr.modify(|_, w| w.uif().clear_bit());

    let gpioe = &dp.GPIOE;
    unsafe {
        if INDEX == 0 {
            gpioe.odr.modify(|_, w| w.odr9().set_bit());
            gpioe.odr.modify(|_, w| w.odr8().clear_bit());
        } else if INDEX == 1 {
            gpioe.odr.modify(|_, w| w.odr10().set_bit());
            gpioe.odr.modify(|_, w| w.odr9().clear_bit());
        } else if INDEX == 2 {
            gpioe.odr.modify(|_, w| w.odr11().set_bit());
            gpioe.odr.modify(|_, w| w.odr10().clear_bit());
        } else if INDEX == 3 {
            gpioe.odr.modify(|_, w| w.odr12().set_bit());
            gpioe.odr.modify(|_, w| w.odr11().clear_bit());
        } else if INDEX == 4 {
            gpioe.odr.modify(|_, w| w.odr13().set_bit());
            gpioe.odr.modify(|_, w| w.odr12().clear_bit());
        } else if INDEX == 5 {
            gpioe.odr.modify(|_, w| w.odr14().set_bit());
            gpioe.odr.modify(|_, w| w.odr13().clear_bit());
        } else if INDEX == 6 {
            gpioe.odr.modify(|_, w| w.odr15().set_bit());
            gpioe.odr.modify(|_, w| w.odr14().clear_bit());
        } else if INDEX == 7 {
            gpioe.odr.modify(|_, w| w.odr8().set_bit());
            gpioe.odr.modify(|_, w| w.odr15().clear_bit());
        }
        INDEX = if INDEX == 7 { 0 } else { INDEX + 1 };
    }
}

I think your get_peripheral function in your latest example (and the more specific versions in the previous examples before that) are highly dangerous. Using them, it's very easy to get multiple mutable references to the same memory, which is unsound.

If you want to keep doing it manually, I recommend you wrap them in a cortex_m::interrupt::Mutex.

However, I think the best way to solve this problem is to just use RTIC. It is really good and will generate the most efficient code based on how you access resources. I highly recommend you look at it with an open mind.

Another note, in case you don't already know: The stm32f3 library is what we call a PAC (Peripheral Access Crate), which is a very low-level way to access the hardware. Depending on your use case, it might be preferable to use stm32f3xx-hal instead. I haven't used it (so I can't vouch for it), but its development seems very active.

1 Like

Thank you. I will try these later.