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}