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