I'm new using Rust for embedded develompment. I'm currently figuring out how to set up timer interrupts. The idea is to blink an LED periodically, which should be pretty simple.
I've managed to set up the timer and have enabled the corresponding interrupt. Problem is, it seems to keep repeating the ISR forever, never returning to the main code. My hypothesis is that the interrupt is not being unpended correctly. Here's my code:
#![no_std]
#![no_main]
use core::sync::atomic::{AtomicBool, Ordering};
use cortex_m_rt::entry;
use stm32f3xx_hal::prelude::*;
use stm32f3xx_hal::timer::Timer;
use stm32f3xx_hal::stm32;
use stm32::{interrupt, Interrupt};
#[panic_handler]
unsafe fn panic_handler(info: &core::panic::PanicInfo) -> ! {
cortex_m_semihosting::hprintln!("ERROR! {:?}", info).unwrap();
loop {}
}
#[interrupt]
fn TIM7() {
stm32::NVIC::unpend(Interrupt::TIM7);
TICK.store(true, Ordering::Relaxed);
}
static TICK: AtomicBool = AtomicBool::new(false);
#[entry]
fn main() -> ! {
let peripherals = stm32f3xx_hal::stm32::Peripherals::take().unwrap();
let mut rcc = peripherals.RCC.constrain();
let mut flash = peripherals.FLASH.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let tim7 = peripherals.TIM7;
let _tim7 = Timer::tim7(tim7, 1.hz(), clocks, &mut rcc.apb1);
let tim7 = unsafe {&* stm32::TIM7::ptr()};
tim7.dier.write(|w| w.uie().set_bit());
tim7.cr1.write(|w| w.cen().set_bit());
unsafe {
stm32::NVIC::unmask(Interrupt::TIM7);
}
let mut gpioe = peripherals.GPIOE.split(&mut rcc.ahb);
let mut led = gpioe.pe9
.into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);
let mut led_on = false;
loop {
cortex_m_semihosting::hprintln!("doing stuff").unwrap();
if TICK.swap(false, Ordering::Relaxed) {
led_on = !led_on;
if led_on {
led.set_high().unwrap()
} else {
led.set_low().unwrap()
}
}
}
}
The output: three lines of doing stuff. After that (Probably because the timer went off), no output anymore.
I think you need to clear the interrupt flag. If you don’t, the interrupt will be triggered again right after the ISR function. (See page 654 of the reference manual)
Here is my blinking LED application. I did it with stm32f4xx_hal. This crate’s clear_interrupt() method writes 0 to the status register’s UIF bit. If I don’t call that, the program repeats the ISR forever.
#![no_main]
#![no_std]
extern crate panic_halt;
use core::cell::{Cell, RefCell};
use core::ops::DerefMut;
use cortex_m;
use cortex_m::interrupt::{free, Mutex};
use cortex_m_rt::entry;
use stm32::interrupt;
use stm32f4xx_hal as hal;
use crate::hal::{
prelude::*,
stm32,
timer::{Event, Timer},
};
static LED_STATE: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
static TIMER_TIM2: Mutex<RefCell<Option<Timer<stm32::TIM2>>>> = Mutex::new(RefCell::new(None));
#[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 led_state = LED_STATE.borrow(cs);
led_state.replace(!led_state.get());
});
}
#[entry]
fn main() -> ! {
let dp = stm32::Peripherals::take().unwrap();
// Set up the LED
let gpiob = dp.GPIOB.split();
let mut led = gpiob.pb7.into_push_pull_output();
// Set up the system clock
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
// Set up the interrupt timer
let mut timer = Timer::tim2(dp.TIM2, 10.hz(), clocks);
timer.listen(Event::TimeOut);
free(|cs| {
TIMER_TIM2.borrow(cs).replace(Some(timer));
});
// Enable interrupt
stm32::NVIC::unpend(hal::stm32::Interrupt::TIM2);
unsafe {
stm32::NVIC::unmask(hal::stm32::Interrupt::TIM2);
}
loop {
if free(|cs| LED_STATE.borrow(cs).get()) {
led.set_high().unwrap();
} else {
led.set_low().unwrap();
}
}
}
That's exactly the issue, and in stm32f3xx_hal the timer method is called clear_update_interrupt_flag() (no arguments).
Also the call to unpend in the NVIC can be omitted; the NVIC automatically unpends when your ISR is called. It's a little confusing but for every peripheral interrupt there are actually two interrupt pending bits--one in the peripheral and one in the NVIC. It's always the former that you want to clear in your ISR. NVIC's pending bit indicates when a lower-priority IRQ happens while an interrupt is being serviced, in which case the NVIC puts the lower-priority IRQ into a pending state and services it when all higher-priority interrupts are finished. You only need to care about NVIC's pending bit when you want to detect these kinds of nested IRQs, which is rarely.