diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e48f0b2ca5..403df615787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add initial LP-IO support for ESP32-C6 (#639) +- Implement sleep with some wakeup methods for `esp32` (#574) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index d79f5ff3edc..091fee40979 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -13,6 +13,7 @@ license = "MIT OR Apache-2.0" [dependencies] bitflags = "2.3.3" +bitfield = "0.14.0" cfg-if = "1.0.0" critical-section = "1.1.1" embedded-can = { version = "0.4.1", optional = true } @@ -64,12 +65,12 @@ basic-toml = "0.1.2" serde = { version = "1.0.164", features = ["derive"] } [features] -esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api", "procmacros/esp32"] +esp32 = ["esp32/rt", "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api", "procmacros/esp32"] esp32c2 = ["esp32c2/rt", "riscv", "procmacros/esp32c2"] esp32c3 = ["esp32c3/rt", "riscv", "procmacros/esp32c3"] esp32c6 = ["esp32c6/rt", "riscv", "procmacros/esp32c6"] esp32h2 = ["esp32h2/rt", "riscv", "procmacros/esp32h2"] -esp32s2 = ["esp32s2/rt", "xtensa", "xtensa-lx/esp32s2", "xtensa-lx-rt/esp32s2", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s2"] +esp32s2 = ["esp32s2/rt", "xtensa", "xtensa-lx/esp32s2", "xtensa-lx-rt/esp32s2", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s2"] esp32s3 = ["esp32s3/rt", "xtensa", "xtensa-lx/esp32s3", "xtensa-lx-rt/esp32s3", "lock_api", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s3"] esp32_40mhz = [] diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index c29f0475db6..44b43473071 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -11,6 +11,8 @@ use core::{convert::Infallible, marker::PhantomData}; use crate::peripherals::{GPIO, IO_MUX}; +#[cfg(xtensa)] +pub(crate) use crate::rtc_pins; pub use crate::soc::gpio::*; pub(crate) use crate::{analog, gpio}; @@ -89,7 +91,13 @@ pub enum AlternateFunction { Function5 = 5, } -pub trait RTCPin {} +pub trait RTCPin { + fn rtc_number(&self) -> u8; + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: u8); +} + +pub trait RTCInputPin: RTCPin {} +pub trait RTCOutputPin: RTCPin {} pub trait AnalogPin {} @@ -1397,6 +1405,51 @@ macro_rules! gpio { }; } +#[cfg(xtensa)] +#[doc(hidden)] +#[macro_export] +macro_rules! rtc_pins { + ( + $( ( $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $prefix:pat ) )+ + ) => { + impl crate::gpio::RTCPin for GpioPin + where + Self: crate::gpio::GpioProperties, + ::PinType: crate::gpio::IsAnalogPin, + { + fn rtc_number(&self) -> u8 { + match GPIONUM { + $( + $pin_num => $rtc_pin, + )+ + _ => unreachable!(), + } + } + /// Set the RTC properties of the pin. If `mux` is true then then pin is + /// routed to RTC, when false it is routed to IO_MUX. + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: u8) { + use crate::peripherals::RTC_IO; + let rtcio = unsafe{ &*RTC_IO::ptr() }; + match GPIONUM { + $( + $pin_num => { + // disable input + paste::paste!{ + rtcio.$pin_reg.modify(|_,w| unsafe {w + .[<$prefix fun_ie>]().bit(input_enable) + .[<$prefix mux_sel>]().bit(mux) + .[<$prefix fun_sel>]().bits(func) + }); + } + } + )+ + _ => unreachable!(), + } + } + } + }; +} + // Following code enables `into_analog` #[doc(hidden)] diff --git a/esp-hal-common/src/reset.rs b/esp-hal-common/src/reset.rs index 8c07ce4c32c..6aa760a78f1 100644 --- a/esp-hal-common/src/reset.rs +++ b/esp-hal-common/src/reset.rs @@ -2,6 +2,7 @@ use crate::rtc_cntl::SocResetReason; +#[derive(Debug, Copy, Clone)] pub enum SleepSource { /// In case of deep sleep, reset was not caused by exit from deep sleep Undefined = 0, diff --git a/esp-hal-common/src/rtc_cntl/mod.rs b/esp-hal-common/src/rtc_cntl/mod.rs index 480ed4ec9fd..9da38137f2d 100644 --- a/esp-hal-common/src/rtc_cntl/mod.rs +++ b/esp-hal-common/src/rtc_cntl/mod.rs @@ -14,12 +14,17 @@ use crate::efuse::Efuse; use crate::peripherals::{LP_TIMER, LP_WDT}; #[cfg(not(any(esp32c6, esp32h2)))] use crate::peripherals::{RTC_CNTL, TIMG0}; +#[cfg(any(esp32))] +use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers}; use crate::{ clock::Clock, peripheral::{Peripheral, PeripheralRef}, reset::{SleepSource, WakeupReason}, Cpu, }; +// only include sleep where its been implemented +#[cfg(any(esp32))] +pub mod sleep; #[cfg(any(esp32c6, esp32h2))] type RtcCntl = crate::peripherals::LP_CLKRST; @@ -191,6 +196,64 @@ impl<'d> Rtc<'d> { pub fn get_time_ms(&self) -> u64 { self.get_time_raw() * 1_000 / RtcClock::get_slow_freq().frequency().to_Hz() as u64 } + + /// enter deep sleep and wake with the provided `wake_sources` + #[cfg(esp32)] + pub fn sleep_deep<'a>( + &mut self, + wake_sources: &[&'a dyn WakeSource], + delay: &mut crate::Delay, + ) -> ! { + let config = RtcSleepConfig::deep(); + self.sleep(&config, wake_sources, delay); + unreachable!(); + } + + /// enter light sleep and wake with the provided `wake_sources` + #[cfg(esp32)] + pub fn sleep_light<'a>( + &mut self, + wake_sources: &[&'a dyn WakeSource], + delay: &mut crate::Delay, + ) { + let config = RtcSleepConfig::default(); + self.sleep(&config, wake_sources, delay) + } + + /// enter sleep wthe the provided `config` and wake with the provided + /// `wake_sources` + #[cfg(esp32)] + pub fn sleep<'a>( + &mut self, + config: &RtcSleepConfig, + wake_sources: &[&'a dyn WakeSource], + delay: &mut crate::Delay, + ) { + let mut config = config.clone(); + let mut wakeup_triggers = WakeTriggers::default(); + for wake_source in wake_sources { + wake_source.apply(self, &mut wakeup_triggers, &mut config) + } + config.apply(self); + use embedded_hal::blocking::delay::DelayMs; + delay.delay_ms(100u32); + unsafe { + let rtc_cntl = &*RTC_CNTL::ptr(); + + rtc_cntl + .reset_state + .modify(|_, w| w.procpu_stat_vector_sel().set_bit()); + + // set bits for what can wake us up + rtc_cntl + .wakeup_state + .modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into())); + + rtc_cntl + .state0 + .write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit()); + } + } } #[cfg(not(any(esp32c6, esp32h2)))] diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs new file mode 100644 index 00000000000..9e3553e28ad --- /dev/null +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs @@ -0,0 +1,451 @@ +use super::{Ext0WakeupSource, Ext1WakeupSource, TimerWakeupSource, WakeSource, WakeTriggers}; +use crate::{ + gpio::{Pin, RTCPin}, + rtc_cntl::{sleep::WakeupLevel, Clock, RtcClock}, + Rtc, +}; + +// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, +// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. +// Valid if RTC_CNTL_DBG_ATTEN is 0. +pub const RTC_CNTL_DBIAS_0V90: u8 = 0; +pub const RTC_CNTL_DBIAS_0V95: u8 = 1; +pub const RTC_CNTL_DBIAS_1V00: u8 = 2; +pub const RTC_CNTL_DBIAS_1V05: u8 = 3; +pub const RTC_CNTL_DBIAS_1V10: u8 = 4; +pub const RTC_CNTL_DBIAS_1V15: u8 = 5; +pub const RTC_CNTL_DBIAS_1V20: u8 = 6; +pub const RTC_CNTL_DBIAS_1V25: u8 = 7; +// Various delays to be programmed into power control state machines +pub const RTC_CNTL_XTL_BUF_WAIT_SLP_US: u32 = 1000; +pub const RTC_CNTL_PLL_BUF_WAIT_SLP_CYCLES: u8 = 1; +pub const RTC_CNTL_CK8M_WAIT_SLP_CYCLES: u8 = 4; +pub const RTC_CNTL_WAKEUP_DELAY_CYCLES: u8 = 7; +pub const RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES: u8 = 1; +pub const RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES: u16 = 1; +pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 128; + +pub const RTC_MEM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +pub const RTC_MEM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +pub const ROM_RAM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +pub const ROM_RAM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +pub const WIFI_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +pub const WIFI_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +pub const RTC_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +pub const RTC_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +pub const DG_WRAP_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +pub const DG_WRAP_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; + +pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5; + +impl WakeSource for TimerWakeupSource { + fn apply(&self, rtc: &Rtc, triggers: &mut WakeTriggers, _sleep_config: &mut RtcSleepConfig) { + triggers.set_timer(true); + let rtc_cntl = unsafe { &*esp32::RTC_CNTL::ptr() }; + let clock_freq = RtcClock::get_slow_freq(); + // TODO: maybe add sleep time adjustlemnt like idf + // TODO: maybe add check to prevent overflow? + let clock_hz = clock_freq.frequency().to_Hz() as u64; + let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64; + // "alarm" time in slow rtc ticks + let now = rtc.get_time_raw(); + let time_in_ticks = now + ticks; + unsafe { + rtc_cntl + .slp_timer0 + .write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32)); + #[rustfmt::skip] + rtc_cntl.slp_timer1.write(|w| { w + .slp_val_hi().bits(((time_in_ticks >> 32) & 0xffff) as u16) + .main_timer_alarm_en().set_bit() + }); + } + } +} + +impl<'a, P: Pin + RTCPin> WakeSource for Ext0WakeupSource<'a, P> { + fn apply(&self, _rtc: &Rtc, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig) { + // don't power down RTC peripherals + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_ext0(true); + + // set pin to RTC function + self.pin.borrow_mut().rtc_set_config(true, true, 0); + + unsafe { + let rtc_io = &*esp32::RTC_IO::ptr(); + // set pin register field + rtc_io + .ext_wakeup0 + .modify(|_, w| w.sel().bits(self.pin.borrow().rtc_number())); + // set level register field + let rtc_cntl = &*esp32::RTC_CNTL::ptr(); + rtc_cntl + .ext_wakeup_conf + .modify(|_r, w| w.ext_wakeup0_lv().bit(self.level == WakeupLevel::High)); + } + } +} + +impl<'a, P: Pin + RTCPin> Drop for Ext0WakeupSource<'a, P> { + fn drop(&mut self) { + // should we have saved the pin configuration first? + // set pin back to IO_MUX (input_enable and func have no effect when pin is sent + // to IO_MUX) + self.pin.borrow_mut().rtc_set_config(true, false, 0); + } +} + +impl<'a> WakeSource for Ext1WakeupSource<'a> { + fn apply(&self, _rtc: &Rtc, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig) { + // don't power down RTC peripherals + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_ext1(true); + + // set pins to RTC function + let mut pins = self.pins.borrow_mut(); + let mut bits = 0u32; + for pin in pins.iter_mut() { + pin.rtc_set_config(true, true, 0); + bits |= 1 << pin.rtc_number(); + } + + unsafe { + // set pin register field + // set level register field + let rtc_cntl = &*esp32::RTC_CNTL::ptr(); + rtc_cntl.ext_wakeup1.modify(|_, w| w.sel().bits(bits)); + rtc_cntl + .ext_wakeup_conf + .modify(|_r, w| w.ext_wakeup1_lv().bit(self.level == WakeupLevel::High)); + } + } +} + +impl<'a> Drop for Ext1WakeupSource<'a> { + fn drop(&mut self) { + // should we have saved the pin configuration first? + // set pin back to IO_MUX (input_enable and func have no effect when pin is sent + // to IO_MUX) + let mut pins = self.pins.borrow_mut(); + for pin in pins.iter_mut() { + pin.rtc_set_config(true, false, 0); + } + } +} + +bitfield::bitfield! { + #[derive(Clone, Copy)] + pub struct RtcSleepConfig(u32); + impl Debug; + /// force normal voltage in sleep mode (digital domain memory) + pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0; + /// force normal voltage in sleep mode (RTC memory) + pub rtc_mem_inf_fpu, set_rtc_mem_inf_fpu: 1; + /// keep low voltage in sleep mode (even if ULP/touch is used) + pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 2; + /// power down RTC fast memory + pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 3; + /// power down RTC slow memory + pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 4; + /// power down RTC peripherals + pub rtc_peri_pd_en, set_rtc_peri_pd_en: 5; + /// power down WiFi + pub wifi_pd_en, set_wifi_pd_en: 6; + /// Power down Internal 8M oscillator + pub int_8m_pd_en, set_int_8m_pd_en: 7; + /// power down main RAM and ROM + pub rom_mem_pd_en, set_rom_mem_pd_en: 8; + /// power down digital domain + pub deep_slp, set_deep_slp: 9; + /// enable WDT flashboot mode + pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 10; + /// bias for digital domain, in active mode + pub u8, dig_dbias_wak, set_dig_dbias_wak: 13, 11; + /// bias for digital domain, in sleep mode + pub u8, dig_dbias_slp, set_dig_dbias_slp: 16, 14; + /// bias for RTC domain, in active mode + pub u8, rtc_dbias_wak, set_rtc_dbias_wak: 19, 17; + /// bias for RTC domain, in sleep mode + pub u8, rtc_dbias_slp, set_rtc_dbias_slp: 22, 20; + /// remove all peripheral force power up flags + pub lslp_meminf_pd, set_lslp_meminf_pd: 23; + /// power down VDDSDIO regulator + pub vddsdio_pd_en, set_vddsdio_pd_en: 24; + /// keep main XTAL powered up in sleep + pub xtal_fpu, set_xtal_fpu: 25; + /// enable deep sleep reject + pub deep_slp_reject, set_deep_slp_reject: 26; + /// enable light sleep reject + pub light_slp_reject, set_light_slp_reject: 27; +} + +impl Default for RtcSleepConfig { + fn default() -> Self { + let mut cfg = Self(Default::default()); + cfg.set_lslp_meminf_pd(true); + cfg.set_deep_slp_reject(true); + cfg.set_light_slp_reject(true); + cfg.set_dig_dbias_wak(RTC_CNTL_DBIAS_1V10); + cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg.set_rtc_dbias_wak(RTC_CNTL_DBIAS_1V10); + cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg + } +} + +impl RtcSleepConfig { + pub fn deep() -> Self { + let mut cfg = Self::default(); + cfg.set_deep_slp(true); + cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_0V90); + // cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_0V90); + cfg.set_vddsdio_pd_en(true); + cfg.set_int_8m_pd_en(true); + cfg.set_xtal_fpu(false); + cfg.set_wifi_pd_en(true); + cfg.set_rom_mem_pd_en(true); + cfg.set_rtc_peri_pd_en(true); + cfg.set_rtc_fastmem_pd_en(true); + cfg.set_rtc_slowmem_pd_en(true); + cfg + } + + fn base_settings(&self, _rtc: &Rtc) { + // settings derived from esp-idf after basic boot + unsafe { + let rtc_cntl = &*esp32::RTC_CNTL::ptr(); + #[rustfmt::skip] + rtc_cntl.options0.modify(|_, w| w + .bias_core_force_pu().clear_bit() + .bias_core_folw_8m().set_bit() + .bias_i2c_force_pu().clear_bit() + .bias_i2c_folw_8m().set_bit() + .bias_force_nosleep().clear_bit() + .bias_sleep_folw_8m().set_bit() + .xtl_force_pu().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.reg.modify(|_, w| w + .force_pu().clear_bit() + .dboost_force_pu().clear_bit() + .dboost_force_pd().set_bit() + ); + #[rustfmt::skip] + rtc_cntl.pwc.modify(|_, w| w + .slowmem_force_pu().clear_bit() + .fastmem_force_pu().clear_bit() + .force_noiso().clear_bit() + .slowmem_force_noiso().clear_bit() + .fastmem_force_noiso().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.dig_pwc.modify(|_, w| w + .dg_wrap_force_pu().clear_bit() + .wifi_force_pu().clear_bit() + .wifi_force_pd().set_bit() + .inter_ram4_force_pu().clear_bit() + .inter_ram3_force_pu().clear_bit() + .inter_ram2_force_pu().clear_bit() + .inter_ram1_force_pu().clear_bit() + .inter_ram0_force_pu().clear_bit() + .rom0_force_pu().clear_bit() + .lslp_mem_force_pu().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.dig_iso.modify(|_, w| w + .dg_wrap_force_noiso().clear_bit() + .wifi_force_noiso().clear_bit() + .wifi_force_iso().set_bit() + .inter_ram4_force_noiso().clear_bit() + .inter_ram3_force_noiso().clear_bit() + .inter_ram2_force_noiso().clear_bit() + .inter_ram1_force_noiso().clear_bit() + .inter_ram0_force_noiso().clear_bit() + .rom0_force_noiso().clear_bit() + .dg_pad_force_unhold().clear_bit() + .dg_pad_force_noiso().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.int_ena.modify(|_, w| w + .brown_out_int_ena().set_bit() + ); + } + } + + pub(crate) fn apply(&self, rtc: &Rtc) { + self.base_settings(rtc); + // like esp-idf rtc_sleep_init() + unsafe { + let rtc_cntl = &*esp32::RTC_CNTL::ptr(); + + #[rustfmt::skip] + rtc_cntl.timer5.modify(|_, w| w + .min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN) + // set rtc memory timer + .rtcmem_powerup_timer().bits(RTC_MEM_POWERUP_CYCLES) + .rtcmem_wait_timer().bits(RTC_MEM_WAIT_CYCLES) + ); + + #[rustfmt::skip] + rtc_cntl.timer3.modify(|_, w| w + // set rom&ram timer + .rom_ram_powerup_timer().bits(ROM_RAM_POWERUP_CYCLES) + .rom_ram_wait_timer().bits(ROM_RAM_WAIT_CYCLES) + // set wifi timer + .wifi_powerup_timer().bits(WIFI_POWERUP_CYCLES) + .wifi_wait_timer().bits(WIFI_WAIT_CYCLES) + ); + + #[rustfmt::skip] + rtc_cntl.timer4.modify(|_, w| w + // set rtc peri timer + .powerup_timer().bits(RTC_POWERUP_CYCLES) + .wait_timer().bits(RTC_WAIT_CYCLES) + // set digital wrap timer + .dg_wrap_powerup_timer().bits(DG_WRAP_POWERUP_CYCLES) + .dg_wrap_wait_timer().bits(DG_WRAP_WAIT_CYCLES) + ); + + #[rustfmt::skip] + rtc_cntl.dig_pwc.modify(|_, w| w + .lslp_mem_force_pu().bit(self.lslp_mem_inf_fpu()) + ); + + // remove all peripheral force power up flags + if self.lslp_meminf_pd() { + #[rustfmt::skip] + rtc_cntl.dig_pwc.modify(|_, w| w + .lslp_mem_force_pu().clear_bit() + ); + + #[rustfmt::skip] + rtc_cntl.pwc.modify(|_, w| w + .slowmem_force_pu().clear_bit() + .fastmem_force_pu().clear_bit() + ); + + // esp-idf also clears these: + #[rustfmt::skip] + (&*esp32::DPORT::ptr()).mem_pd_mask.modify(|_, w| w + .lslp_mem_pd_mask().clear_bit() + ); + #[rustfmt::skip] + (&*esp32::I2S0::ptr()).pd_conf.modify(|_, w| w + .plc_mem_force_pu().clear_bit() + .fifo_force_pu().clear_bit() + ); + #[rustfmt::skip] + (&*esp32::BB::ptr()).bbpd_ctrl.modify(|_, w| w + .fft_force_pu().clear_bit() + .dc_est_force_pu().clear_bit() + ); + #[rustfmt::skip] + (&*esp32::NRX::ptr()).nrxpd_ctrl.modify(|_, w| w + .rx_rot_force_pu().clear_bit() + .vit_force_pu().clear_bit() + .demap_force_pu().clear_bit() + ); + // #[rustfmt::skip] + // (&*esp32::FE::ptr()).gen_ctrl.modify(|_, w| w + // .iq_est_force_pu().clear_bit() + // ); + // #[rustfmt::skip] + // (&*esp32::FE2::ptr()).tx_interp_ctrl.modify(|_, w| w + // .inf_force_pu().clear_bit() + // ); + } + + #[rustfmt::skip] + rtc_cntl.pwc.modify(|_, w| w + .slowmem_folw_cpu().bit(self.rtc_mem_inf_follow_cpu()) + .fastmem_folw_cpu().bit(self.rtc_mem_inf_follow_cpu()) + // TODO: does this need to be optional based on if there is something stored in fastmem? + //.fastmem_pd_en().bit(self.rtc_fastmem_pd_en()) + .fastmem_force_pu().bit(!self.rtc_fastmem_pd_en()) + .fastmem_force_lpu().bit(!self.rtc_fastmem_pd_en()) + .fastmem_force_noiso().bit(!self.rtc_fastmem_pd_en()) + .slowmem_pd_en().bit(self.rtc_slowmem_pd_en()) + .slowmem_force_pu().bit(!self.rtc_slowmem_pd_en()) + .slowmem_force_noiso().bit(!self.rtc_slowmem_pd_en()) + .slowmem_force_lpu().bit(!self.rtc_slowmem_pd_en()) + .pd_en().bit(self.rtc_peri_pd_en()) + ); + + // #[rustfmt::skip] + // rtc_cntl.dig_pwc.modify(|_, w| w + // .wifi_pd_en().bit(self.wifi_pd_en()) + // .rom0_pd_en().bit(self.rom_mem_pd_en()) + // ); + + if self.deep_slp() { + #[rustfmt::skip] + rtc_cntl.dig_iso.modify(|_, w| w + .dg_wrap_force_noiso().clear_bit() + .wifi_force_noiso().clear_bit() + .dg_pad_force_iso().clear_bit() + .dg_pad_force_noiso().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.dig_pwc.modify(|_, w| w + .dg_wrap_pd_en().set_bit() + .dg_wrap_force_pu().clear_bit() + .dg_wrap_force_pd().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.options0.modify(|_, w| w + .bias_force_nosleep().clear_bit() + .bb_i2c_force_pu().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.ana_conf.modify(|_, w| w + .ckgen_i2c_pu().clear_bit() + .pll_i2c_pu().clear_bit() + .rfrx_pbus_pu().clear_bit() + .txrf_i2c_pu().clear_bit() + ); + } else { + #[rustfmt::skip] + rtc_cntl.dig_pwc.modify(|_, w| w + .dg_wrap_pd_en().clear_bit() + ); + #[rustfmt::skip] + rtc_cntl.bias_conf.modify(|_, w| w + .dbg_atten().bits(0) + ); + } + + #[rustfmt::skip] + rtc_cntl.options0.modify(|_, w| w + .xtl_force_pu().bit(self.xtal_fpu()) + ); + + #[rustfmt::skip] + rtc_cntl.clk_conf.modify(|_, w| w + .ck8m_force_pu().bit(!self.int_8m_pd_en()) + ); + + // enable VDDSDIO control by state machine + #[rustfmt::skip] + rtc_cntl.sdio_conf.modify(|_, w| w + .sdio_force().clear_bit() + .sdio_pd_en().bit(self.vddsdio_pd_en()) + ); + + #[rustfmt::skip] + rtc_cntl.reg.modify(|_, w| w + .dbias_slp().bits(self.rtc_dbias_slp()) + .dbias_wak().bits(self.rtc_dbias_wak()) + .dig_dbias_slp().bits(self.dig_dbias_slp()) + .dig_dbias_wak().bits(self.dig_dbias_wak()) + ); + + #[rustfmt::skip] + rtc_cntl.slp_reject_conf.modify(|_, w| w + .deep_slp_reject_en().bit(self.deep_slp_reject()) + .light_slp_reject_en().bit(self.light_slp_reject()) + ); + } + } +} diff --git a/esp-hal-common/src/rtc_cntl/sleep.rs b/esp-hal-common/src/rtc_cntl/sleep.rs new file mode 100644 index 00000000000..0c373318b3a --- /dev/null +++ b/esp-hal-common/src/rtc_cntl/sleep.rs @@ -0,0 +1,96 @@ +use core::{cell::RefCell, time::Duration}; + +use crate::{ + gpio::{Pin, RTCPin}, + Rtc, +}; + +#[cfg_attr(esp32, path = "rtc/esp32_sleep.rs")] +mod rtc_sleep; +pub use rtc_sleep::*; + +#[derive(Debug, Default, Clone, Copy, PartialEq)] +pub enum WakeupLevel { + Low, + #[default] + High, +} + +#[derive(Debug, Default, Clone, Copy)] +pub struct TimerWakeupSource { + duration: Duration, +} + +impl TimerWakeupSource { + pub fn new(duration: Duration) -> Self { + Self { duration } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Error { + NotRtcPin, + TooManyWakeupSources, +} + +#[allow(unused)] +#[derive(Debug)] +pub struct Ext0WakeupSource<'a, P: RTCPin + Pin> { + pin: RefCell<&'a mut P>, + level: WakeupLevel, +} + +impl<'a, P: RTCPin + Pin> Ext0WakeupSource<'a, P> { + pub fn new(pin: &'a mut P, level: WakeupLevel) -> Self { + Self { + pin: RefCell::new(pin), + level, + } + } +} + +pub struct Ext1WakeupSource<'a> { + pins: RefCell<&'a mut [&'a mut dyn RTCPin]>, + level: WakeupLevel, +} + +impl<'a> Ext1WakeupSource<'a> { + pub fn new(pins: &'a mut [&'a mut dyn RTCPin], level: WakeupLevel) -> Self { + Self { + pins: RefCell::new(pins), + level, + } + } +} + +bitfield::bitfield! { + #[derive(Default, Clone, Copy)] + pub struct WakeTriggers(u16); + impl Debug; + /// EXT0 GPIO wakeup + pub ext0, set_ext0: 0; + /// EXT1 GPIO wakeup + pub ext1, set_ext1: 1; + /// GPIO wakeup (light sleep only) + pub gpio, set_gpio: 2; + /// Timer wakeup + pub timer, set_timer: 3; + /// SDIO wakeup (light sleep only) + pub sdio, set_sdio: 4; + /// MAC wakeup (light sleep only) + pub mac, set_mac: 5; + /// UART0 wakeup (light sleep only) + pub uart0, set_uart0: 6; + /// UART1 wakeup (light sleep only) + pub uart1, set_uart1: 7; + /// Touch wakeup + pub touch, set_touch: 8; + /// ULP wakeup + pub ulp, set_ulp: 9; + /// BT wakeup (light sleep only) + pub bt, set_bt: 10; +} + +pub trait WakeSource { + fn apply(&self, rtc: &Rtc, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig); +} diff --git a/esp-hal-common/src/soc/esp32/gpio.rs b/esp-hal-common/src/soc/esp32/gpio.rs index c0474256674..caf02168cc0 100644 --- a/esp-hal-common/src/soc/esp32/gpio.rs +++ b/esp-hal-common/src/soc/esp32/gpio.rs @@ -715,6 +715,27 @@ crate::gpio::analog! { (27, 17, touch_pad7, mux_sel, fun_sel, fun_ie, rue, rde ) } +crate::gpio::rtc_pins! { + (36, 0, sensor_pads, sense1_ ) + (37, 1, sensor_pads, sense2_ ) + (38, 2, sensor_pads, sense3_ ) + (39, 3, sensor_pads, sense4_ ) + (34, 4, adc_pad, adc1_ ) + (35, 5, adc_pad, adc2_ ) + (25, 6, pad_dac1, pdac1_ ) + (26, 7, pad_dac2, pdac2_ ) + (33, 8, xtal_32k_pad, x32n_ ) + (32, 9, xtal_32k_pad, x32p_ ) + (4, 10, touch_pad0, "") + (0, 11, touch_pad1, "") + (2, 12, touch_pad2, "") + (15, 13, touch_pad3, "") + (13, 14, touch_pad4, "") + (12, 15, touch_pad5, "") + (14, 16, touch_pad6, "") + (27, 17, touch_pad7, "") +} + impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank0 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() diff --git a/esp-hal-common/src/soc/esp32s2/gpio.rs b/esp-hal-common/src/soc/esp32s2/gpio.rs index 6bc5c9a0466..d6d62ea78ca 100644 --- a/esp-hal-common/src/soc/esp32s2/gpio.rs +++ b/esp-hal-common/src/soc/esp32s2/gpio.rs @@ -378,6 +378,31 @@ crate::gpio::analog! { (21, 21, rtc_pad21, mux_sel, fun_sel, fun_ie, rue, rde) } +crate::gpio::rtc_pins! { + ( 0, 0, touch_pad[0], touch_pad0_) + ( 1, 1, touch_pad[1], touch_pad0_) + ( 2, 2, touch_pad[2], touch_pad0_) + ( 3, 3, touch_pad[3], touch_pad0_) + ( 4, 4, touch_pad[4], touch_pad0_) + ( 5, 5, touch_pad[5], touch_pad0_) + ( 6, 6, touch_pad[6], touch_pad0_) + ( 7, 7, touch_pad[7], touch_pad0_) + ( 8, 8, touch_pad[8], touch_pad0_) + ( 9, 9, touch_pad[9], touch_pad0_) + (10, 10, touch_pad[10], touch_pad0_) + (11, 11, touch_pad[11], touch_pad0_) + (12, 12, touch_pad[12], touch_pad0_) + (13, 13, touch_pad[13], touch_pad0_) + (14, 14, touch_pad[14], touch_pad0_) + (15, 15, xtal_32p_pad, x32p_) + (16, 16, xtal_32n_pad, x32n_) + (17, 17, pad_dac1, pdac1_) + (18, 18, pad_dac2, pdac2_) + (19, 19, rtc_pad19, "") + (20, 20, rtc_pad20, "") + (21, 21, rtc_pad21, "") +} + impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank0 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() diff --git a/esp-hal-common/src/soc/esp32s3/gpio.rs b/esp-hal-common/src/soc/esp32s3/gpio.rs index fd2eea84e1f..7bcf204fba0 100644 --- a/esp-hal-common/src/soc/esp32s3/gpio.rs +++ b/esp-hal-common/src/soc/esp32s3/gpio.rs @@ -333,6 +333,31 @@ crate::gpio::analog! { (21, 21, rtc_pad21, mux_sel, fun_sel, fun_ie, rue, rde) } +crate::gpio::rtc_pins! { + ( 0, 0, touch_pad0, "") + ( 1, 1, touch_pad1, "") + ( 2, 2, touch_pad2, "") + ( 3, 3, touch_pad3, "") + ( 4, 4, touch_pad4, "") + ( 5, 5, touch_pad5, "") + ( 6, 6, touch_pad6, "") + ( 7, 7, touch_pad7, "") + ( 8, 8, touch_pad8, "") + ( 9, 9, touch_pad9, "") + (10, 10, touch_pad10, "") + (11, 11, touch_pad11, "") + (12, 12, touch_pad12, "") + (13, 13, touch_pad13, "") + (14, 14, touch_pad14, "") + (15, 15, xtal_32p_pad, x32p_) + (16, 16, xtal_32n_pad, x32n_) + (17, 17, pad_dac1, pdac1_) + (18, 18, pad_dac2, pdac2_) + (19, 19, rtc_pad19, "") + (20, 20, rtc_pad20, "") + (21, 21, rtc_pad21, "") +} + // Whilst the S3 is a dual core chip, it shares the enable registers between // cores so treat it as a single core device impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank0 { diff --git a/esp32-hal/examples/sleep_timer.rs b/esp32-hal/examples/sleep_timer.rs new file mode 100644 index 00000000000..44a5be340b6 --- /dev/null +++ b/esp32-hal/examples/sleep_timer.rs @@ -0,0 +1,59 @@ +//! Demonstrates deep sleep with timer wakeup + +#![no_std] +#![no_main] + +use core::time::Duration; + +use esp32_hal as hal; +use esp_backtrace as _; +use esp_println::println; +use hal::{ + clock::ClockControl, + entry, + peripherals::Peripherals, + prelude::*, + rtc_cntl::{get_reset_reason, get_wakeup_cause, sleep::TimerWakeupSource, SocResetReason}, + timer::TimerGroup, + Delay, + Rtc, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the RTC and TIMG watchdog timers + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + println!("up and runnning!"); + let reason = get_reset_reason(hal::Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + println!("reset reason: {:?}", reason); + let wake_reason = get_wakeup_cause(); + println!("wake reason: {:?}", wake_reason); + + let mut delay = Delay::new(&clocks); + + let timer = TimerWakeupSource::new(Duration::from_secs(30)); + println!("sleeping!"); + delay.delay_ms(100u32); + rtc.sleep_deep(&[&timer], &mut delay); +} diff --git a/esp32-hal/examples/sleep_timer_ext0.rs b/esp32-hal/examples/sleep_timer_ext0.rs new file mode 100644 index 00000000000..0260de33c78 --- /dev/null +++ b/esp32-hal/examples/sleep_timer_ext0.rs @@ -0,0 +1,69 @@ +//! Demonstrates deep sleep with timer and ext0 (using gpio27) wakeup + +#![no_std] +#![no_main] + +use core::time::Duration; + +use esp32_hal as hal; +use esp_backtrace as _; +use esp_println::println; +use hal::{ + clock::ClockControl, + entry, + peripherals::Peripherals, + prelude::*, + rtc_cntl::{ + get_reset_reason, + get_wakeup_cause, + sleep::{Ext0WakeupSource, TimerWakeupSource, WakeupLevel}, + SocResetReason, + }, + timer::TimerGroup, + Delay, + Rtc, + IO, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the RTC and TIMG watchdog timers + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut ext0_pin = io.pins.gpio27; + + println!("up and runnning!"); + let reason = get_reset_reason(hal::Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + println!("reset reason: {:?}", reason); + let wake_reason = get_wakeup_cause(); + println!("wake reason: {:?}", wake_reason); + + let mut delay = Delay::new(&clocks); + + let timer = TimerWakeupSource::new(Duration::from_secs(30)); + let ext0 = Ext0WakeupSource::new(&mut ext0_pin, WakeupLevel::High); + println!("sleeping!"); + delay.delay_ms(100u32); + rtc.sleep_deep(&[&timer, &ext0], &mut delay); +} diff --git a/esp32-hal/examples/sleep_timer_ext1.rs b/esp32-hal/examples/sleep_timer_ext1.rs new file mode 100644 index 00000000000..5716d9a55e6 --- /dev/null +++ b/esp32-hal/examples/sleep_timer_ext1.rs @@ -0,0 +1,71 @@ +//! Demonstrates deep sleep with timer and ext1 (using gpio27 & gpio23) wakeup + +#![no_std] +#![no_main] + +use core::time::Duration; + +use esp32_hal as hal; +use esp_backtrace as _; +use esp_println::println; +use hal::{ + clock::ClockControl, + entry, + peripherals::Peripherals, + prelude::*, + rtc_cntl::{ + get_reset_reason, + get_wakeup_cause, + sleep::{Ext1WakeupSource, TimerWakeupSource, WakeupLevel}, + SocResetReason, + }, + timer::TimerGroup, + Delay, + Rtc, + IO, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the RTC and TIMG watchdog timers + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut pin27 = io.pins.gpio27; + let mut pin32 = io.pins.gpio32; + + println!("up and runnning!"); + let reason = get_reset_reason(hal::Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + println!("reset reason: {:?}", reason); + let wake_reason = get_wakeup_cause(); + println!("wake reason: {:?}", wake_reason); + + let mut delay = Delay::new(&clocks); + + let timer = TimerWakeupSource::new(Duration::from_secs(30)); + let mut wakeup_pins: [&mut dyn hal::gpio::RTCPin; 2] = [&mut pin27, &mut pin32]; + let ext1 = Ext1WakeupSource::new(&mut wakeup_pins, WakeupLevel::High); + println!("sleeping!"); + delay.delay_ms(100u32); + rtc.sleep_deep(&[&timer, &ext1], &mut delay); +}