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