Skip to main content

ws63_hal/
ulp_gpio.rs

1//! Ultra-Low-Power GPIO (ULP_GPIO) driver for WS63.
2//!
3//! The WS63 ULP_GPIO provides 8 additional GPIO pins (GPIO107-114) that
4//! can operate in ultra-low-power modes. The register layout is identical
5//! to the standard GPIO blocks.
6//!
7//! These pins are accessible at physical address 0x5703_0000 and use
8//! the same register layout as GPIO0.
9
10use crate::peripherals::UlpGpio;
11use core::marker::PhantomData;
12
13/// ULP GPIO pin mode marker types.
14pub struct Input;
15pub struct Output;
16
17/// ULP GPIO pin driver.
18pub struct UlpGpioPin<'d, MODE> {
19    bit: u8,
20    _mode: PhantomData<&'d MODE>,
21}
22
23fn regs() -> &'static ws63_pac::gpio0::RegisterBlock {
24    // SAFETY: PAC peripheral pointer is a static physical MMIO address, always valid
25    unsafe { &*UlpGpio::ptr() }
26}
27
28impl<MODE> UlpGpioPin<'_, MODE> {
29    /// Get the pin number (107-114).
30    pub fn number(&self) -> u8 {
31        107 + self.bit
32    }
33}
34
35impl UlpGpioPin<'_, Output> {
36    /// Set the pin high.
37    pub fn set_high(&mut self) {
38        unsafe { regs().gpio_data_set().write(|w| w.bits(1 << self.bit)) };
39    }
40
41    /// Set the pin low.
42    pub fn set_low(&mut self) {
43        unsafe { regs().gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
44    }
45
46    /// Toggle the pin output level.
47    pub fn toggle(&mut self) {
48        let val = regs().gpio_sw_out().read().bits();
49        if val & (1 << self.bit) != 0 {
50            unsafe { regs().gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
51        } else {
52            unsafe { regs().gpio_data_set().write(|w| w.bits(1 << self.bit)) };
53        }
54    }
55
56    /// Check if the output is set high.
57    pub fn is_set_high(&self) -> bool {
58        (regs().gpio_sw_out().read().bits() >> self.bit) & 1 != 0
59    }
60
61    /// Convert the pin to input mode.
62    pub fn into_input(self) -> UlpGpioPin<'static, Input> {
63        regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
64        UlpGpioPin { bit: self.bit, _mode: PhantomData }
65    }
66}
67
68impl UlpGpioPin<'_, Input> {
69    /// Check if the input is high.
70    pub fn is_high(&self) -> bool {
71        (regs().gpio_sw_out().read().bits() >> self.bit) & 1 != 0
72    }
73
74    /// Check if the input is low.
75    pub fn is_low(&self) -> bool {
76        !self.is_high()
77    }
78
79    /// Enable the interrupt for this pin.
80    pub fn enable_interrupt(&self) {
81        regs().gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
82    }
83
84    /// Disable the interrupt for this pin.
85    pub fn disable_interrupt(&self) {
86        regs().gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
87    }
88
89    /// Clear the interrupt for this pin.
90    pub fn clear_interrupt(&self) {
91        unsafe { regs().gpio_int_eoi().write(|w| w.bits(1 << self.bit)) };
92    }
93
94    /// Check if an interrupt is pending for this pin.
95    pub fn interrupt_pending(&self) -> bool {
96        (regs().gpio_int_raw().read().bits() >> self.bit) & 1 != 0
97    }
98
99    /// Convert the pin to output mode.
100    pub fn into_output(self) -> UlpGpioPin<'static, Output> {
101        regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
102        UlpGpioPin { bit: self.bit, _mode: PhantomData }
103    }
104}
105
106// ── embedded-hal traits ────────────────────────────────────────────
107
108impl embedded_hal::digital::ErrorType for UlpGpioPin<'_, Output> {
109    type Error = core::convert::Infallible;
110}
111impl embedded_hal::digital::OutputPin for UlpGpioPin<'_, Output> {
112    fn set_low(&mut self) -> Result<(), Self::Error> {
113        UlpGpioPin::set_low(self);
114        Ok(())
115    }
116    fn set_high(&mut self) -> Result<(), Self::Error> {
117        UlpGpioPin::set_high(self);
118        Ok(())
119    }
120}
121impl embedded_hal::digital::StatefulOutputPin for UlpGpioPin<'_, Output> {
122    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
123        Ok(UlpGpioPin::is_set_high(self))
124    }
125    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
126        Ok(!UlpGpioPin::is_set_high(self))
127    }
128}
129impl embedded_hal::digital::ErrorType for UlpGpioPin<'_, Input> {
130    type Error = core::convert::Infallible;
131}
132impl embedded_hal::digital::InputPin for UlpGpioPin<'_, Input> {
133    fn is_high(&mut self) -> Result<bool, Self::Error> {
134        Ok(UlpGpioPin::is_high(self))
135    }
136    fn is_low(&mut self) -> Result<bool, Self::Error> {
137        Ok(!UlpGpioPin::is_high(self))
138    }
139}
140
141/// Create a ULP GPIO pin in input mode.
142pub fn create_input_pin(pin: u8) -> UlpGpioPin<'static, Input> {
143    let bit = pin - 107;
144    assert!(bit < 8, "ULP GPIO pin must be 107-114");
145    regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << bit)) });
146    UlpGpioPin { bit, _mode: PhantomData }
147}
148
149/// Create a ULP GPIO pin in output mode.
150pub fn create_output_pin(pin: u8) -> UlpGpioPin<'static, Output> {
151    let bit = pin - 107;
152    assert!(bit < 8, "ULP GPIO pin must be 107-114");
153    regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit)) });
154    UlpGpioPin { bit, _mode: PhantomData }
155}