py32_hal/
exti.rs

1//! External Interrupts (EXTI)
2
3// The following code is modified from embassy-stm32
4// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32
5// Special thanks to the Embassy Project and its contributors for their work!
6
7use core::convert::Infallible;
8use core::future::Future;
9use core::marker::PhantomData;
10use core::pin::Pin;
11use core::task::{Context, Poll};
12
13use embassy_hal_internal::{impl_peripheral, into_ref};
14use embassy_sync::waitqueue::AtomicWaker;
15
16use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull};
17use crate::pac::exti::regs::Lines;
18use crate::pac::EXTI;
19use crate::{interrupt, pac, peripherals, Peripheral};
20
21const EXTI_COUNT: usize = 16;
22const NEW_AW: AtomicWaker = AtomicWaker::new();
23static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT];
24
25fn cpu_regs() -> pac::exti::Exti {
26    EXTI
27}
28
29fn exticr_regs() -> pac::exti::Exti {
30    EXTI
31}
32
33unsafe fn on_irq() {
34    let bits = EXTI.pr().read().0;
35
36    // We don't handle or change any EXTI lines above 16.
37    let bits = bits & 0x0000FFFF;
38
39    // Mask all the channels that fired.
40    cpu_regs().imr().modify(|w| w.0 &= !bits);
41
42    // Wake the tasks
43    for pin in BitIter(bits) {
44        EXTI_WAKERS[pin as usize].wake();
45    }
46
47    // Clear pending
48    EXTI.pr().write_value(Lines(bits));
49
50    // #[cfg(feature = "low-power")]
51    // crate::low_power::on_wakeup_irq();
52}
53
54struct BitIter(u32);
55
56impl Iterator for BitIter {
57    type Item = u32;
58
59    fn next(&mut self) -> Option<Self::Item> {
60        match self.0.trailing_zeros() {
61            32 => None,
62            b => {
63                self.0 &= !(1 << b);
64                Some(b)
65            }
66        }
67    }
68}
69
70/// EXTI input driver.
71///
72/// This driver augments a GPIO `Input` with EXTI functionality. EXTI is not
73/// built into `Input` itself because it needs to take ownership of the corresponding
74/// EXTI channel, which is a limited resource.
75///
76/// Pins PA5, PB5, PC5... all use EXTI channel 5, so you can't use EXTI on, say, PA5 and PC5 at the same time.
77pub struct ExtiInput<'d> {
78    pin: Input<'d>,
79}
80
81impl<'d> Unpin for ExtiInput<'d> {}
82
83impl<'d> ExtiInput<'d> {
84    /// Create an EXTI input.
85    pub fn new<T: GpioPin>(
86        pin: impl Peripheral<P = T> + 'd,
87        ch: impl Peripheral<P = T::ExtiChannel> + 'd,
88        pull: Pull,
89    ) -> Self {
90        into_ref!(pin, ch);
91
92        // Needed if using AnyPin+AnyChannel.
93        assert_eq!(pin.pin(), ch.number());
94
95        Self {
96            pin: Input::new(pin, pull),
97        }
98    }
99
100    /// Get whether the pin is high.
101    pub fn is_high(&self) -> bool {
102        self.pin.is_high()
103    }
104
105    /// Get whether the pin is low.
106    pub fn is_low(&self) -> bool {
107        self.pin.is_low()
108    }
109
110    /// Get the pin level.
111    pub fn get_level(&self) -> Level {
112        self.pin.get_level()
113    }
114
115    /// Asynchronously wait until the pin is high.
116    ///
117    /// This returns immediately if the pin is already high.
118    pub async fn wait_for_high(&mut self) {
119        let fut =
120            ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false);
121        if self.is_high() {
122            return;
123        }
124        fut.await
125    }
126
127    /// Asynchronously wait until the pin is low.
128    ///
129    /// This returns immediately if the pin is already low.
130    pub async fn wait_for_low(&mut self) {
131        let fut =
132            ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true);
133        if self.is_low() {
134            return;
135        }
136        fut.await
137    }
138
139    /// Asynchronously wait until the pin sees a rising edge.
140    ///
141    /// If the pin is already high, it will wait for it to go low then back high.
142    pub async fn wait_for_rising_edge(&mut self) {
143        ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await
144    }
145
146    /// Asynchronously wait until the pin sees a falling edge.
147    ///
148    /// If the pin is already low, it will wait for it to go high then back low.
149    pub async fn wait_for_falling_edge(&mut self) {
150        ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await
151    }
152
153    /// Asynchronously wait until the pin sees any edge (either rising or falling).
154    pub async fn wait_for_any_edge(&mut self) {
155        ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await
156    }
157}
158
159impl<'d> embedded_hal_02::digital::v2::InputPin for ExtiInput<'d> {
160    type Error = Infallible;
161
162    fn is_high(&self) -> Result<bool, Self::Error> {
163        Ok(self.is_high())
164    }
165
166    fn is_low(&self) -> Result<bool, Self::Error> {
167        Ok(self.is_low())
168    }
169}
170
171impl<'d> embedded_hal_1::digital::ErrorType for ExtiInput<'d> {
172    type Error = Infallible;
173}
174
175impl<'d> embedded_hal_1::digital::InputPin for ExtiInput<'d> {
176    fn is_high(&mut self) -> Result<bool, Self::Error> {
177        Ok((*self).is_high())
178    }
179
180    fn is_low(&mut self) -> Result<bool, Self::Error> {
181        Ok((*self).is_low())
182    }
183}
184
185impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> {
186    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
187        self.wait_for_high().await;
188        Ok(())
189    }
190
191    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
192        self.wait_for_low().await;
193        Ok(())
194    }
195
196    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
197        self.wait_for_rising_edge().await;
198        Ok(())
199    }
200
201    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
202        self.wait_for_falling_edge().await;
203        Ok(())
204    }
205
206    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
207        self.wait_for_any_edge().await;
208        Ok(())
209    }
210}
211
212#[must_use = "futures do nothing unless you `.await` or poll them"]
213struct ExtiInputFuture<'a> {
214    pin: u8,
215    phantom: PhantomData<&'a mut AnyPin>,
216}
217
218impl<'a> ExtiInputFuture<'a> {
219    fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self {
220        critical_section::with(|_| {
221            let pin = pin as usize;
222
223            // The port_sel of GPIOF is 2, but embassy seems to handle this automatically, requiring no extra processing.
224            exticr_regs()
225                .exticr(pin / 4)
226                .modify(|w| w.set_exti(pin % 4, port));
227            EXTI.rtsr().modify(|w| w.set_line(pin, rising));
228            EXTI.ftsr().modify(|w| w.set_line(pin, falling));
229
230            // clear pending bit
231            EXTI.pr().write(|w| w.set_line(pin, true));
232
233            cpu_regs().imr().modify(|w| w.set_line(pin, true));
234        });
235
236        Self {
237            pin,
238            phantom: PhantomData,
239        }
240    }
241}
242
243impl<'a> Drop for ExtiInputFuture<'a> {
244    fn drop(&mut self) {
245        critical_section::with(|_| {
246            let pin = self.pin as _;
247            cpu_regs().imr().modify(|w| w.set_line(pin, false));
248        });
249    }
250}
251
252impl<'a> Future for ExtiInputFuture<'a> {
253    type Output = ();
254
255    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
256        EXTI_WAKERS[self.pin as usize].register(cx.waker());
257
258        let imr = cpu_regs().imr().read();
259        if !imr.line(self.pin as _) {
260            Poll::Ready(())
261        } else {
262            Poll::Pending
263        }
264    }
265}
266
267macro_rules! foreach_exti_irq {
268    ($action:ident) => {
269        foreach_interrupt!(
270            (EXTI0)  => { $action!(EXTI0); };
271            (EXTI1)  => { $action!(EXTI1); };
272            (EXTI2)  => { $action!(EXTI2); };
273            (EXTI3)  => { $action!(EXTI3); };
274            (EXTI4)  => { $action!(EXTI4); };
275            (EXTI5)  => { $action!(EXTI5); };
276            (EXTI6)  => { $action!(EXTI6); };
277            (EXTI7)  => { $action!(EXTI7); };
278            (EXTI8)  => { $action!(EXTI8); };
279            (EXTI9)  => { $action!(EXTI9); };
280            (EXTI10) => { $action!(EXTI10); };
281            (EXTI11) => { $action!(EXTI11); };
282            (EXTI12) => { $action!(EXTI12); };
283            (EXTI13) => { $action!(EXTI13); };
284            (EXTI14) => { $action!(EXTI14); };
285            (EXTI15) => { $action!(EXTI15); };
286
287            // plus the weird ones
288            (EXTI0_1)   => { $action!( EXTI0_1 ); };
289            (EXTI15_10) => { $action!(EXTI15_10); };
290            (EXTI15_4)  => { $action!(EXTI15_4); };
291            (EXTI1_0)   => { $action!(EXTI1_0); };
292            (EXTI2_3)   => { $action!(EXTI2_3); };
293            (EXTI2_TSC) => { $action!(EXTI2_TSC); };
294            (EXTI3_2)   => { $action!(EXTI3_2); };
295            (EXTI4_15)  => { $action!(EXTI4_15); };
296            (EXTI9_5)   => { $action!(EXTI9_5); };
297        );
298    };
299}
300
301macro_rules! impl_irq {
302    ($e:ident) => {
303        #[allow(non_snake_case)]
304        #[cfg(feature = "rt")]
305        #[interrupt]
306        unsafe fn $e() {
307            on_irq()
308        }
309    };
310}
311
312foreach_exti_irq!(impl_irq);
313
314trait SealedChannel {}
315
316/// EXTI channel trait.
317#[allow(private_bounds)]
318pub trait Channel: SealedChannel + Sized {
319    /// Get the EXTI channel number.
320    fn number(&self) -> u8;
321
322    /// Type-erase (degrade) this channel into an `AnyChannel`.
323    ///
324    /// This converts EXTI channel singletons (`EXTI0`, `EXTI1`, ...), which
325    /// are all different types, into the same type. It is useful for
326    /// creating arrays of channels, or avoiding generics.
327    fn degrade(self) -> AnyChannel {
328        AnyChannel {
329            number: self.number() as u8,
330        }
331    }
332}
333
334/// Type-erased (degraded) EXTI channel.
335///
336/// This represents ownership over any EXTI channel, known at runtime.
337pub struct AnyChannel {
338    number: u8,
339}
340
341impl_peripheral!(AnyChannel);
342impl SealedChannel for AnyChannel {}
343impl Channel for AnyChannel {
344    fn number(&self) -> u8 {
345        self.number
346    }
347}
348
349macro_rules! impl_exti {
350    ($type:ident, $number:expr) => {
351        impl SealedChannel for peripherals::$type {}
352        impl Channel for peripherals::$type {
353            fn number(&self) -> u8 {
354                $number
355            }
356        }
357    };
358}
359
360impl_exti!(EXTI0, 0);
361impl_exti!(EXTI1, 1);
362impl_exti!(EXTI2, 2);
363impl_exti!(EXTI3, 3);
364impl_exti!(EXTI4, 4);
365impl_exti!(EXTI5, 5);
366impl_exti!(EXTI6, 6);
367impl_exti!(EXTI7, 7);
368impl_exti!(EXTI8, 8);
369impl_exti!(EXTI9, 9);
370impl_exti!(EXTI10, 10);
371impl_exti!(EXTI11, 11);
372impl_exti!(EXTI12, 12);
373impl_exti!(EXTI13, 13);
374impl_exti!(EXTI14, 14);
375impl_exti!(EXTI15, 15);
376
377macro_rules! enable_irq {
378    ($e:ident) => {
379        crate::interrupt::typelevel::$e::enable();
380    };
381}
382
383/// safety: must be called only once
384pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) {
385    use crate::interrupt::typelevel::Interrupt;
386
387    foreach_exti_irq!(enable_irq);
388}