rust-embedded / book

Documentation on how to use the Rust Programming Language to develop firmware for bare metal (microcontroller) devices
https://docs.rust-embedded.org/book/
Apache License 2.0
1.12k stars 177 forks source link

How to clear interrupt with peripheral handle #307

Closed dunajski closed 2 years ago

dunajski commented 2 years ago

Few weeks ago I started to learn Rust Embedded. Now I'm stuck, and I would like to ask you for help. So..

I wanted to use TIM3 in my code to change variable (in future peripheral state) and clear (unpend?) interrupt via registers inside ISR.

In C I did something like this inside ISR:

void TIM3_IRQHandler(void)
{
  if (TIM3->SR & TIM_SR_UIF)
  {
    TIM3->SR &= ~(TIM_SR_UIF);
  }
}

..and now I'm stuck to do this in Rust. At first I show what I've done so far.

#![no_std]
#![no_main]

use panic_halt as _;

use cortex_m_rt::entry;

use core::{cell::RefCell};
use core::ops::DerefMut;
use cortex_m::interrupt::{self, Mutex};
use stm32g0::stm32g071::{self, Interrupt, NVIC, TIM3};

static G_TIM: Mutex<RefCell<Option<stm32g071::TIM3>>> =
    Mutex::new(RefCell::new(None));

#[entry]
fn main() -> ! {
    let p = stm32g071::Peripherals::take().unwrap();

    let rcc_r = &p.RCC;

    let timer_r = &p.TIM3;

    let tim3 = p.TIM3;

    unsafe {
        NVIC::unmask(Interrupt::TIM3);
    };

    rcc_r.apbenr1.write(|w| w.tim3en().set_bit());

    prepare_timer3(timer_r);

    interrupt::free(|cs| {
        G_TIM.borrow(cs).replace(Some(tim3))
    });

    loop {
    }
}

fn prepare_timer3(tim3_r_handle: &TIM3) {
    tim3_r_handle.cr1.write(|w| w.cen().clear_bit());
    tim3_r_handle.psc.write(|w| unsafe { w.psc().bits(16000) });
    tim3_r_handle.arr.write(|w| unsafe { w.arr_l().bits(100) });
    tim3_r_handle.egr.write(|w| w.ug().set_bit());
    tim3_r_handle.dier.write(|w| w.uie().set_bit());
    tim3_r_handle.cr1.write(|w| w.cen().set_bit());
}

#[interrupt]
fn TIM3() {
    interrupt::free(|cs| {
        if let Some(ref mut tim3) =  G_TIM.borrow(cs).borrow_mut().deref_mut() {
            tim3.sr.write(|w| w.uif().clear_bit());
        }
    })
}

And I get this compilation error:

error: cannot find attribute `interrupt` in this scope
  --> src/main.rs:51:3
   |
51 | #[interrupt]
   |   ^^^^^^^^^
   |
   = note: consider importing one of these items:
           cortex_m_rt::interrupt
           crate::stm32g071::interrupt
           stm32g0::stm32g071::interrupt
note: `interrupt` is imported here, but it is a module, not an attribute
  --> src/main.rs:10:27
   |
10 | use cortex_m::interrupt::{self, Mutex};
   |                           ^^^^

error: could not compile `blink-nucleo-g0` due to previous error

I have problem how to resolve those dependency problem. Could you tell me also that what I did with this Mutex G_TIM is fine? I mean I did this after read this article: https://docs.rust-embedded.org/book/concurrency/#sharing-peripherals I also read this https://users.rust-lang.org/t/rust-embedded-stm32f303-timer-interrupt-hanging/40323 but I don't want to use hal crates.

I also asked at Stackoverflow: how-to-clear-interrupt-with-perpiheral-handle-embedded-rust ... and Rust forum: https://users.rust-lang.org/t/how-to-clear-interrupt-with-peripheral-handle/67214

caemor commented 2 years ago

