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}