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