/src/timer.rs
use core::cell::RefCell;
use crate::bsp;
use cortex_m::peripheral::SCB;
use cortex_m_rt::exception;
use critical_section::Mutex;
static TIMER: Mutex<RefCell<Option<Timer>>> = Mutex::new(RefCell::new(None));
pub type Ticks = u32;
#[derive(Debug)]
pub enum TimerError {
TooManyTimers,
}
#[derive(Debug, Clone)]
enum TimerCallbackType {
OneShot,
Recurring(Ticks),
}
#[derive(Debug, Clone)]
struct TimerCallback {
t0: Ticks,
f: fn(),
t_type: TimerCallbackType,
}
struct Timer {
t: Ticks,
callbacks: heapless::Vec<TimerCallback, 10>,
}
impl Timer {
fn tick(&mut self) {
self.t += 1;
}
pub fn gather_pending(&mut self) -> heapless::Vec<fn(), 10> {
let mut pending = heapless::Vec::new();
let mut i = 0;
while i < self.callbacks.len() {
if self.t >= self.callbacks[i].t0 {
let f = match self.callbacks[i].t_type {
TimerCallbackType::OneShot => {
let tc = self.callbacks.swap_remove(i);
tc.f
}
TimerCallbackType::Recurring(d) => {
self.callbacks[i].t0 += d;
self.callbacks[i].f
}
};
pending.push(f).ok();
} else {
i += 1;
}
}
pending
}
}
pub fn init_timer(mut systick: bsp::pac::SYST) {
critical_section::with(|cs| {
systick.set_reload(bsp::pac::SYST::get_ticks_per_10ms() / 10);
systick.clear_current();
systick.enable_interrupt();
systick.enable_counter();
let mut timer = TIMER.borrow_ref_mut(cs);
*timer = Some(Timer {
t: 0,
callbacks: heapless::Vec::new(),
})
})
}
pub fn schedule(d: Ticks, recurring: bool, f: fn()) -> Result<usize, TimerError> {
critical_section::with(|cs| {
let mut timer = TIMER.borrow_ref_mut(cs);
if let Some(ref mut timer) = *timer {
let t_type = if recurring {
TimerCallbackType::Recurring(d)
} else {
TimerCallbackType::OneShot
};
match timer.callbacks.push(TimerCallback {
t0: timer.t + d,
f,
t_type,
}) {
Ok(()) => Ok(timer.callbacks.len() - 1),
Err(_) => Err(TimerError::TooManyTimers),
}
} else {
panic!("Timer not initialized");
}
})
}
pub fn ticks() -> Ticks {
critical_section::with(|cs| {
let mut timer = TIMER.borrow_ref_mut(cs);
if let Some(ref mut timer) = *timer {
timer.t
} else {
panic!("Timer not initialized");
}
})
}
#[exception]
fn SysTick() {
let pending = critical_section::with(|cs| {
let mut timer = TIMER.borrow_ref_mut(cs);
if let Some(ref mut timer) = *timer {
timer.tick();
timer.gather_pending()
} else {
panic!("Timer not initialized");
}
});
for f in pending {
(f)();
}
// Do a scheduling pass
SCB::set_pendsv();
}