embedded_c_sdk_bind_hal/gpio/
exti.rs

1use crate::gpio::Pin;
2use crate::ll_api::{ll_cmd::*, GpioExtiFlag};
3
4pub enum ExtiMode {
5    Rising,
6    Falling,
7    RisingFalling,
8}
9
10impl ExtiMode {
11    pub(crate) fn to_flag(&self) -> GpioExtiFlag {
12        match self {
13            ExtiMode::Rising => GpioExtiFlag::Rising,
14            ExtiMode::Falling => GpioExtiFlag::Falling,
15            ExtiMode::RisingFalling => GpioExtiFlag::RisingFalling,
16        }
17    }
18}
19
20impl<'d> super::pin::Input<'d> {
21    /// Configures the EXTI (External Interrupt) mode for the pin.
22    ///
23    /// # Arguments
24    /// * `mode` - The EXTI mode to set. This can be rising edge, falling edge, both edges, etc.
25    pub fn exti_config(&self, mode: ExtiMode) {
26        let port = self.pin.pin.port();
27        let pin = self.pin.pin.pin();
28
29        ll_invoke_inner!(INVOKE_ID_GPIO_EXTI, port, pin, mode.to_flag());
30    }
31
32    /// Enables the interrupt for the pin.
33    ///
34    /// This function can be reenabled after disabling it.
35    pub fn enable_interrupt(&self) {
36        let port = self.pin.pin.port();
37        let pin = self.pin.pin.pin();
38
39        ll_invoke_inner!(INVOKE_ID_GPIO_EXTI, port, pin, GpioExtiFlag::Enable);
40    }
41
42    /// Disables the interrupt for the pin.
43    ///
44    /// This function can be used to temporarily disable the interrupt without changing the EXTI mode.
45    pub fn disable_interrupt(&self) {
46        let port = self.pin.pin.port();
47        let pin = self.pin.pin.pin();
48
49        ll_invoke_inner!(INVOKE_ID_GPIO_EXTI, port, pin, GpioExtiFlag::Disable);
50    }
51}
52
53#[allow(non_snake_case)]
54#[no_mangle]
55/// An unsafe C-compatible external function for hook EXTI IRQs.
56/// # Arguments
57/// * `_line` - The EXTI line number that triggered the interrupt. This corresponds to the pin number.
58unsafe extern "C" fn EXTI_IRQ_hook_rs(_line: u8) {
59    #[cfg(all(feature = "embassy", feature = "exti-async"))]
60    // If the 'embassy' feature is enabled, update the status of the EXTI event in the embassy framework's future system.
61    exti_future::exti_set_status(_line);
62
63    #[cfg(feature = "exti-irq-callback")]
64    {
65        // If the 'exti-irq-callback' feature is enabled, call the user-provided callback function.
66        // This allows users to define their own ISR handling logic.
67        extern "Rust" {
68            fn exti_irq_callback(line: u8);
69        }
70        exti_irq_callback(_line);
71    }
72}
73
74#[cfg(all(feature = "embassy", feature = "exti-async"))]
75pub mod exti_future {
76    use crate::gpio::Pin;
77    use core::future::Future;
78    use core::task::{Context, Poll};
79    use embassy_sync::waitqueue::AtomicWaker;
80    use portable_atomic::{AtomicU16, Ordering};
81
82    const EXTI_COUNT: usize = 16;
83    const NEW_AW: AtomicWaker = AtomicWaker::new();
84    static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT];
85    static EXTI_STATUS: AtomicU16 = AtomicU16::new(0);
86
87    #[inline]
88    pub fn exti_set_status(line: u8) {
89        if line < EXTI_COUNT as u8 {
90            EXTI_STATUS.bit_set(line as u32, Ordering::Relaxed);
91            EXTI_WAKERS[line as usize].wake();
92        }
93    }
94
95    impl<'d> super::super::pin::Input<'d> {
96        pub async fn wait_for_high(&mut self) {
97            let port = self.pin.pin.port();
98            let pin = self.pin.pin.pin();
99            let fut = ExtiInputFuture::new(port, pin, super::ExtiMode::Rising);
100            if Self::is_high(self) {
101                return;
102            }
103            fut.await
104        }
105
106        pub async fn wait_for_low(&mut self) {
107            let port = self.pin.pin.port();
108            let pin = self.pin.pin.pin();
109            let fut = ExtiInputFuture::new(port, pin, super::ExtiMode::Falling);
110            if !Self::is_high(self) {
111                return;
112            }
113            fut.await
114        }
115
116        pub async fn wait_for_rising_edge(&mut self) {
117            let port = self.pin.pin.port();
118            let pin = self.pin.pin.pin();
119            ExtiInputFuture::new(port, pin, super::ExtiMode::Rising).await
120        }
121
122        pub async fn wait_for_falling_edge(&mut self) {
123            let port = self.pin.pin.port();
124            let pin = self.pin.pin.pin();
125            ExtiInputFuture::new(port, pin, super::ExtiMode::Falling).await
126        }
127
128        pub async fn wait_for_any_edge(&mut self) {
129            let port = self.pin.pin.port();
130            let pin = self.pin.pin.pin();
131            ExtiInputFuture::new(port, pin, super::ExtiMode::RisingFalling).await
132        }
133    }
134
135    struct ExtiInputFuture {
136        port: u8,
137        pin: u8,
138    }
139
140    impl ExtiInputFuture {
141        fn new(port: u8, pin: u8, mode: super::ExtiMode) -> Self {
142            ll_invoke_inner!(crate::INVOKE_ID_GPIO_EXTI, port, pin, mode.to_flag());
143            //ll_invoke_inner!(crate::INVOKE_ID_GPIO_EXTI, pin, super::GpioExtiFlag::Enable);
144            Self { port, pin }
145        }
146    }
147
148    impl Drop for ExtiInputFuture {
149        fn drop(&mut self) {
150            ll_invoke_inner!(
151                crate::INVOKE_ID_GPIO_EXTI,
152                self.port,
153                self.pin,
154                super::GpioExtiFlag::Disable
155            );
156        }
157    }
158
159    impl Future for ExtiInputFuture {
160        type Output = ();
161
162        fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
163            EXTI_WAKERS[self.pin as usize].register(cx.waker());
164
165            if EXTI_STATUS.bit_clear(self.pin as u32, Ordering::Relaxed) {
166                Poll::Ready(())
167            } else {
168                Poll::Pending
169            }
170        }
171    }
172
173    impl<'d> embedded_hal_async::digital::Wait for super::super::Input<'d> {
174        async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
175            self.wait_for_high().await;
176            Ok(())
177        }
178
179        async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
180            self.wait_for_low().await;
181            Ok(())
182        }
183
184        async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
185            self.wait_for_rising_edge().await;
186            Ok(())
187        }
188
189        async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
190            self.wait_for_falling_edge().await;
191            Ok(())
192        }
193
194        async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
195            self.wait_for_any_edge().await;
196            Ok(())
197        }
198    }
199}