1use crate::{Mcu, pac::Interrupt};
2use alloc::boxed::Box;
3use core::{
4 cell::{Cell, UnsafeCell},
5 mem::MaybeUninit,
6};
7
8pub struct Callback {
9 callback: UnsafeCell<MaybeUninit<Box<dyn FnMut()>>>,
10 once_flag: critical_section::Mutex<Cell<bool>>,
11 it_line: Interrupt,
12}
13
14unsafe impl Sync for Callback {}
15
16impl Callback {
20 pub const fn new(it_line: Interrupt) -> Self {
21 Self {
22 callback: UnsafeCell::new(MaybeUninit::uninit()),
23 once_flag: critical_section::Mutex::new(Cell::new(true)),
24 it_line,
25 }
26 }
27
28 pub fn set(&self, mcu: &mut Mcu, callback: impl FnMut() + 'static) {
31 let cb = Box::new(callback);
32 critical_section::with(|cs| {
33 assert!(self.once_flag.borrow(cs).get());
34 let callback = unsafe { &mut *self.callback.get() };
35 callback.write(cb);
36 self.once_flag.borrow(cs).set(false);
37 });
38 mcu.nvic.enable(self.it_line, true);
39 }
40
41 #[inline(always)]
45 pub unsafe fn call(&self) {
46 let cb = unsafe { (&mut *self.callback.get()).assume_init_mut() }.as_mut();
47 (*cb)();
48 }
49}
50
51#[macro_export]
52macro_rules! interrupt_handler {
53 ($(
54 ($LINE:ident, $CALLBACK:ident),
55 )+) => {
56 use $crate::pac::interrupt;
57 $(
58 pub static $CALLBACK: $crate::interrupt::Callback =
59 $crate::interrupt::Callback::new($crate::pac::Interrupt::$LINE);
60
61 #[allow(non_snake_case)]
62 #[interrupt]
63 fn $LINE() {
64 unsafe { $CALLBACK.call() }
65 }
66 )+
67 };
68}