esp_hal/rtc_cntl/sleep/
mod.rs

1//! # RTC Control Sleep Module
2//!
3//! ## Overview
4//! The `sleep` module allows configuring various wakeup sources and setting up
5//! the sleep behavior based on those sources. The supported wakeup sources
6//! include:
7//!    * `GPIO` pins - light sleep only
8//!    * timers
9//!    * `SDIO (Secure Digital Input/Output) - light sleep only`
10//!    * `MAC (Media Access Control)` wake - light sleep only
11//!    * `UART0` - light sleep only
12//!    * `UART1` - light sleep only
13//!    * `touch`
14//!    * `ULP (Ultra-Low Power)` wake
15//!    * `BT (Bluetooth) wake` - light sleep only
16
17use core::cell::RefCell;
18#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))]
19use core::time::Duration;
20
21#[cfg(any(esp32, esp32s2, esp32s3))]
22use crate::gpio::RtcPin as RtcIoWakeupPinType;
23#[cfg(any(esp32c3, esp32c6, esp32c2))]
24use crate::gpio::RtcPinWithResistors as RtcIoWakeupPinType;
25use crate::rtc_cntl::Rtc;
26
27#[cfg_attr(esp32, path = "esp32.rs")]
28#[cfg_attr(esp32s2, path = "esp32s2.rs")]
29#[cfg_attr(esp32s3, path = "esp32s3.rs")]
30#[cfg_attr(esp32c3, path = "esp32c3.rs")]
31#[cfg_attr(esp32c6, path = "esp32c6.rs")]
32#[cfg_attr(esp32c2, path = "esp32c2.rs")]
33mod sleep_impl;
34
35pub use sleep_impl::*;
36
37#[derive(Debug, Default, Clone, Copy, PartialEq)]
38/// Level at which a wake-up event is triggered
39pub enum WakeupLevel {
40    /// The wake-up event is triggered when the pin is low.
41    Low,
42    #[default]
43    ///  The wake-up event is triggered when the pin is high.
44    High,
45}
46
47#[procmacros::doc_replace]
48/// Represents a timer wake-up source, triggering an event after a specified
49/// duration.
50///
51/// ```rust, no_run
52/// # {before_snippet}
53/// # use core::time::Duration;
54/// # use esp_hal::delay::Delay;
55/// # use esp_hal::rtc_cntl::{reset_reason, sleep::TimerWakeupSource, wakeup_cause, Rtc, SocResetReason};
56/// # use esp_hal::system::Cpu;
57///
58/// let delay = Delay::new();
59/// let mut rtc = Rtc::new(peripherals.LPWR);
60///
61/// let reason = reset_reason(Cpu::ProCpu);
62/// let wake_reason = wakeup_cause();
63///
64/// println!("{:?} {?}", reason, wake_reason);
65///
66/// let timer = TimerWakeupSource::new(Duration::from_secs(5));
67/// delay.delay_millis(100);
68/// rtc.sleep_deep(&[&timer]);
69///
70/// # {after_snippet}
71/// ```
72#[derive(Debug, Default, Clone, Copy)]
73#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))]
74pub struct TimerWakeupSource {
75    /// The duration after which the wake-up event is triggered.
76    duration: Duration,
77}
78
79#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))]
80impl TimerWakeupSource {
81    /// Creates a new timer wake-up source with the specified duration.
82    pub fn new(duration: Duration) -> Self {
83        Self { duration }
84    }
85}
86
87/// Errors that can occur when configuring RTC wake-up sources.
88#[derive(Debug, Clone, Copy, PartialEq)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub enum Error {
91    /// The selected pin is not a valid RTC pin.
92    NotRtcPin,
93    /// The maximum number of wake-up sources has been exceeded.
94    TooManyWakeupSources,
95}
96
97#[procmacros::doc_replace]
98/// External wake-up source (Ext0).
99///
100/// ```rust, no_run
101/// # {before_snippet}
102/// # use core::time::Duration;
103/// # use esp_hal::delay::Delay;
104/// # use esp_hal::rtc_cntl::{reset_reason, sleep::{Ext0WakeupSource, TimerWakeupSource, WakeupLevel}, wakeup_cause, Rtc, SocResetReason};
105/// # use esp_hal::system::Cpu;
106/// # use esp_hal::gpio::{Input, InputConfig, Pull};
107///
108/// let delay = Delay::new();
109/// let mut rtc = Rtc::new(peripherals.LPWR);
110///
111/// let config = InputConfig::default().with_pull(Pull::None);
112/// let mut pin_4 = peripherals.GPIO4;
113/// let pin_4_input = Input::new(pin_4.reborrow(), config);
114///
115/// let reason = reset_reason(Cpu::ProCpu);
116/// let wake_reason = wakeup_cause();
117///
118/// println!("{:?} {?}", reason, wake_reason);
119///
120/// let timer = TimerWakeupSource::new(Duration::from_secs(30));
121///
122/// core::mem::drop(pin_4_input);
123/// let ext0 = Ext0WakeupSource::new(pin_4, WakeupLevel::High);
124///
125/// delay.delay_millis(100);
126/// rtc.sleep_deep(&[&timer, &ext0]);
127///
128/// # }
129/// ```
130#[cfg(any(esp32, esp32s2, esp32s3))]
131pub struct Ext0WakeupSource<P: RtcIoWakeupPinType> {
132    /// The pin used as the wake-up source.
133    pin: RefCell<P>,
134    /// The level at which the wake-up event is triggered.
135    level: WakeupLevel,
136}
137
138#[cfg(any(esp32, esp32s2, esp32s3))]
139impl<P: RtcIoWakeupPinType> Ext0WakeupSource<P> {
140    /// Creates a new external wake-up source (Ext0``) with the specified pin
141    /// and wake-up level.
142    pub fn new(pin: P, level: WakeupLevel) -> Self {
143        Self {
144            pin: RefCell::new(pin),
145            level,
146        }
147    }
148}
149
150#[procmacros::doc_replace]
151/// External wake-up source (Ext1).
152///
153/// ```rust, no_run
154/// # {before_snippet}
155/// # use core::time::Duration;
156/// # use esp_hal::delay::Delay;
157/// # use esp_hal::rtc_cntl::{reset_reason, sleep::{Ext1WakeupSource, TimerWakeupSource, WakeupLevel}, wakeup_cause, Rtc, SocResetReason};
158/// # use esp_hal::system::Cpu;
159/// # use esp_hal::gpio::{Input, InputConfig, Pull, RtcPin};
160///
161/// let delay = Delay::new();
162/// let mut rtc = Rtc::new(peripherals.LPWR);
163///
164/// let config = InputConfig::default().with_pull(Pull::None);
165/// let mut pin_2 = peripherals.GPIO2;
166/// let mut pin_4 = peripherals.GPIO4;
167/// let pin_4_driver = Input::new(pin_4.reborrow(), config);
168///
169/// let reason = reset_reason(Cpu::ProCpu);
170/// let wake_reason = wakeup_cause();
171///
172/// println!("{:?} {?}", reason, wake_reason);
173///
174/// let timer = TimerWakeupSource::new(Duration::from_secs(30));
175///
176/// // Drop the driver to access `pin_4`
177/// core::mem::drop(pin_4_driver);
178///
179/// let mut wakeup_pins: [&mut dyn RtcPin; 2] = [&mut pin_4, &mut pin_2];
180///
181/// let ext1 = Ext1WakeupSource::new(&mut wakeup_pins, WakeupLevel::High);
182///
183/// delay.delay_millis(100);
184/// rtc.sleep_deep(&[&timer, &ext1]);
185///
186/// # }
187/// ```
188#[cfg(any(esp32, esp32s2, esp32s3))]
189pub struct Ext1WakeupSource<'a, 'b> {
190    /// A collection of pins used as wake-up sources.
191    pins: RefCell<&'a mut [&'b mut dyn RtcIoWakeupPinType]>,
192    /// The level at which the wake-up event is triggered across all pins.
193    level: WakeupLevel,
194}
195
196#[cfg(any(esp32, esp32s2, esp32s3))]
197impl<'a, 'b> Ext1WakeupSource<'a, 'b> {
198    /// Creates a new external wake-up source (Ext1) with the specified pins and
199    /// wake-up level.
200    pub fn new(pins: &'a mut [&'b mut dyn RtcIoWakeupPinType], level: WakeupLevel) -> Self {
201        Self {
202            pins: RefCell::new(pins),
203            level,
204        }
205    }
206}
207
208#[procmacros::doc_replace]
209/// External wake-up source (Ext1).
210/// ```rust, no_run
211/// # {before_snippet}
212/// # use core::time::Duration;
213/// # use esp_hal::delay::Delay;
214/// # use esp_hal::rtc_cntl::{reset_reason, sleep::{Ext1WakeupSource, TimerWakeupSource, WakeupLevel}, wakeup_cause, Rtc, SocResetReason};
215/// # use esp_hal::system::Cpu;
216/// # use esp_hal::gpio::{Input, InputConfig, Pull, RtcPinWithResistors};
217///
218/// let delay = Delay::new();
219/// let mut rtc = Rtc::new(peripherals.LPWR);
220///
221/// let config = InputConfig::default().with_pull(Pull::None);
222/// let mut pin2 = peripherals.GPIO2;
223/// let mut pin3 = peripherals.GPIO3;
224/// let mut pin2_input = Input::new(pin2.reborrow(), config);
225///
226/// let reason = reset_reason(Cpu::ProCpu);
227/// let wake_reason = wakeup_cause();
228///
229/// println!("{:?} {?}", reason, wake_reason);
230///
231/// let timer = TimerWakeupSource::new(Duration::from_secs(30));
232///
233/// core::mem::drop(pin2_input);
234///
235/// let wakeup_pins: &mut [(&mut dyn RtcPinWithResistors, WakeupLevel)] =
236/// &mut [
237///     (&mut pin2, WakeupLevel::Low),
238///     (&mut pin3, WakeupLevel::High),
239/// ];
240///
241/// let ext1 = Ext1WakeupSource::new(wakeup_pins);
242///
243/// delay.delay_millis(100);
244/// rtc.sleep_deep(&[&timer, &ext1]);
245///
246/// # }
247/// ```
248#[cfg(esp32c6)]
249pub struct Ext1WakeupSource<'a, 'b> {
250    pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>,
251}
252
253#[cfg(esp32c6)]
254impl<'a, 'b> Ext1WakeupSource<'a, 'b> {
255    /// Creates a new external wake-up source (Ext1) with the specified pins and
256    /// wake-up level.
257    pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self {
258        Self {
259            pins: RefCell::new(pins),
260        }
261    }
262}
263
264#[procmacros::doc_replace(
265    "pin0" => {
266        cfg(any(esp32c3, esp32c2)) => "let mut pin_0 = peripherals.GPIO2;",
267        cfg(any(esp32s2, esp32s3)) => "let mut pin_0 = peripherals.GPIO17;"
268    },
269    "pin1" => {
270        cfg(any(esp32c3, esp32c2)) => "let mut pin_1 = peripherals.GPIO3;",
271        cfg(any(esp32s2, esp32s3)) => "let mut pin_1 = peripherals.GPIO18;"
272    },
273    "wakeup_pins" => {
274        cfg(any(esp32c3, esp32c2)) => "let wakeup_pins: &mut [(&mut dyn gpio::RtcPinWithResistors, WakeupLevel)] = \n\t&mut [(&mut pin_0, WakeupLevel::Low),(&mut pin_1, WakeupLevel::High)];",
275        cfg(any(esp32s2, esp32s3)) => "let wakeup_pins: &mut [(&mut dyn gpio::RtcPin, WakeupLevel)] = \n\t&mut [(&mut pin_0, WakeupLevel::Low),(&mut pin_1, WakeupLevel::High)];"
276    },
277)]
278/// RTC_IO wakeup source
279///
280/// RTC_IO wakeup allows configuring any combination of RTC_IO pins with
281/// arbitrary wakeup levels to wake up the chip from sleep. This wakeup source
282/// can be used to wake up from both light and deep sleep.
283///
284/// ```rust, no_run
285/// # {before_snippet}
286/// # use core::time::Duration;
287/// # use esp_hal::delay::Delay;
288/// # use esp_hal::gpio::{self, Input, InputConfig, Pull};
289/// # use esp_hal::rtc_cntl::{reset_reason,
290/// #   sleep::{RtcioWakeupSource, TimerWakeupSource, WakeupLevel},
291/// #   wakeup_cause, Rtc, SocResetReason
292/// # };
293/// # use esp_hal::system::Cpu;
294///
295/// let mut rtc = Rtc::new(peripherals.LPWR);
296///
297/// let reason = reset_reason(Cpu::ProCpu);
298/// let wake_reason = wakeup_cause();
299///
300/// println!("{:?} {?}", reason, wake_reason);
301///
302/// let delay = Delay::new();
303/// let timer = TimerWakeupSource::new(Duration::from_secs(10));
304/// # {pin0}
305/// # {pin1}
306/// # {wakeup_pins}
307///
308/// let rtcio = RtcioWakeupSource::new(wakeup_pins);
309/// delay.delay_millis(100);
310/// rtc.sleep_deep(&[&timer, &rtcio]);
311///
312/// # {after_snippet}
313/// ```
314#[cfg(any(esp32c3, esp32s2, esp32s3, esp32c2))]
315pub struct RtcioWakeupSource<'a, 'b> {
316    pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>,
317}
318
319#[cfg(any(esp32c3, esp32s2, esp32s3, esp32c2))]
320impl<'a, 'b> RtcioWakeupSource<'a, 'b> {
321    /// Creates a new external GPIO wake-up source.
322    pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self {
323        Self {
324            pins: RefCell::new(pins),
325        }
326    }
327}
328
329/// LP Core wakeup source
330///
331/// Wake up from LP core. This wakeup source
332/// can be used to wake up from both light and deep sleep.
333#[cfg(esp32c6)]
334pub struct WakeFromLpCoreWakeupSource {}
335
336#[cfg(esp32c6)]
337impl WakeFromLpCoreWakeupSource {
338    /// Create a new instance of `WakeFromLpCoreWakeupSource`
339    pub fn new() -> Self {
340        Self {}
341    }
342}
343
344#[cfg(esp32c6)]
345impl Default for WakeFromLpCoreWakeupSource {
346    fn default() -> Self {
347        Self::new()
348    }
349}
350
351/// GPIO wakeup source
352///
353/// Wake up from GPIO high or low level. Any pin can be used with this wake up
354/// source. Configure the pin for wake up via
355/// [crate::gpio::Input::wakeup_enable].
356///
357/// This wakeup source can be used to wake up from light sleep only.
358pub struct GpioWakeupSource {}
359
360impl GpioWakeupSource {
361    /// Create a new instance of [GpioWakeupSource]
362    pub fn new() -> Self {
363        Self {}
364    }
365}
366
367impl Default for GpioWakeupSource {
368    fn default() -> Self {
369        Self::new()
370    }
371}
372
373impl WakeSource for GpioWakeupSource {
374    fn apply(
375        &self,
376        _rtc: &Rtc<'_>,
377        triggers: &mut WakeTriggers,
378        _sleep_config: &mut RtcSleepConfig,
379    ) {
380        triggers.set_gpio(true);
381    }
382}
383
384macro_rules! uart_wakeup_impl {
385    ($num:literal) => {
386        paste::paste! {
387            #[doc = concat!("UART", $num, " wakeup source")]
388            ///
389            /// The chip can be woken up by reverting RXD for multiple cycles until the
390            /// number of rising edges is equal to or greater than the given value.
391            ///
392            /// Note that the character which triggers wakeup (and any characters before
393            /// it) will not be received by the UART after wakeup. This means that the
394            /// external device typically needs to send an extra character to trigger
395            /// wakeup before sending the data.
396            ///
397            /// After waking-up from UART, you should send some extra data through the UART
398            /// port in Active mode, so that the internal wakeup indication signal can be
399            /// cleared. Otherwise, the next UART wake-up would trigger with two less
400            /// rising edges than the configured threshold value.
401            ///
402            /// Wakeup from light sleep takes some time, so not every character sent to the
403            /// UART can be received by the application.
404            ///
405            /// This wakeup source can be used to wake up from light sleep only.
406            pub struct [< Uart $num WakeupSource >] {
407                threshold: u16,
408            }
409
410            impl [< Uart $num WakeupSource >] {
411                #[doc = concat!("Create a new instance of UART", $num, " wakeup source>") ]
412                ///
413                /// # Panics
414                ///
415                /// Panics if `threshold` is out of bounds.
416                pub fn new(threshold: u16) -> Self {
417                    if threshold > 1023 {
418                        panic!("Invalid threshold");
419                    }
420                    Self { threshold }
421                }
422            }
423
424            impl WakeSource for [< Uart $num WakeupSource >] {
425                fn apply(&self, _rtc: &Rtc<'_>, triggers: &mut WakeTriggers, _sleep_config: &mut RtcSleepConfig) {
426                    triggers.[< set_uart $num >](true);
427                    let uart = crate::peripherals::[< UART $num >]::regs();
428
429                    #[cfg(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3))]
430                    uart.sleep_conf()
431                        .modify(|_, w| unsafe { w.active_threshold().bits(self.threshold) });
432
433                    #[cfg(not(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3)))]
434                    uart.sleep_conf2().modify(|_, w| unsafe {
435                        w.wk_mode_sel()
436                            .bits(0)
437                            .active_threshold()
438                            .bits(self.threshold)
439                    });
440                }
441            }
442        }
443    };
444}
445
446uart_wakeup_impl!(0);
447uart_wakeup_impl!(1);
448
449#[cfg(esp32s2)]
450bitfield::bitfield! {
451    /// Represents the wakeup triggers.
452    #[derive(Default, Clone, Copy)]
453    pub struct WakeTriggers(u16);
454    impl Debug;
455    /// EXT0 GPIO wakeup
456    pub ext0, set_ext0: 0;
457    /// EXT1 GPIO wakeup
458    pub ext1, set_ext1: 1;
459    /// GPIO wakeup (l5ght sleep only)
460    pub gpio, set_gpio: 2;
461    /// Timer wakeup
462    pub timer, set_timer: 3;
463    /// WiFi SoC wakeup
464    pub wifi_soc, set_wifi_soc: 5;
465    /// UART0 wakeup (light sleep only)
466    pub uart0, set_uart0: 6;
467    /// UART1 wakeup (light sleep only)
468    pub uart1, set_uart1: 7;
469    /// Touch wakeup
470    pub touch, set_touch: 8;
471    /// ULP-FSM wakeup
472    pub ulp, set_ulp: 11;
473    /// USB wakeup
474    pub usb, set_usb: 15;
475}
476
477#[cfg(any(esp32, esp32c2, esp32c3, esp32s3))]
478bitfield::bitfield! {
479    /// Represents the wakeup triggers.
480    #[derive(Default, Clone, Copy)]
481    pub struct WakeTriggers(u16);
482    impl Debug;
483    /// EXT0 GPIO wakeup
484    pub ext0, set_ext0: 0;
485    /// EXT1 GPIO wakeup
486    pub ext1, set_ext1: 1;
487    /// GPIO wakeup (light sleep only)
488    pub gpio, set_gpio: 2;
489    /// Timer wakeup
490    pub timer, set_timer: 3;
491    /// SDIO wakeup (light sleep only)
492    pub sdio, set_sdio: 4;
493    /// MAC wakeup (light sleep only)
494    pub mac, set_mac: 5;
495    /// UART0 wakeup (light sleep only)
496    pub uart0, set_uart0: 6;
497    /// UART1 wakeup (light sleep only)
498    pub uart1, set_uart1: 7;
499    /// Touch wakeup
500    pub touch, set_touch: 8;
501    /// ULP wakeup
502    pub ulp, set_ulp: 9;
503    /// BT wakeup (light sleep only)
504    pub bt, set_bt: 10;
505}
506
507#[cfg(soc_has_pmu)]
508bitfield::bitfield! {
509    /// Represents the wakeup triggers.
510    #[derive(Default, Clone, Copy)]
511    pub struct WakeTriggers(u16);
512    impl Debug;
513
514    /// EXT0 GPIO wakeup
515    pub ext0, set_ext0: 0;
516    /// EXT1 GPIO wakeup
517    pub ext1, set_ext1: 1;
518    /// GPIO wakeup
519    pub gpio, set_gpio: 2;
520    /// WiFi beacon wakeup
521    pub wifi_beacon, set_wifi_beacon: 3;
522    /// Timer wakeup
523    pub timer, set_timer: 4;
524    /// WiFi SoC wakeup
525    pub wifi_soc, set_wifi_soc: 5;
526    /// UART0 wakeup
527    pub uart0, set_uart0: 6;
528    /// UART1 wakeup
529    pub uart1, set_uart1: 7;
530    /// SDIO wakeup
531    pub sdio, set_sdio: 8;
532    /// BT wakeup
533    pub bt, set_bt: 10;
534    /// LP core wakeup
535    pub lp_core, set_lp_core: 11;
536    /// USB wakeup
537    pub usb, set_usb: 14;
538}
539
540/// Trait representing a wakeup source.
541pub trait WakeSource {
542    /// Configures the RTC and applies the wakeup triggers.
543    fn apply(&self, rtc: &Rtc<'_>, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig);
544}