diff --git a/CHANGELOG.md b/CHANGELOG.md index c2488f3b270..070c940dafa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715) - Implement RTCIO pullup, pulldown and hold control for Xtensa MCUs (#684) - Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720] +- S3: Implement RTCIO wakeup source (#690) - Add PARL_IO TX driver for ESP32-C6 / ESP32-H2 (#733) - Implement `ufmt_write::uWrite` trait for USB Serial JTAG (#751) diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs index 0ebe8d09f84..f46b3fffbbf 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs @@ -1,6 +1,6 @@ use super::{Ext0WakeupSource, Ext1WakeupSource, TimerWakeupSource, WakeSource, WakeTriggers}; use crate::{ - gpio::{Pin, RTCPin, RtcFunction}, + gpio::{RTCPin, RtcFunction}, rtc_cntl::{sleep::WakeupLevel, Clock, RtcClock}, Rtc, }; @@ -65,7 +65,7 @@ impl WakeSource for TimerWakeupSource { } } -impl WakeSource for Ext0WakeupSource<'_, P> { +impl WakeSource for Ext0WakeupSource<'_, 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); @@ -91,7 +91,7 @@ impl WakeSource for Ext0WakeupSource<'_, P> { } } -impl Drop for Ext0WakeupSource<'_, P> { +impl Drop for Ext0WakeupSource<'_, 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 diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs index 051b2841710..55fe6820984 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs @@ -7,9 +7,9 @@ use super::{ WakeupLevel, }; use crate::{ - gpio::{Pin, RTCPin, RtcFunction}, + gpio::{RTCPin, RtcFunction}, regi2c_write_mask, - rtc_cntl::{Clock, RtcClock}, + rtc_cntl::{sleep::RtcioWakeupSource, Clock, RtcClock}, Rtc, }; @@ -110,7 +110,7 @@ impl WakeSource for TimerWakeupSource { } } -impl WakeSource for Ext0WakeupSource<'_, P> { +impl WakeSource for Ext0WakeupSource<'_, 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); @@ -136,7 +136,7 @@ impl WakeSource for Ext0WakeupSource<'_, P> { } } -impl Drop for Ext0WakeupSource<'_, P> { +impl Drop for Ext0WakeupSource<'_, 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 @@ -191,6 +191,58 @@ impl Drop for Ext1WakeupSource<'_, '_> { } } +impl<'a, 'b> RtcioWakeupSource<'a, 'b> { + fn apply_pin(&self, pin: &mut dyn RTCPin, level: WakeupLevel) { + let rtcio = unsafe { &*crate::peripherals::RTC_IO::PTR }; + + pin.rtc_set_config(true, true, RtcFunction::Rtc); + + rtcio.pin[pin.number() as usize].modify(|_, w| { + w.wakeup_enable().set_bit().int_type().variant(match level { + WakeupLevel::Low => 4, + WakeupLevel::High => 5, + }) + }); + } +} + +impl WakeSource for RtcioWakeupSource<'_, '_> { + fn apply(&self, _rtc: &Rtc, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig) { + let mut pins = self.pins.borrow_mut(); + + if pins.is_empty() { + return; + } + + // don't power down RTC peripherals + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_gpio(true); + + // Since we only use RTCIO pins, we can keep deep sleep enabled. + let sens = unsafe { &*crate::peripherals::SENS::PTR }; + + // TODO: disable clock when not in use + sens.sar_peri_clk_gate_conf + .modify(|_, w| w.iomux_clk_en().set_bit()); + + for (pin, level) in pins.iter_mut() { + self.apply_pin(*pin, *level); + } + } +} + +impl Drop for RtcioWakeupSource<'_, '_> { + 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, _level) in pins.iter_mut() { + pin.rtc_set_config(true, false, RtcFunction::Rtc); + } + } +} + bitfield::bitfield! { #[derive(Clone, Copy)] pub struct RtcSleepConfig(u64); diff --git a/esp-hal-common/src/rtc_cntl/sleep.rs b/esp-hal-common/src/rtc_cntl/sleep.rs index 15692ce2357..a70d9e40695 100644 --- a/esp-hal-common/src/rtc_cntl/sleep.rs +++ b/esp-hal-common/src/rtc_cntl/sleep.rs @@ -21,10 +21,7 @@ use core::{cell::RefCell, time::Duration}; -use crate::{ - gpio::{Pin, RTCPin}, - Rtc, -}; +use crate::{gpio::RTCPin, Rtc}; #[cfg_attr(esp32, path = "rtc/esp32_sleep.rs")] #[cfg_attr(esp32s3, path = "rtc/esp32s3_sleep.rs")] @@ -58,12 +55,12 @@ pub enum Error { #[allow(unused)] #[derive(Debug)] -pub struct Ext0WakeupSource<'a, P: RTCPin + Pin> { +pub struct Ext0WakeupSource<'a, P: RTCPin> { pin: RefCell<&'a mut P>, level: WakeupLevel, } -impl<'a, P: RTCPin + Pin> Ext0WakeupSource<'a, P> { +impl<'a, P: RTCPin> Ext0WakeupSource<'a, P> { pub fn new(pin: &'a mut P, level: WakeupLevel) -> Self { Self { pin: RefCell::new(pin), @@ -86,6 +83,24 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { } } +/// RTC_IO wakeup source +/// +/// RTC_IO wakeup allows configuring any combination of RTC_IO pins with +/// arbitrary wakeup levels to wake up the chip from sleep. This wakeup source +/// can be used to wake up from both light and deep sleep. +#[allow(unused)] +pub struct RtcioWakeupSource<'a, 'b> { + pins: RefCell<&'a mut [(&'b mut dyn RTCPin, WakeupLevel)]>, +} + +impl<'a, 'b> RtcioWakeupSource<'a, 'b> { + pub fn new(pins: &'a mut [(&'b mut dyn RTCPin, WakeupLevel)]) -> Self { + Self { + pins: RefCell::new(pins), + } + } +} + bitfield::bitfield! { #[derive(Default, Clone, Copy)] pub struct WakeTriggers(u16); diff --git a/esp32s3-hal/examples/sleep_timer_rtcio.rs b/esp32s3-hal/examples/sleep_timer_rtcio.rs new file mode 100644 index 00000000000..e197cbf8741 --- /dev/null +++ b/esp32s3-hal/examples/sleep_timer_rtcio.rs @@ -0,0 +1,79 @@ +//! Demonstrates deep sleep with timer and ext0 (using gpio18) wakeup + +#![no_std] +#![no_main] + +use core::time::Duration; + +use esp32s3_hal as hal; +use esp_backtrace as _; +use esp_hal_common::{ + gpio::{RTCPin, RTCPinWithResistors}, + rtc_cntl::sleep::RtcioWakeupSource, +}; +use esp_println::println; +use hal::{ + clock::ClockControl, + entry, + peripherals::Peripherals, + prelude::*, + rtc_cntl::{ + get_reset_reason, + get_wakeup_cause, + sleep::{TimerWakeupSource, WakeupLevel}, + SocResetReason, + }, + timer::TimerGroup, + Delay, + Rtc, + IO, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.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 rtcio_pin18 = io.pins.gpio18; + + rtcio_pin18.rtcio_pad_hold(true); + rtcio_pin18.rtcio_pullup(true); + + 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 RTCPin, WakeupLevel); 1] = + [(&mut rtcio_pin18, WakeupLevel::Low)]; + let rtcio = RtcioWakeupSource::new(&mut wakeup_pins); + println!("sleeping!"); + delay.delay_ms(100u32); + rtc.sleep_deep(&[&timer, &rtcio], &mut delay); +}