esp_hal/gpio/
lp_io.rs

1//! Low Power IO (LP_IO)
2//!
3//! # Overview
4//! The hardware provides a couple of GPIO pins with low power (LP)
5//! capabilities and analog functions.
6//!
7//! ## Configuration
8//! These pins can be controlled by either IO MUX or LP IO MUX.
9//!
10//! If controlled by LP IO MUX, these pins will bypass IO MUX and GPIO
11//! matrix for the use by ULP and peripherals in LP system.
12//!
13//! When configured as LP GPIOs, the pins can still be controlled by ULP or
14//! the peripherals in LP system during chip Deep-sleep, and wake up the
15//! chip from Deep-sleep.
16//!
17//! ## Examples
18//!
19//! ## Configure a LP Pin as Output
20//!
21//! ```rust, no_run
22#![doc = crate::before_snippet!()]
23//! use esp_hal::gpio::lp_io::LowPowerOutput;
24//! // configure GPIO 1 as LP output pin
25//! let lp_pin: LowPowerOutput<'_, 1> =
26//!     LowPowerOutput::new(peripherals.GPIO1);
27//! # Ok(())
28//! # }
29//! ```
30
31use core::marker::PhantomData;
32
33use super::{InputPin, OutputPin, RtcPin};
34use crate::peripherals::{GPIO, LP_AON, LP_IO};
35
36/// A GPIO output pin configured for low power operation
37pub struct LowPowerOutput<'d, const PIN: u8> {
38    phantom: PhantomData<&'d mut ()>,
39}
40
41impl<'d, const PIN: u8> LowPowerOutput<'d, PIN> {
42    /// Create a new output pin for use by the low-power core
43    #[instability::unstable]
44    pub fn new<P>(_pin: P) -> Self
45    where
46        P: OutputPin + RtcPin + 'd,
47    {
48        init_low_power_pin(PIN);
49
50        let this = Self {
51            phantom: PhantomData,
52        };
53        this.output_enable(true);
54
55        this
56    }
57
58    fn output_enable(&self, enable: bool) {
59        let lp_io = LP_IO::regs();
60        if enable {
61            lp_io
62                .out_enable_w1ts()
63                .write(|w| unsafe { w.enable_w1ts().bits(1 << PIN) });
64        } else {
65            lp_io
66                .out_enable_w1tc()
67                .write(|w| unsafe { w.enable_w1tc().bits(1 << PIN) });
68        }
69    }
70}
71
72/// A GPIO input pin configured for low power operation
73pub struct LowPowerInput<'d, const PIN: u8> {
74    phantom: PhantomData<&'d mut ()>,
75}
76
77impl<'d, const PIN: u8> LowPowerInput<'d, PIN> {
78    /// Create a new input pin for use by the low-power core
79    #[instability::unstable]
80    pub fn new<P>(_pin: P) -> Self
81    where
82        P: InputPin + RtcPin + 'd,
83    {
84        init_low_power_pin(PIN);
85
86        let this = Self {
87            phantom: PhantomData,
88        };
89        this.input_enable(true);
90        this.pullup_enable(false);
91        this.pulldown_enable(false);
92
93        this
94    }
95
96    fn input_enable(&self, enable: bool) {
97        LP_IO::regs()
98            .gpio(PIN as usize)
99            .modify(|_, w| w.fun_ie().bit(enable));
100    }
101
102    /// Sets pull-up enable for the pin
103    pub fn pullup_enable(&self, enable: bool) {
104        LP_IO::regs()
105            .gpio(PIN as usize)
106            .modify(|_, w| w.fun_wpu().bit(enable));
107    }
108
109    /// Sets pull-down enable for the pin
110    pub fn pulldown_enable(&self, enable: bool) {
111        LP_IO::regs()
112            .gpio(PIN as usize)
113            .modify(|_, w| w.fun_wpd().bit(enable));
114    }
115}
116
117/// A GPIO open-drain output pin configured for low power operation
118pub struct LowPowerOutputOpenDrain<'d, const PIN: u8> {
119    phantom: PhantomData<&'d mut ()>,
120}
121
122impl<'d, const PIN: u8> LowPowerOutputOpenDrain<'d, PIN> {
123    /// Create a new output pin for use by the low-power core
124    #[instability::unstable]
125    pub fn new<P>(_pin: P) -> Self
126    where
127        P: InputPin + OutputPin + RtcPin + 'd,
128    {
129        init_low_power_pin(PIN);
130
131        let this = Self {
132            phantom: PhantomData,
133        };
134
135        this.set_open_drain_output(true);
136        this.input_enable(true);
137        this.pullup_enable(true);
138        this.pulldown_enable(false);
139        this.output_enable(true);
140
141        this
142    }
143
144    fn output_enable(&self, enable: bool) {
145        let lp_io = LP_IO::regs();
146        if enable {
147            lp_io
148                .out_enable_w1ts()
149                .write(|w| unsafe { w.enable_w1ts().bits(1 << PIN) });
150        } else {
151            lp_io
152                .out_enable_w1tc()
153                .write(|w| unsafe { w.enable_w1tc().bits(1 << PIN) });
154        }
155    }
156
157    fn input_enable(&self, enable: bool) {
158        LP_IO::regs()
159            .gpio(PIN as usize)
160            .modify(|_, w| w.fun_ie().bit(enable));
161    }
162
163    /// Sets pull-up enable for the pin
164    pub fn pullup_enable(&self, enable: bool) {
165        LP_IO::regs()
166            .gpio(PIN as usize)
167            .modify(|_, w| w.fun_wpu().bit(enable));
168    }
169
170    /// Sets pull-down enable for the pin
171    pub fn pulldown_enable(&self, enable: bool) {
172        LP_IO::regs()
173            .gpio(PIN as usize)
174            .modify(|_, w| w.fun_wpd().bit(enable));
175    }
176
177    fn set_open_drain_output(&self, enable: bool) {
178        GPIO::regs()
179            .pin(PIN as usize)
180            .modify(|_, w| w.pad_driver().bit(enable));
181    }
182}
183
184pub(crate) fn init_low_power_pin(pin: u8) {
185    LP_AON::regs()
186        .gpio_mux()
187        .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | (1 << pin)) });
188
189    LP_IO::regs()
190        .gpio(pin as usize)
191        .modify(|_, w| unsafe { w.mcu_sel().bits(0) });
192}
193
194#[doc(hidden)]
195macro_rules! lp_gpio {
196    (
197        $($gpionum:literal)+
198    ) => {
199        $(
200            impl $crate::gpio::RtcPin for paste::paste!($crate::peripherals::[<GPIO $gpionum>]<'_>) {
201                unsafe fn apply_wakeup(&self, wakeup: bool, level: u8) {
202                    let lp_io = $crate::peripherals::LP_IO::regs();
203                    lp_io.pin($gpionum).modify(|_, w| {
204                        unsafe {
205                            w.wakeup_enable().bit(wakeup).int_type().bits(level)
206                        }
207                    });
208                }
209
210                fn rtcio_pad_hold(&self, enable: bool) {
211                    let mask = 1 << $gpionum;
212                    unsafe {
213                        let lp_aon = $crate::peripherals::LP_AON::regs();
214
215                        lp_aon.gpio_hold0().modify(|r, w| {
216                            if enable {
217                                w.gpio_hold0().bits(r.gpio_hold0().bits() | mask)
218                            } else {
219                                w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask)
220                            }
221                        });
222                    }
223                }
224
225                /// Set the LP properties of the pin. If `mux` is true then then pin is
226                /// routed to LP_IO, when false it is routed to IO_MUX.
227                fn rtc_set_config(&self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) {
228                    let mask = 1 << $gpionum;
229                    unsafe {
230                        let lp_aon = $crate::peripherals::LP_AON::regs();
231                        // Select LP_IO
232                        lp_aon
233                            .gpio_mux()
234                            .modify(|r, w| {
235                                if mux {
236                                    w.sel().bits(r.sel().bits() | mask)
237                                } else {
238                                    w.sel().bits(r.sel().bits() & !mask)
239                                }
240                            });
241
242                        // Configure input, function and select normal operation registers
243                        let lp_io = $crate::peripherals::LP_IO::regs();
244                        lp_io.gpio($gpionum).modify(|_, w| {
245                            w.slp_sel().bit(false);
246                            w.fun_ie().bit(input_enable);
247                            w.mcu_sel().bits(func as u8)
248                        });
249                    }
250                }
251            }
252
253            impl $crate::gpio::RtcPinWithResistors for paste::paste!($crate::peripherals::[<GPIO $gpionum>]<'_>) {
254                fn rtcio_pullup(&self, enable: bool) {
255                    let lp_io = $crate::peripherals::LP_IO::regs();
256                    lp_io.gpio($gpionum).modify(|_, w| w.fun_wpu().bit(enable));
257                }
258
259                fn rtcio_pulldown(&self, enable: bool) {
260                    let lp_io = $crate::peripherals::LP_IO::regs();
261                    lp_io.gpio($gpionum).modify(|_, w| w.fun_wpd().bit(enable));
262                }
263            }
264        )+
265    }
266}
267
268pub(crate) use lp_gpio;