I think the current problem is that you overwrite the interrupt with your local assignment of the module here use cortex_m::interrupt::{self, Mutex};. Maybe just removing the self there and using cortex_m::interrupt::free at the bottom will solver your problem already.

dunajski commented 2 years ago

I changed to:

use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use stm32g0::stm32g071::{self, Interrupt, NVIC, TIM3, interrupt};

and usage of interrupt::free to free.

#[interrupt]
fn TIM2() {
    free(|cs| {
        if let Some(ref mut tim2) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
            tim2.sr.write(|w| w.uif().clear_bit()); // cannot reach here with debugger
        }
    });
}

I think that my ISR is invkoing in loop. How to clear this interrupt properly? I marked that I can not reach line with comment in ISR. Does anyone know why

if let Some(ref mut tim2) = G_TIM.borrow(cs).borrow_mut().deref_mut()

is evaluated as false?

adamgreig commented 2 years ago

Perhaps the problem is the interrupt fires before you call replace on the G_TIM Option, so it's None at that point. You could try making the critical section in main cover the entire timer setup and storing it in the option instead, like:

    rcc_r.apbenr1.write(|w| w.tim3en().set_bit());
    interrupt::free(|cs| {
        prepare_timer3(timer_r);
        G_TIM.borrow(cs).replace(Some(tim3))
    });
dunajski commented 2 years ago

@adamgreig I think you are right, thank you. I show my whole code, maybe someone will need it in future.

#![no_std]
#![no_main]

use panic_halt as _;

use core::cell::RefCell;
use core::ops::DerefMut;
use cortex_m::interrupt::free;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
use stm32g0::stm32g071::{self, interrupt, Interrupt, NVIC};

static G_TIM: Mutex<RefCell<Option<stm32g071::TIM2>>> = Mutex::new(RefCell::new(None));
static G_GPIOA: Mutex<RefCell<Option<stm32g071::GPIOA>>> = Mutex::new(RefCell::new(None));

#[entry]
fn main() -> ! {
    let p = stm32g071::Peripherals::take().unwrap();

    let gpioa = &p.GPIOA;
    let rcc_r = &p.RCC;

    // enable Clock for GPIOA
    rcc_r.iopenr.modify(|_, w| w.iopaen().set_bit());

    let tim2 = p.TIM2;

    // Nucleo G071RB LED so need to set as output
    gpioa.moder.modify(|_, w| unsafe { w.moder5().bits(0b01) });

    rcc_r.apbenr1.write(|w| w.tim2en().set_bit());
    free(|cs| {
        tim2.cr1.write(|w| w.cen().clear_bit());
        tim2.psc.write(|w| unsafe { w.psc().bits(16000) });
        tim2.arr.write(|w| unsafe { w.arr_l().bits(1000) });
        tim2.egr.write(|w| w.ug().set_bit());
        tim2.dier.write(|w| w.uie().set_bit());
        tim2.cr1.write(|w| w.cen().set_bit());
        G_TIM.borrow(cs).replace(Some(tim2));
    });

    // NVIC unmask interrupt
    unsafe {
        NVIC::unmask(Interrupt::TIM2);
    };

    let gpioa = p.GPIOA;
    free(|cs| {
        G_GPIOA.borrow(cs).replace(Some(gpioa));
    });

    let mut increment = 0;
    loop {
        increment += 1;
        if increment > 1000 {
            increment = 0;
        }
    }
}

#[interrupt]
fn TIM2() {
    free(|cs| {
        if let Some(ref mut tim2) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
            tim2.sr.write(|w| w.uif().clear_bit());
        }
    });

    free(|cs| {
        if let Some(ref mut gpioa) = G_GPIOA.borrow(cs).borrow_mut().deref_mut() {
            if gpioa.odr.read().odr5().bit_is_set() {
                gpioa.odr.modify(|_, w| w.odr5().clear_bit());
            } else {
                gpioa.odr.modify(|_, w| w.odr5().set_bit());
            }
        }
    });
}

I have to read more about how Mutex works.