Skip to main content

vorago_shared_hal/gpio/
asynch.rs

1//! # Async GPIO functionality for the Vorago GPIO peripherals.
2//!
3//! This module provides the [InputPinAsync] which implements
4//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
5//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
6//! which must be provided for async support to work. However, it provides the
7//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
8//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
9#![deny(missing_docs)]
10use core::future::Future;
11
12use embassy_sync::waitqueue::AtomicWaker;
13use embedded_hal_async::digital::Wait;
14use portable_atomic::AtomicBool;
15
16#[cfg(feature = "vor4x")]
17use crate::NUM_PORT_DEFAULT;
18#[cfg(feature = "vor1x")]
19use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
20
21#[cfg(feature = "vor4x")]
22use super::ll::PortDoesNotSupportInterrupts;
23
24pub use super::ll::InterruptEdge;
25use super::{
26    Input, Port,
27    ll::{DynPinId, LowLevelGpio},
28};
29
30cfg_if::cfg_if! {
31    if #[cfg(feature = "vor1x")] {
32        static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_A] = [const { AtomicWaker::new() }; NUM_PORT_A];
33        static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_B] = [const { AtomicWaker::new() }; NUM_PORT_B];
34        static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_A] =
35            [const { AtomicBool::new(false) }; NUM_PORT_A];
36        static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] =
37            [const { AtomicBool::new(false) }; NUM_PORT_B];
38    } else {
39        static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_DEFAULT] =
40            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
41        static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_DEFAULT] =
42            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
43        static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PORT_DEFAULT] =
44            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
45        static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PORT_DEFAULT] =
46            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
47        static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PORT_DEFAULT] =
48            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
49        static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PORT_DEFAULT] =
50            [const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
51
52        static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_DEFAULT] =
53            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
54        static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_DEFAULT] =
55            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
56        static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PORT_DEFAULT] =
57            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
58        static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PORT_DEFAULT] =
59            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
60        static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PORT_DEFAULT] =
61            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
62        static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PORT_DEFAULT] =
63            [const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
64    }
65}
66
67#[inline]
68fn pin_group_to_waker_and_edge_detection_group(
69    port: Port,
70) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
71    match port {
72        Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
73        Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
74        #[cfg(feature = "vor4x")]
75        Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
76        #[cfg(feature = "vor4x")]
77        Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
78        #[cfg(feature = "vor4x")]
79        Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
80        #[cfg(feature = "vor4x")]
81        Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
82        #[cfg(feature = "vor4x")]
83        Port::G => unreachable!(),
84    }
85}
86
87/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
88///
89/// This function should be called in all interrupt handlers which handle any GPIO interrupts
90/// matching the [Port] argument.
91/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
92/// as well as update the static edge detection structures. This allows the pin future to complete
93/// complete async operations.
94#[cfg(feature = "vor1x")]
95pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
96    on_interrupt_for_async_gpio_for_port_generic(port);
97}
98
99/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
100///
101/// This function should be called in all interrupt handlers which handle any GPIO interrupts
102/// matching the [Port] argument.
103/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
104/// as well as update the static edge detection structures. This allows the pin future to complete
105/// complete async operations.
106#[cfg(feature = "vor4x")]
107pub fn on_interrupt_for_async_gpio_for_port(
108    port: Port,
109) -> Result<(), PortDoesNotSupportInterrupts> {
110    if port == Port::G {
111        return Err(PortDoesNotSupportInterrupts);
112    }
113    on_interrupt_for_async_gpio_for_port_generic(port);
114    Ok(())
115}
116
117fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
118    let mut gpio = unsafe { port.steal_regs() };
119
120    let irq_enb = gpio.read_irq_enable();
121    let edge_status = gpio.read_edge_status();
122    let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
123
124    on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
125}
126
127#[inline]
128fn on_interrupt_for_port(
129    mut irq_enb: u32,
130    edge_status: u32,
131    wakers: &'static [AtomicWaker],
132    edge_detection: &'static [AtomicBool],
133) {
134    // Check all enabled interrupts.
135    while irq_enb != 0 {
136        // For all enabled interrupts, check whether the corresponding edge detection has
137        // triggered.
138        let bit_pos = irq_enb.trailing_zeros() as usize;
139        let bit_mask = 1 << bit_pos;
140
141        if edge_status & bit_mask != 0 {
142            edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
143            wakers[bit_pos].wake();
144        }
145        // Clear the processed bit
146        irq_enb &= !bit_mask;
147    }
148}
149
150/// Input pin future which implements the [Future] trait.
151///
152/// Generally, you want to use the [InputPinAsync] types instead of this
153/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
154/// struture is granted  to allow writing custom async structures.
155pub struct InputPinFuture {
156    id: DynPinId,
157    waker_group: &'static [AtomicWaker],
158    edge_detection_group: &'static [AtomicBool],
159}
160
161impl InputPinFuture {
162    /// Create a new input pin future from mutable reference to an [Input] pin.
163    #[cfg(feature = "vor1x")]
164    pub fn new_with_input_pin(pin: &mut Input, edge: InterruptEdge) -> Self {
165        let (waker_group, edge_detection_group) =
166            pin_group_to_waker_and_edge_detection_group(pin.id().port());
167        edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
168        pin.configure_edge_interrupt(edge);
169        pin.enable_interrupt_gpio_only();
170        Self {
171            id: pin.id(),
172            waker_group,
173            edge_detection_group,
174        }
175    }
176
177    /// Create a new input pin future from mutable reference to an [Input] pin.
178    #[cfg(feature = "vor4x")]
179    pub fn new_with_input_pin(
180        pin: &mut Input,
181        edge: InterruptEdge,
182    ) -> Result<Self, PortDoesNotSupportInterrupts> {
183        let (waker_group, edge_detection_group) =
184            pin_group_to_waker_and_edge_detection_group(pin.id().port());
185        pin.configure_edge_interrupt(edge);
186        pin.enable_interrupt_gpio_only();
187        Ok(Self {
188            id: pin.id(),
189            waker_group,
190            edge_detection_group,
191        })
192    }
193}
194
195impl Drop for InputPinFuture {
196    fn drop(&mut self) {
197        let mut ll = LowLevelGpio::new(self.id);
198        #[cfg(feature = "vor1x")]
199        ll.disable_interrupt(false);
200        #[cfg(feature = "vor4x")]
201        ll.disable_interrupt();
202    }
203}
204
205impl Future for InputPinFuture {
206    type Output = ();
207    fn poll(
208        self: core::pin::Pin<&mut Self>,
209        cx: &mut core::task::Context<'_>,
210    ) -> core::task::Poll<Self::Output> {
211        let idx = self.id.offset();
212        self.waker_group[idx].register(cx.waker());
213        if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
214            return core::task::Poll::Ready(());
215        }
216        core::task::Poll::Pending
217    }
218}
219
220/// Input pin which has additional asynchronous support.
221pub struct InputPinAsync {
222    pin: Input,
223}
224
225impl InputPinAsync {
226    /// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
227    /// passed as well and is used to route and enable the interrupt.
228    ///
229    /// Please note that the interrupt handler itself must be provided by the user and the
230    /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
231    /// for the asynchronous functionality to work.
232    #[cfg(feature = "vor1x")]
233    pub fn new(mut pin: Input, irq_config: InterruptConfig) -> Self {
234        // Do not enable GPIO interrupt bit yet.
235        pin.enable_interrupt(irq_config, false);
236        Self { pin }
237    }
238
239    /// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
240    /// passed as well and is used to route and enable the interrupt.
241    ///
242    /// Please note that the interrupt handler itself must be provided by the user and the
243    /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
244    /// for the asynchronous functionality to work.
245    #[cfg(feature = "vor4x")]
246    pub fn new(mut pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
247        if pin.id().port() == Port::G {
248            return Err(PortDoesNotSupportInterrupts);
249        }
250        // Do not enable GPIO interrupt bit yet.
251        pin.enable_interrupt(true, false)?;
252        Ok(Self { pin })
253    }
254
255    /// Asynchronously wait until the pin is high.
256    ///
257    /// This returns immediately if the pin is already high.
258    pub async fn wait_for_high(&mut self) {
259        // Unwrap okay, checked pin in constructor.
260        #[cfg(feature = "vor1x")]
261        let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh);
262        #[cfg(feature = "vor4x")]
263        let fut =
264            InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
265        if self.pin.is_high() {
266            return;
267        }
268        fut.await;
269    }
270
271    /// Check whether pin is high.
272    #[inline]
273    pub fn is_high(&self) -> bool {
274        self.pin.is_high()
275    }
276
277    /// Check whether pin is low.
278    #[inline]
279    pub fn is_low(&self) -> bool {
280        self.pin.is_low()
281    }
282
283    /// Shared access to [Input] pin.
284    #[inline]
285    pub fn inner(&self) -> &Input {
286        &self.pin
287    }
288
289    /// Mutable access to [Input] pin.
290    #[inline]
291    pub fn inner_mut(&mut self) -> &mut Input {
292        &mut self.pin
293    }
294
295    /// Asynchronously wait until the pin is low.
296    ///
297    /// This returns immediately if the pin is already high.
298    pub async fn wait_for_low(&mut self) {
299        // Unwrap okay, checked pin in constructor.
300        #[cfg(feature = "vor1x")]
301        let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow);
302        #[cfg(feature = "vor4x")]
303        let fut =
304            InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
305        if self.pin.is_low() {
306            return;
307        }
308        fut.await;
309    }
310
311    /// Asynchronously wait until the pin sees a falling edge.
312    pub async fn wait_for_falling_edge(&mut self) {
313        // Unwrap okay, checked pin in constructor.
314        #[cfg(feature = "vor1x")]
315        InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).await;
316        #[cfg(feature = "vor4x")]
317        InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
318            .unwrap()
319            .await;
320    }
321
322    /// Asynchronously wait until the pin sees a rising edge.
323    pub async fn wait_for_rising_edge(&mut self) {
324        // Unwrap okay, checked pin in constructor.
325        #[cfg(feature = "vor1x")]
326        InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).await;
327    }
328
329    /// Asynchronously wait until the pin sees any edge (either rising or falling).
330    pub async fn wait_for_any_edge(&mut self) {
331        // Unwrap okay, checked pin in constructor.
332        #[cfg(feature = "vor1x")]
333        InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges).await;
334        #[cfg(feature = "vor4x")]
335        InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
336            .unwrap()
337            .await;
338    }
339
340    /// Release the contained [Input] pin.
341    #[inline]
342    pub fn release(self) -> Input {
343        self.pin
344    }
345}
346
347impl embedded_hal::digital::ErrorType for InputPinAsync {
348    type Error = core::convert::Infallible;
349}
350
351impl Wait for InputPinAsync {
352    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
353        self.wait_for_high().await;
354        Ok(())
355    }
356
357    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
358        self.wait_for_low().await;
359        Ok(())
360    }
361
362    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
363        self.wait_for_rising_edge().await;
364        Ok(())
365    }
366
367    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
368        self.wait_for_falling_edge().await;
369        Ok(())
370    }
371
372    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
373        self.wait_for_any_edge().await;
374        Ok(())
375    }
376}
377
378impl embedded_hal::digital::InputPin for InputPinAsync {
379    fn is_low(&mut self) -> Result<bool, Self::Error> {
380        Ok(self.inner().is_low())
381    }
382
383    fn is_high(&mut self) -> Result<bool, Self::Error> {
384        Ok(self.inner().is_high())
385    }
386}