use crate::{Mcu, pac::Interrupt};
use alloc::boxed::Box;
use core::{
cell::{Cell, UnsafeCell},
mem::MaybeUninit,
};
pub struct Callback {
callback: UnsafeCell<MaybeUninit<Box<dyn FnMut()>>>,
once_flag: critical_section::Mutex<Cell<bool>>,
it_line: Interrupt,
}
unsafe impl Sync for Callback {}
impl Callback {
pub const fn new(it_line: Interrupt) -> Self {
Self {
callback: UnsafeCell::new(MaybeUninit::uninit()),
once_flag: critical_section::Mutex::new(Cell::new(true)),
it_line,
}
}
pub fn set(&self, mcu: &mut Mcu, callback: impl FnMut() + 'static) {
let cb = Box::new(callback);
critical_section::with(|cs| {
assert!(self.once_flag.borrow(cs).get());
let callback = unsafe { &mut *self.callback.get() };
callback.write(cb);
self.once_flag.borrow(cs).set(false);
});
mcu.nvic.enable(self.it_line, true);
}
#[inline(always)]
pub unsafe fn call(&self) {
let cb = unsafe { (&mut *self.callback.get()).assume_init_mut() }.as_mut();
(*cb)();
}
}
#[macro_export]
macro_rules! interrupt_handler {
($(
($LINE:ident, $CALLBACK:ident),
)+) => {
use $crate::pac::interrupt;
$(
pub static $CALLBACK: $crate::interrupt::Callback =
$crate::interrupt::Callback::new($crate::pac::Interrupt::$LINE);
#[allow(non_snake_case)]
#[interrupt]
fn $LINE() {
unsafe { $CALLBACK.call() }
}
)+
};
}