embassy_stm32/
exti.rs

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