ariel_os_stm32/
extint_registry.rs

1use ariel_os_embassy_common::gpio::input::InterruptError;
2use embassy_stm32::{
3    exti::{AnyChannel, Channel},
4    gpio::Pin,
5    peripherals, OptionalPeripherals, Peripheral,
6};
7use portable_atomic::{AtomicBool, AtomicU16, Ordering};
8
9pub static EXTINT_REGISTRY: ExtIntRegistry = ExtIntRegistry::new();
10
11pub struct ExtIntRegistry {
12    initialized: AtomicBool,
13    used_interrupt_channels: AtomicU16, // 16 channels
14}
15
16impl ExtIntRegistry {
17    // Collect all channel peripherals so that the registry is the only one managing them.
18    const fn new() -> Self {
19        Self {
20            initialized: AtomicBool::new(false),
21            used_interrupt_channels: AtomicU16::new(0),
22        }
23    }
24
25    pub fn init(&self, peripherals: &mut OptionalPeripherals) {
26        peripherals.EXTI0.take().unwrap();
27        peripherals.EXTI1.take().unwrap();
28        peripherals.EXTI2.take().unwrap();
29        peripherals.EXTI3.take().unwrap();
30        peripherals.EXTI4.take().unwrap();
31        peripherals.EXTI5.take().unwrap();
32        peripherals.EXTI6.take().unwrap();
33        peripherals.EXTI7.take().unwrap();
34        peripherals.EXTI8.take().unwrap();
35        peripherals.EXTI9.take().unwrap();
36        peripherals.EXTI10.take().unwrap();
37        peripherals.EXTI11.take().unwrap();
38        peripherals.EXTI12.take().unwrap();
39        peripherals.EXTI13.take().unwrap();
40        peripherals.EXTI14.take().unwrap();
41        peripherals.EXTI15.take().unwrap();
42
43        self.initialized.store(true, Ordering::Release);
44
45        // Do nothing else, just consume the peripherals: they are ours now!
46    }
47
48    pub fn get_interrupt_channel_for_pin<P: Peripheral<P = T>, T: Pin>(
49        &self,
50        pin: P,
51    ) -> Result<AnyChannel, InterruptError> {
52        // Make sure that the interrupt channels have been captured during initialization.
53        assert!(self.initialized.load(Ordering::Acquire));
54
55        let pin = pin.into_ref().map_into();
56        let pin_number = pin.pin();
57
58        // As interrupt channels are mutually exclusive between ports (ie., if channel i has
59        // been bound for pin i of a port, it cannot be used for pin i of another port), we
60        // only check the pin number.
61        // NOTE(ordering): since setting a bit is an idempotent operation, and since we do not
62        // allow clearing them, the ordering does not matter.
63        let was_used = self
64            .used_interrupt_channels
65            .bit_set(pin_number.into(), Ordering::Relaxed);
66
67        if was_used {
68            return Err(InterruptError::IntChannelAlreadyUsed);
69        }
70
71        // They are the same
72        let ch_number = pin_number;
73
74        // NOTE(embassy): ideally we would be using `T::ExtiChannel::steal()` instead of this
75        // match, but Embassy does not provide this.
76        // SAFETY: this function enforces that the same channel cannot be obtained twice,
77        // making sure multiple instances are not used at the same time as the mandatory
78        // `init()` method has collected all channel peripherals beforehand.
79        let ch = match ch_number {
80            0 => unsafe { peripherals::EXTI0::steal() }.degrade(),
81            1 => unsafe { peripherals::EXTI1::steal() }.degrade(),
82            2 => unsafe { peripherals::EXTI2::steal() }.degrade(),
83            3 => unsafe { peripherals::EXTI3::steal() }.degrade(),
84            4 => unsafe { peripherals::EXTI4::steal() }.degrade(),
85            5 => unsafe { peripherals::EXTI5::steal() }.degrade(),
86            6 => unsafe { peripherals::EXTI6::steal() }.degrade(),
87            7 => unsafe { peripherals::EXTI7::steal() }.degrade(),
88            8 => unsafe { peripherals::EXTI8::steal() }.degrade(),
89            9 => unsafe { peripherals::EXTI9::steal() }.degrade(),
90            10 => unsafe { peripherals::EXTI10::steal() }.degrade(),
91            11 => unsafe { peripherals::EXTI11::steal() }.degrade(),
92            12 => unsafe { peripherals::EXTI12::steal() }.degrade(),
93            13 => unsafe { peripherals::EXTI13::steal() }.degrade(),
94            14 => unsafe { peripherals::EXTI14::steal() }.degrade(),
95            15 => unsafe { peripherals::EXTI15::steal() }.degrade(),
96            _ => unreachable!(),
97        };
98
99        Ok(ch)
100    }
101}