Skip to main content

ws63_hal/
gpio.rs

1//! GPIO driver for WS63 (19 pins: GPIO0 bits 7-0, GPIO1 bits 15-8, GPIO2 bits 18-16).
2//!
3//! Three GPIO blocks at 0x4402_8000, 0x4402_9000, 0x4402_A000.
4//!
5//! # Pin drivers
6//!
7//! - [`Input`] — digital input with configurable pull
8//! - [`Output`] — push-pull output
9//! - [`Flex`] — combined input + output driver
10//! - [`AnyPin`] — type-erased pin
11
12use crate::peripherals::{Gpio0, Gpio1, Gpio2, IoConfig};
13use core::marker::PhantomData;
14
15// ── Configuration types ───────────────────────────────────────────
16
17/// Pull resistor configuration.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Pull {
20    None,
21    Up,
22    Down,
23}
24
25/// GPIO interrupt trigger condition (sets `GPIO_INT_TYPE` + `GPIO_INT_POLARITY`).
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum InterruptTrigger {
28    /// Edge-sensitive, low→high transition.
29    RisingEdge,
30    /// Edge-sensitive, high→low transition.
31    FallingEdge,
32    /// Level-sensitive, asserted while high.
33    HighLevel,
34    /// Level-sensitive, asserted while low.
35    LowLevel,
36}
37
38/// Digital input configuration.
39#[derive(Debug, Clone, Copy)]
40pub struct InputConfig {
41    pub pull: Pull,
42}
43
44impl Default for InputConfig {
45    fn default() -> Self {
46        Self { pull: Pull::None }
47    }
48}
49
50impl InputConfig {
51    pub const fn new() -> Self {
52        Self { pull: Pull::None }
53    }
54    pub const fn with_pull(mut self, pull: Pull) -> Self {
55        self.pull = pull;
56        self
57    }
58}
59
60/// Digital output configuration.
61#[derive(Debug, Clone, Copy, Default)]
62pub struct OutputConfig {
63    pub open_drain: bool,
64    pub initial_high: bool,
65}
66
67impl OutputConfig {
68    pub const fn new() -> Self {
69        Self { open_drain: false, initial_high: false }
70    }
71    pub const fn with_open_drain(mut self, od: bool) -> Self {
72        self.open_drain = od;
73        self
74    }
75    pub const fn with_initial(mut self, high: bool) -> Self {
76        self.initial_high = high;
77        self
78    }
79}
80
81/// Mode marker types.
82pub struct InputMode;
83pub struct OutputMode;
84
85// ── Type-erased pin ────────────────────────────────────────────────
86
87/// A type-erased pin that can represent any GPIO pin.
88pub struct AnyPin<'d> {
89    block: u8,
90    bit: u8,
91    _lifetime: PhantomData<&'d mut ()>,
92}
93
94impl<'d> AnyPin<'d> {
95    /// Create an `AnyPin` from a raw pin number without checking.
96    ///
97    /// # Safety
98    /// The pin must be valid (0-18). This bypasses the type system.
99    pub unsafe fn steal(pin: u8) -> Self {
100        Self { block: pin / 8, bit: pin % 8, _lifetime: PhantomData }
101    }
102
103    /// Get the pin number (0-18).
104    pub fn number(&self) -> u8 {
105        self.block * 8 + self.bit
106    }
107
108    /// Set output enable (true = input, false = output).
109    fn set_oen(&self, input: bool) {
110        let r = regs(self.block);
111        let mask = 1 << self.bit;
112        if input {
113            r.gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
114        } else {
115            r.gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
116        }
117    }
118
119    /// Initialize this pin as a GPIO input.
120    ///
121    /// Applies `config.pull` to the pad via the IO_CONFIG pad register
122    /// (`apply_pull`; pins 0..=14 have a pad-control register).
123    pub fn init_input(self, config: InputConfig) -> Input<'d> {
124        self.set_oen(true);
125        apply_pull(self.number(), config.pull);
126        Input { pin: self, config }
127    }
128
129    /// Initialize this pin as a GPIO output.
130    pub fn init_output(self, config: OutputConfig) -> Output<'d> {
131        let mut out = Output { pin: self, config };
132        if config.initial_high {
133            out.set_high();
134        } else {
135            out.set_low();
136        }
137        out.pin.set_oen(false);
138        out
139    }
140
141    /// Initialize this pin as a Flex (combined input/output) driver.
142    pub fn init_flex(self, config: OutputConfig) -> Flex<'d> {
143        if config.initial_high {
144            unsafe { regs(self.block).gpio_data_set().write(|w| w.bits(1 << self.bit)) };
145        } else {
146            unsafe { regs(self.block).gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
147        }
148        self.set_oen(false);
149        Flex { pin: self, config }
150    }
151}
152
153// ── Digital input driver ──────────────────────────────────────────
154
155/// Digital input pin driver.
156pub struct Input<'d> {
157    pin: AnyPin<'d>,
158    #[allow(dead_code)]
159    config: InputConfig,
160}
161
162impl<'d> Input<'d> {
163    pub fn is_high(&self) -> bool {
164        (regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
165    }
166
167    pub fn is_low(&self) -> bool {
168        !self.is_high()
169    }
170
171    pub fn number(&self) -> u8 {
172        self.pin.number()
173    }
174
175    pub fn enable_interrupt(&self) {
176        let r = regs(self.pin.block);
177        r.gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin.bit)) });
178    }
179
180    pub fn disable_interrupt(&self) {
181        let r = regs(self.pin.block);
182        r.gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin.bit)) });
183    }
184
185    pub fn clear_interrupt(&self) {
186        unsafe { regs(self.pin.block).gpio_int_eoi().write(|w| w.bits(1 << self.pin.bit)) };
187    }
188
189    /// Set the interrupt trigger condition for this pin (edge/level + polarity).
190    ///
191    /// Configures `GPIO_INT_TYPE` (edge vs level) and `GPIO_INT_POLARITY`
192    /// (rising/high vs falling/low). Call before [`enable_interrupt`](Self::enable_interrupt).
193    pub fn set_interrupt_trigger(&self, trigger: InterruptTrigger) {
194        let r = regs(self.pin.block);
195        let mask = 1u32 << self.pin.bit;
196        let (edge, high) = match trigger {
197            InterruptTrigger::RisingEdge => (true, true),
198            InterruptTrigger::FallingEdge => (true, false),
199            InterruptTrigger::HighLevel => (false, true),
200            InterruptTrigger::LowLevel => (false, false),
201        };
202        r.gpio_int_type().modify(|r, w| unsafe { w.bits(if edge { r.bits() | mask } else { r.bits() & !mask }) });
203        r.gpio_int_polarity().modify(|r, w| unsafe { w.bits(if high { r.bits() | mask } else { r.bits() & !mask }) });
204    }
205
206    pub fn interrupt_pending(&self) -> bool {
207        (regs(self.pin.block).gpio_int_raw().read().bits() >> self.pin.bit) & 1 != 0
208    }
209}
210
211impl embedded_hal::digital::ErrorType for Input<'_> {
212    type Error = core::convert::Infallible;
213}
214
215impl embedded_hal::digital::InputPin for Input<'_> {
216    fn is_high(&mut self) -> Result<bool, Self::Error> {
217        Ok(Input::is_high(self))
218    }
219    fn is_low(&mut self) -> Result<bool, Self::Error> {
220        Ok(Input::is_low(self))
221    }
222}
223
224// ── Digital output driver ──────────────────────────────────────────
225
226/// Digital output pin driver.
227pub struct Output<'d> {
228    pin: AnyPin<'d>,
229    config: OutputConfig,
230}
231
232impl<'d> Output<'d> {
233    pub fn set_high(&mut self) {
234        unsafe { regs(self.pin.block).gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
235    }
236
237    pub fn set_low(&mut self) {
238        unsafe { regs(self.pin.block).gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
239    }
240
241    pub fn toggle(&mut self) {
242        let r = regs(self.pin.block);
243        let val = r.gpio_sw_out().read().bits();
244        if val & (1 << self.pin.bit) != 0 {
245            unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
246        } else {
247            unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
248        }
249    }
250
251    pub fn is_set_high(&self) -> bool {
252        (regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
253    }
254
255    pub fn number(&self) -> u8 {
256        self.pin.number()
257    }
258
259    /// Convert this output into a Flex pin.
260    pub fn into_flex(self) -> Flex<'d> {
261        Flex { pin: self.pin, config: self.config }
262    }
263}
264
265impl embedded_hal::digital::ErrorType for Output<'_> {
266    type Error = core::convert::Infallible;
267}
268
269impl embedded_hal::digital::OutputPin for Output<'_> {
270    fn set_low(&mut self) -> Result<(), Self::Error> {
271        Output::set_low(self);
272        Ok(())
273    }
274    fn set_high(&mut self) -> Result<(), Self::Error> {
275        Output::set_high(self);
276        Ok(())
277    }
278}
279
280impl embedded_hal::digital::StatefulOutputPin for Output<'_> {
281    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
282        Ok(Output::is_set_high(self))
283    }
284    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
285        Ok(!Output::is_set_high(self))
286    }
287}
288
289// ── Flex pin driver (combined input + output) ─────────────────────
290
291/// Combined input + output pin driver.
292pub struct Flex<'d> {
293    pin: AnyPin<'d>,
294    #[allow(dead_code)]
295    config: OutputConfig,
296}
297
298impl<'d> Flex<'d> {
299    pub fn set_high(&mut self) {
300        self.pin.set_oen(false);
301        unsafe { regs(self.pin.block).gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
302    }
303
304    pub fn set_low(&mut self) {
305        self.pin.set_oen(false);
306        unsafe { regs(self.pin.block).gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
307    }
308
309    pub fn toggle(&mut self) {
310        let r = regs(self.pin.block);
311        self.pin.set_oen(false);
312        let val = r.gpio_sw_out().read().bits();
313        if val & (1 << self.pin.bit) != 0 {
314            unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.pin.bit)) };
315        } else {
316            unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.pin.bit)) };
317        }
318    }
319
320    pub fn is_set_high(&self) -> bool {
321        (regs(self.pin.block).gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0
322    }
323
324    pub fn is_high(&self) -> bool {
325        // Save output enable state, switch to input, read, restore
326        let r = regs(self.pin.block);
327        let oen = r.gpio_sw_oen().read().bits();
328        self.pin.set_oen(true);
329        let val = (r.gpio_sw_out().read().bits() >> self.pin.bit) & 1 != 0;
330        // Restore original OEN state
331        let mask = 1 << self.pin.bit;
332        if oen & mask != 0 {
333            r.gpio_sw_oen().modify(|reg, w| unsafe { w.bits(reg.bits() | mask) });
334        } else {
335            r.gpio_sw_oen().modify(|reg, w| unsafe { w.bits(reg.bits() & !mask) });
336        }
337        val
338    }
339
340    pub fn is_low(&self) -> bool {
341        !self.is_high()
342    }
343
344    pub fn number(&self) -> u8 {
345        self.pin.number()
346    }
347}
348
349impl embedded_hal::digital::ErrorType for Flex<'_> {
350    type Error = core::convert::Infallible;
351}
352
353impl embedded_hal::digital::OutputPin for Flex<'_> {
354    fn set_low(&mut self) -> Result<(), Self::Error> {
355        Flex::set_low(self);
356        Ok(())
357    }
358    fn set_high(&mut self) -> Result<(), Self::Error> {
359        Flex::set_high(self);
360        Ok(())
361    }
362}
363
364impl embedded_hal::digital::StatefulOutputPin for Flex<'_> {
365    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
366        Ok(Flex::is_set_high(self))
367    }
368    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
369        Ok(!Flex::is_set_high(self))
370    }
371}
372
373impl embedded_hal::digital::InputPin for Flex<'_> {
374    fn is_high(&mut self) -> Result<bool, Self::Error> {
375        Ok(Flex::is_high(self))
376    }
377    fn is_low(&mut self) -> Result<bool, Self::Error> {
378        Ok(Flex::is_low(self))
379    }
380}
381
382// ── Internal register access ──────────────────────────────────────
383
384fn regs(block: u8) -> &'static ws63_pac::gpio0::RegisterBlock {
385    unsafe {
386        match block {
387            0 => &*Gpio0::ptr(),
388            1 => &*Gpio1::ptr(),
389            2 => &*Gpio2::ptr(),
390            _ => unreachable!(),
391        }
392    }
393}
394
395// ── Pad pull-resistor control (IO_CONFIG) ─────────────────────────
396// The pull resistor lives in the per-pad IO_CONFIG control register, not the
397// GPIO block. `pad_gpio_NN_ctrl` is at IO_CONFIG + 0x800 + N*4 (PAC layout) with
398// PE = bit 9 (pull enable), PS = bit 10 (pull select); per the SVD the {PE,PS}
399// pair encodes 00 = none, 11 = pull-up, 10 = pull-down (matches
400// `io_config::build_pad_ctrl`). Only GPIO pads 0..=14 have a control register.
401const IO_CONFIG_BASE: usize = 0x4400_D000;
402const PAD_GPIO_CTRL_OFF: usize = 0x800;
403const PAD_PE_BIT: u32 = 1 << 9;
404const PAD_PS_BIT: u32 = 1 << 10;
405
406/// Apply a pull-resistor setting to a GPIO pad via IO_CONFIG.
407///
408/// Read-modify-write so drive strength / Schmitt / input-enable bits are kept.
409/// A no-op for pins 15..=18, which have no `pad_gpio_NN_ctrl` register in this
410/// layout (their pull is configured through other pads / the ROM pin map).
411fn apply_pull(pin: u8, pull: Pull) {
412    if pin > 14 {
413        return;
414    }
415    let reg = (IO_CONFIG_BASE + PAD_GPIO_CTRL_OFF + (pin as usize) * 4) as *mut u32;
416    let (pe, ps) = match pull {
417        Pull::None => (false, false),
418        Pull::Up => (true, true),
419        Pull::Down => (true, false),
420    };
421    unsafe {
422        let mut v = core::ptr::read_volatile(reg);
423        v &= !(PAD_PE_BIT | PAD_PS_BIT);
424        if pe {
425            v |= PAD_PE_BIT;
426        }
427        if ps {
428            v |= PAD_PS_BIT;
429        }
430        core::ptr::write_volatile(reg, v);
431    }
432}
433
434// ── Legacy GpioPin (backward-compatible type-state GPIO) ──────────
435
436/// Legacy GPIO pin with type-state (Input/Output mode).
437pub struct GpioPin<'d, MODE> {
438    block: u8,
439    bit: u8,
440    _mode: PhantomData<&'d MODE>,
441}
442
443impl<MODE> GpioPin<'_, MODE> {
444    pub fn number(&self) -> u8 {
445        self.block * 8 + self.bit
446    }
447}
448
449impl GpioPin<'_, OutputMode> {
450    pub fn set_high(&mut self) {
451        unsafe { regs(self.block).gpio_data_set().write(|w| w.bits(1 << self.bit)) };
452    }
453    pub fn set_low(&mut self) {
454        unsafe { regs(self.block).gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
455    }
456    pub fn toggle(&mut self) {
457        let r = regs(self.block);
458        let val = r.gpio_sw_out().read().bits();
459        if val & (1 << self.bit) != 0 {
460            unsafe { r.gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
461        } else {
462            unsafe { r.gpio_data_set().write(|w| w.bits(1 << self.bit)) };
463        }
464    }
465    pub fn is_set_high(&self) -> bool {
466        (regs(self.block).gpio_sw_out().read().bits() >> self.bit) & 1 != 0
467    }
468    pub fn into_input(self) -> GpioPin<'static, InputMode> {
469        regs(self.block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
470        GpioPin { block: self.block, bit: self.bit, _mode: PhantomData }
471    }
472}
473
474impl GpioPin<'_, InputMode> {
475    pub fn is_high(&self) -> bool {
476        (regs(self.block).gpio_sw_out().read().bits() >> self.bit) & 1 != 0
477    }
478    pub fn is_low(&self) -> bool {
479        !self.is_high()
480    }
481    pub fn enable_interrupt(&self) {
482        regs(self.block).gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
483    }
484    pub fn disable_interrupt(&self) {
485        regs(self.block).gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
486    }
487    pub fn clear_interrupt(&self) {
488        unsafe { regs(self.block).gpio_int_eoi().write(|w| w.bits(1 << self.bit)) };
489    }
490    pub fn interrupt_pending(&self) -> bool {
491        (regs(self.block).gpio_int_raw().read().bits() >> self.bit) & 1 != 0
492    }
493    pub fn into_output(self) -> GpioPin<'static, OutputMode> {
494        regs(self.block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
495        GpioPin { block: self.block, bit: self.bit, _mode: PhantomData }
496    }
497}
498
499// Legacy embedded-hal impls for GpioPin
500impl embedded_hal::digital::ErrorType for GpioPin<'_, OutputMode> {
501    type Error = core::convert::Infallible;
502}
503impl embedded_hal::digital::OutputPin for GpioPin<'_, OutputMode> {
504    fn set_low(&mut self) -> Result<(), Self::Error> {
505        GpioPin::set_low(self);
506        Ok(())
507    }
508    fn set_high(&mut self) -> Result<(), Self::Error> {
509        GpioPin::set_high(self);
510        Ok(())
511    }
512}
513impl embedded_hal::digital::StatefulOutputPin for GpioPin<'_, OutputMode> {
514    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
515        Ok(GpioPin::is_set_high(self))
516    }
517    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
518        Ok(!GpioPin::is_set_high(self))
519    }
520}
521impl embedded_hal::digital::ErrorType for GpioPin<'_, InputMode> {
522    type Error = core::convert::Infallible;
523}
524impl embedded_hal::digital::InputPin for GpioPin<'_, InputMode> {
525    fn is_high(&mut self) -> Result<bool, Self::Error> {
526        Ok(GpioPin::is_high(self))
527    }
528    fn is_low(&mut self) -> Result<bool, Self::Error> {
529        Ok(GpioPin::is_low(self))
530    }
531}
532
533// ── Pin creation functions ────────────────────────────────────────
534
535/// Create an input pin from a pin number (0-18).
536pub fn create_input_pin(pin: u8) -> GpioPin<'static, InputMode> {
537    let block = pin / 8;
538    let bit = pin % 8;
539    regs(block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << bit)) });
540    GpioPin { block, bit, _mode: PhantomData }
541}
542
543/// Create an output pin from a pin number (0-18).
544pub fn create_output_pin(pin: u8) -> GpioPin<'static, OutputMode> {
545    let block = pin / 8;
546    let bit = pin % 8;
547    regs(block).gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit)) });
548    GpioPin { block, bit, _mode: PhantomData }
549}
550
551// ── InputSignal / OutputSignal (peripheral interconnect) ──────────
552
553/// An output signal from a peripheral that can be routed to a GPIO pin.
554#[derive(Debug, Clone, Copy, PartialEq, Eq)]
555pub struct OutputSignal(pub(crate) u8);
556
557/// An input signal to a peripheral that can be routed from a GPIO pin.
558#[derive(Debug, Clone, Copy, PartialEq, Eq)]
559pub struct InputSignal(pub(crate) u8);
560
561/// Types that can serve as peripheral outputs (signals towards GPIO matrix).
562pub trait PeripheralOutput: crate::private::Sealed {
563    fn output_signal(&self) -> OutputSignal;
564}
565
566/// Types that can serve as peripheral inputs (signals from GPIO matrix towards peripherals).
567pub trait PeripheralInput: crate::private::Sealed {
568    fn input_signal(&self) -> InputSignal;
569}
570
571// Seal GPIO types for peripheral traits
572impl crate::private::Sealed for Output<'_> {}
573impl crate::private::Sealed for Input<'_> {}
574impl crate::private::Sealed for Flex<'_> {}
575impl crate::private::Sealed for GpioPin<'_, OutputMode> {}
576impl crate::private::Sealed for GpioPin<'_, InputMode> {}
577
578// ── IO MUX configuration ──────────────────────────────────────────
579
580/// IO MUX configuration.
581pub struct Io<'d> {
582    pub io_config: IoConfig<'d>,
583}
584
585impl<'d> Io<'d> {
586    pub fn new(io_config: IoConfig<'d>) -> Self {
587        Self { io_config }
588    }
589    pub fn register_block(&self) -> &ws63_pac::io_config::RegisterBlock {
590        self.io_config.register_block()
591    }
592}
593
594// ── Async (embedded-hal-async) ──────────────────────────────────────────────
595#[cfg(feature = "async")]
596mod asynch_impl {
597    use super::{Input, InterruptTrigger, regs};
598    use crate::asynch::IrqSignal;
599    use crate::interrupt::{self, Interrupt};
600    use core::future::Future;
601    use core::pin::Pin;
602    use core::task::{Context, Poll};
603    use embedded_hal_async::digital::Wait;
604
605    static GPIO_SIGNAL: [IrqSignal; 3] = [IrqSignal::new(), IrqSignal::new(), IrqSignal::new()];
606
607    fn bank_irq(bank: usize) -> Interrupt {
608        match bank {
609            0 => Interrupt::GPIO_INT0,
610            1 => Interrupt::GPIO_INT1,
611            _ => Interrupt::GPIO_INT2,
612        }
613    }
614
615    /// GPIO trap-handler hook for `bank` (0..2 → IRQ 33..35, custom local). Masks
616    /// the fired pins (so they don't storm), clears their edge latch, wakes the
617    /// awaiting [`Wait`] future, and clears the `LOCIPCLR` pending bit. Call this
618    /// from the trap when `mcause` is GPIO_INT0..2.
619    pub fn on_interrupt(bank: u8) {
620        let r = regs(bank);
621        let fired = r.gpio_int_raw().read().bits();
622        // Mask the fired pins (a fresh wait re-enables) + clear the edge latch.
623        r.gpio_int_en().modify(|v, w| unsafe { w.bits(v.bits() & !fired) });
624        unsafe { r.gpio_int_eoi().write(|w| w.bits(fired)) };
625        GPIO_SIGNAL[bank as usize].signal();
626        interrupt::clear_pending(bank_irq(bank as usize));
627    }
628
629    async fn arm_and_wait(input: &mut Input<'_>, trig: InterruptTrigger) {
630        let bank = input.pin.block as usize;
631        input.set_interrupt_trigger(trig);
632        input.clear_interrupt();
633        GPIO_SIGNAL[bank].reset();
634        input.enable_interrupt();
635        // SAFETY: enabling a known, fixed WS63 GPIO IRQ line.
636        unsafe { interrupt::enable(bank_irq(bank)) };
637        GpioWaitFuture { bank }.await;
638    }
639
640    struct GpioWaitFuture {
641        bank: usize,
642    }
643
644    impl Future for GpioWaitFuture {
645        type Output = ();
646        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
647            if GPIO_SIGNAL[self.bank].take_fired() {
648                Poll::Ready(())
649            } else {
650                GPIO_SIGNAL[self.bank].register(cx.waker());
651                Poll::Pending
652            }
653        }
654    }
655
656    /// Interrupt-driven async edge/level waiting on a GPIO input. Requires the
657    /// app to route the GPIO trap to [`on_interrupt`] and enable global
658    /// interrupts. Level waits return immediately if the pin already matches.
659    impl Wait for Input<'_> {
660        async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
661            if self.is_high() {
662                return Ok(());
663            }
664            arm_and_wait(self, InterruptTrigger::HighLevel).await;
665            Ok(())
666        }
667        async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
668            if self.is_low() {
669                return Ok(());
670            }
671            arm_and_wait(self, InterruptTrigger::LowLevel).await;
672            Ok(())
673        }
674        async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
675            arm_and_wait(self, InterruptTrigger::RisingEdge).await;
676            Ok(())
677        }
678        async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
679            arm_and_wait(self, InterruptTrigger::FallingEdge).await;
680            Ok(())
681        }
682        async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
683            let trig = if self.is_high() { InterruptTrigger::FallingEdge } else { InterruptTrigger::RisingEdge };
684            arm_and_wait(self, trig).await;
685            Ok(())
686        }
687    }
688}
689
690#[cfg(feature = "async")]
691pub use asynch_impl::on_interrupt;