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