From 5f9a4a495e90f06a01ca27d5f41a7c843e5290f6 Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Wed, 30 Aug 2023 14:25:50 +0200 Subject: [PATCH 1/8] sleep: Adds module for light and deep sleep with examples --- examples/deep_sleep.rs | 56 ++++++ examples/light_sleep.rs | 140 +++++++++++++++ src/lib.rs | 2 + src/sleep.rs | 376 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+) create mode 100644 examples/deep_sleep.rs create mode 100644 examples/light_sleep.rs create mode 100644 src/sleep.rs diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs new file mode 100644 index 00000000000..1710022e738 --- /dev/null +++ b/examples/deep_sleep.rs @@ -0,0 +1,56 @@ +//! Tests deep sleep +//! +//! Enables multiple deep sleep wakeup sources and then enter deep sleep. +//! There is no loop here, since the program will not continue after deep sleep. +//! For ESP32c3, only timer wakeup is supported. +//! The program starts by printing reset and wakeup reason, since the deep +//! sleep effectively ends the program, this is how we get information about +//! the previous run. + +use core::time::Duration; +use esp_idf_hal::peripherals::Peripherals; +use esp_idf_hal::reset::{ResetReason, WakeupReason}; +use esp_idf_hal::sleep::*; + +fn print_wakeup_result() { + let reset_reason = ResetReason::get(); + let wakeup_reason = WakeupReason::get(); + println!( + "reset after {:?} wakeup due to {:?}", + reset_reason, wakeup_reason + ); +} + +fn main() -> anyhow::Result<()> { + esp_idf_sys::link_patches(); + + print_wakeup_result(); + + #[cfg(any(esp32, esp32s2, esp32s3))] + let peripherals = Peripherals::take().unwrap(); + + // RTC wakeup definitions + #[cfg(esp32)] + let rtc_pins = [ + RtcWakeupPin::new(peripherals.pins.gpio4.into(), false, true), + RtcWakeupPin::new(peripherals.pins.gpio27.into(), false, true), + ]; + #[cfg(any(esp32s2, esp32s3))] + let rtc_pins = [ + RtcWakeupPin::new(peripherals.pins.gpio1.into(), false, true), + RtcWakeupPin::new(peripherals.pins.gpio2.into(), false, true), + ]; + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_wakeup = Some(RtcWakeup::new(&rtc_pins, RtcWakeLevel::AnyHigh)); + + let dsleep = DeepSleep { + timer: Some(TimerWakeup::new(Duration::from_secs(5))), + #[cfg(any(esp32, esp32s2, esp32s3))] + rtc: rtc_wakeup, + ..Default::default() + }; + + println!("Deep sleep with: {:?}", dsleep); + dsleep.prepare()?; + dsleep.sleep(); +} diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs new file mode 100644 index 00000000000..414e5cf5c68 --- /dev/null +++ b/examples/light_sleep.rs @@ -0,0 +1,140 @@ +//! Tests light sleep +//! +//! Enables multiple light sleep wakeup sources and do sleeps in a loop. +//! Prints wakeup reason and sleep time on wakeup. + +use core::time::Duration; +use esp_idf_hal::gpio::{self, AnyInputPin, PinDriver}; +use esp_idf_hal::peripherals::Peripherals; +use esp_idf_hal::prelude::*; +use esp_idf_hal::reset::WakeupReason; +use esp_idf_hal::sleep::*; +use esp_idf_hal::uart::config::Config; +use esp_idf_hal::uart::UartDriver; +use std::thread; +use std::time::Instant; + +use crate::gpio::Level; + +fn print_wakeup_result(time_before: Instant) { + let time_after = Instant::now(); + + let wakeup_reason = WakeupReason::get(); + println!( + "wake up from light sleep due to {:?} which lasted for {:?}", + wakeup_reason, + time_after - time_before + ); +} + +fn main() -> anyhow::Result<()> { + esp_idf_sys::link_patches(); + + // run in a thread with increased stack size to prevent overflow + let builder = std::thread::Builder::new().stack_size(8 * 1024); + let th = builder.spawn(move || -> anyhow::Result<()> { + let peripherals = Peripherals::take().unwrap(); + + // RTC wakeup definitions + #[cfg(esp32)] + let rtc_pins = [ + RtcWakeupPin::new(peripherals.pins.gpio26.into(), false, true), + RtcWakeupPin::new(peripherals.pins.gpio27.into(), false, true), + ]; + #[cfg(any(esp32s2, esp32s3))] + let rtc_pins = [ + RtcWakeupPin::new(peripherals.pins.gpio1.into(), false, true), + RtcWakeupPin::new(peripherals.pins.gpio2.into(), false, true), + ]; + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_wakeup = Some(RtcWakeup::new(&rtc_pins, RtcWakeLevel::AnyHigh)); + + // GPIO wakeup definitions + #[cfg(esp32)] + let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio16))?; + #[cfg(esp32)] + let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio17))?; + + #[cfg(any(esp32s2, esp32s3))] + let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio37))?; + #[cfg(any(esp32s2, esp32s3))] + let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio38))?; + + #[cfg(esp32c3)] + let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio6))?; + #[cfg(esp32c3)] + let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio7))?; + + let gpio_pins = [ + GpioWakeupPin::new(gpio_pin1, Level::High)?, + GpioWakeupPin::new(gpio_pin2, Level::High)?, + ]; + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + let gpio_wakeup = Some(GpioWakeup::new(&gpio_pins)); + #[cfg(not(any(esp32, esp32c3, esp32s2, esp32s3)))] + let gpio_wakeup: Option = None; + + // UART definitions + let config = Config::new().baudrate(Hertz(115_200)); + #[cfg(any(esp32))] + let uart = UartDriver::new( + peripherals.uart0, + peripherals.pins.gpio4, + peripherals.pins.gpio3, + Option::::None, + Option::::None, + &config, + )?; + #[cfg(any(esp32s2, esp32s3))] + let uart = UartDriver::new( + peripherals.uart0, + peripherals.pins.gpio43, + peripherals.pins.gpio44, + Option::::None, + Option::::None, + &config, + )?; + #[cfg(esp32c3)] + let uart = UartDriver::new( + peripherals.uart0, + peripherals.pins.gpio21, + peripherals.pins.gpio20, + Option::::None, + Option::::None, + &config, + )?; + #[cfg(any(esp32, esp32s2, esp32s3, esp32c3))] + let uart_wakeup = Some(UartWakeup::new(&uart, 3)); + #[cfg(not(any(esp32, esp32s2, esp32s3, esp32c3)))] + let uart_wakeup: Option = None; + + let lsleep = LightSleep { + timer: Some(TimerWakeup::new(Duration::from_secs(5))), + #[cfg(any(esp32, esp32s2, esp32s3))] + rtc: rtc_wakeup, + gpio: gpio_wakeup, + uart: uart_wakeup, + ..Default::default() + }; + + loop { + println!("Light sleep with: {:?}", lsleep); + // short sleep to flush stdout + thread::sleep(Duration::from_millis(60)); + + let time_before = Instant::now(); + let err = lsleep.sleep(); + match err { + Ok(_) => print_wakeup_result(time_before), + Err(e) => { + println!("failed to do sleep: {:?}", e); + thread::sleep(Duration::from_secs(1)); + } + } + println!("---"); + } + })?; + + let _err = th.join(); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 8aa00210e67..d0f5ebbfdcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,8 @@ pub mod rmt; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod rom; #[cfg(not(feature = "riscv-ulp-hal"))] +pub mod sleep; +#[cfg(not(feature = "riscv-ulp-hal"))] pub mod spi; pub mod sys; #[cfg(not(feature = "riscv-ulp-hal"))] diff --git a/src/sleep.rs b/src/sleep.rs new file mode 100644 index 00000000000..bedb41549d6 --- /dev/null +++ b/src/sleep.rs @@ -0,0 +1,376 @@ +//! Driver for light and deep sleep. +//! +//! The ESP can be put into light or deep sleep. In light sleep, the CPU and peripherals +//! are still powered, but do not run (clock-gated). When woken up, the CPU and its +//! peripherals keep their states and the CPU continues running from the same place in the +//! code. +//! +//! Deep sleep is similar to light sleep, but the CPU is also powered down. Only RTC and +//! possibly ULP co processor is running. When woking up, the CPU does not keep its state, +//! and the program starts from the beginning. +//! +//! When enter either light or deep sleep, one or more wakeup sources must be enabled. +//! In this driver, the various wakeup sources are defined as different structs, which +//! can be added to a light or deep sleep struct. This struct can then be used to perform +//! a sleep operation with the given wakeup sources. +//! +//! The wakeup sources available depends on the ESP chip and type of sleep. The driver +//! intends to enforce these constraints at compile time where possible, i.e. if it builds +//! it should work. +//! + +use core::fmt; +#[cfg(not(any(esp32, esp32s2, esp32s3)))] +use core::marker::PhantomData; +use core::time::Duration; +use esp_idf_sys::*; + +#[cfg(any(esp32, esp32s2, esp32s3))] +use crate::gpio::Pin; +use crate::gpio::{AnyInputPin, Input, Level, PinDriver}; +use crate::uart::UartDriver; + +/// Will wake the CPU up after a given duration +#[derive(Debug)] +pub struct TimerWakeup { + pub duration: Duration, +} + +impl TimerWakeup { + pub fn new(duration: Duration) -> Self { + Self { duration } + } + + pub fn apply(&self) -> Result<(), EspError> { + esp!(unsafe { esp_sleep_enable_timer_wakeup(self.duration.as_micros() as u64) })?; + Ok(()) + } +} + +/// Will wake up the CPU based on changes in RTC GPIO pins. Is available for both light and +/// deep sleep. However, for light sleep, the GpioWakeup struct offers more flexibility. +/// Pullup and pulldown can be enabled for each pin. This settings will be applied when +/// deep sleep is enabled. +#[cfg(any(esp32, esp32s2, esp32s3))] +pub struct RtcWakeup<'a> { + pub pins: &'a [RtcWakeupPin], + pub wake_level: RtcWakeLevel, +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl<'a> RtcWakeup<'a> { + pub fn new(pins: &'a [RtcWakeupPin], wake_level: RtcWakeLevel) -> Self { + Self { pins, wake_level } + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl<'a> RtcWakeup<'a> { + fn mask(&self) -> u64 { + let mut m: u64 = 0; + for pin in self.pins.iter() { + m |= 1 << pin.pin.pin(); + } + m + } + + pub fn apply(&self) -> Result<(), EspError> { + #[cfg(any(esp32, esp32s3))] + for pin in self.pins { + if pin.pullup || pin.pulldown { + esp!(unsafe { + esp_sleep_pd_config( + esp_sleep_pd_domain_t_ESP_PD_DOMAIN_RTC_PERIPH, + esp_sleep_pd_option_t_ESP_PD_OPTION_ON, + ) + })?; + + if pin.pullup { + esp!(unsafe { rtc_gpio_pullup_en(pin.pin.pin()) })?; + } + if pin.pulldown { + esp!(unsafe { rtc_gpio_pulldown_en(pin.pin.pin()) })?; + } + } + } + + if !self.pins.is_empty() { + /* Do not use ext0, as ext1 with one pin does the same */ + esp!(unsafe { esp_sleep_enable_ext1_wakeup(self.mask(), self.wake_level.mode()) })?; + } + + Ok(()) + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl<'a> fmt::Debug for RtcWakeup<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RtcWakeup {{ pins: [")?; + + for pin in self.pins.iter() { + write!(f, "{:?} ", pin.pin.pin())?; + } + write!( + f, + "] mask: {:#x} wake_level: {:?} }}", + self.mask(), + self.wake_level + ) + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +#[derive(Debug)] +pub enum RtcWakeLevel { + AllLow, + AnyHigh, +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl RtcWakeLevel { + pub fn mode(&self) -> u32 { + match self { + RtcWakeLevel::AllLow => esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ALL_LOW, + RtcWakeLevel::AnyHigh => esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_HIGH, + } + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub struct RtcWakeupPin { + pub pin: AnyInputPin, // this should use RtcInput somehow + pub pullup: bool, + pub pulldown: bool, +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl RtcWakeupPin { + pub fn new(pin: AnyInputPin, pullup: bool, pulldown: bool) -> Self { + Self { + pin, + pullup, + pulldown, + } + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl fmt::Debug for RtcWakeupPin { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "RtcWakeupPin {{ pin: {:?} pullup {:?} pulldown {:?}}}", + self.pin.pin(), + self.pullup, + self.pulldown + ) + } +} + +/// Will wake up the CPU based on changes in GPIO pins. Is only available for light sleep. +/// It takes PinDriver as input, which means that any required configurations on the pin +/// can be done before adding it to the GpioWakeup struct. +#[derive(Debug)] +pub struct GpioWakeup<'a> { + pub pins: &'a [GpioWakeupPin<'a>], +} + +impl<'a> GpioWakeup<'a> { + pub fn new(pins: &'a [GpioWakeupPin<'a>]) -> Self { + Self { pins } + } +} + +impl<'a> GpioWakeup<'a> { + pub fn apply(&self) -> Result<(), EspError> { + for pin in self.pins.iter() { + let intr_level = match pin.wake_level { + Level::Low => gpio_int_type_t_GPIO_INTR_LOW_LEVEL, + Level::High => gpio_int_type_t_GPIO_INTR_HIGH_LEVEL, + }; + esp!(unsafe { gpio_wakeup_enable(pin.pin.pin(), intr_level) })?; + } + esp!(unsafe { esp_sleep_enable_gpio_wakeup() })?; + Ok(()) + } +} + +pub struct GpioWakeupPin<'a> { + pub pin: PinDriver<'a, AnyInputPin, Input>, + pub wake_level: Level, +} + +impl<'a> GpioWakeupPin<'a> { + pub fn new( + pin: PinDriver<'a, AnyInputPin, Input>, + wake_level: Level, + ) -> Result { + Ok(Self { pin, wake_level }) + } +} + +impl<'a> fmt::Debug for GpioWakeupPin<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "GpioWakeupPin {{ pin: {:?} wake_level: {:?} }}", + self.pin.pin(), + self.wake_level + ) + } +} + +/// Will wake up the CPU when the number of positive edges higher than a threshold +/// is detected on the UART RX pin. Is only available for light sleep. +pub struct UartWakeup<'a> { + pub uart: &'a UartDriver<'a>, + pub threshold: i32, +} + +impl<'a> UartWakeup<'a> { + pub fn new(uart: &'a UartDriver<'a>, threshold: i32) -> Self { + Self { uart, threshold } + } + + pub fn apply(&self) -> Result<(), EspError> { + esp!(unsafe { uart_set_wakeup_threshold(self.uart.port(), self.threshold) })?; + esp!(unsafe { esp_sleep_enable_uart_wakeup(self.uart.port()) })?; + Ok(()) + } +} + +impl<'a> fmt::Debug for UartWakeup<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UartWakeup") + } +} + +/// Will wake up the CPU when a touchpad is touched. +#[cfg(any(esp32, esp32s2, esp32s3))] +#[derive(Debug)] +pub struct TouchWakeup {} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl TouchWakeup { + pub fn apply(&self) -> Result<(), EspError> { + esp!(unsafe { esp_sleep_enable_touchpad_wakeup() })?; + Ok(()) + } +} + +/// Will let the ULP co-processor wake up the CPU. Requires that the ULP is started. +#[cfg(any(esp32, esp32s2, esp32s3))] +#[derive(Debug)] +pub struct UlpWakeup {} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl UlpWakeup { + pub fn apply(&self) -> Result<(), EspError> { + esp!(unsafe { esp_sleep_enable_ulp_wakeup() })?; + Ok(()) + } +} + +/// Struct for light sleep. Add wakeup sources to this struct, and then call sleep(). +#[derive(Debug, Default)] +pub struct LightSleep<'a> { + pub timer: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub rtc: Option>, + pub gpio: Option>, + pub uart: Option>, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub touch: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub ulp: Option, +} + +impl<'a> LightSleep<'a> { + pub fn sleep(&self) -> Result<(), EspError> { + esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; + + if let Some(timer) = &self.timer { + timer.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(rtc) = &self.rtc { + rtc.apply()?; + } + + if let Some(gpio) = &self.gpio { + gpio.apply()?; + } + + if let Some(uart) = &self.uart { + uart.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(touch) = &self.touch { + touch.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(ulp) = &self.ulp { + ulp.apply()?; + } + + esp!(unsafe { esp_light_sleep_start() })?; + Ok(()) + } +} + +/// Struct for deep sleep. Add wakeup sources to this struct, and then call sleep(). +#[derive(Debug, Default)] +pub struct DeepSleep<'a> { + pub timer: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub rtc: Option>, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub touch: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] + pub ulp: Option, + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + pub _p: PhantomData<&'a ()>, +} + +impl<'a> DeepSleep<'a> { + pub fn prepare(&self) -> Result<(), EspError> { + esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; + + if let Some(timer) = &self.timer { + timer.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(rtc) = &self.rtc { + rtc.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(touch) = &self.touch { + touch.apply()?; + } + + #[cfg(any(esp32, esp32s2, esp32s3))] + if let Some(ulp) = &self.ulp { + ulp.apply()?; + } + Ok(()) + } + + pub fn sleep(&self) -> ! { + unsafe { esp_deep_sleep_start() }; + + // Ensure that funciton never returns for esp idf version 5.x + #[cfg(esp_idf_version_major = "5")] + panic!(); + } + + pub fn prepare_and_sleep(&self) -> Result<(), EspError> { + self.prepare()?; + self.sleep(); + } +} From 914e35d2e1830a7b08f565a439798152d94d480f Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Wed, 6 Sep 2023 22:57:00 +0200 Subject: [PATCH 2/8] fixing nits will squash later --- src/sleep.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sleep.rs b/src/sleep.rs index bedb41549d6..652abb55ec9 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -6,10 +6,10 @@ //! code. //! //! Deep sleep is similar to light sleep, but the CPU is also powered down. Only RTC and -//! possibly ULP co processor is running. When woking up, the CPU does not keep its state, +//! possibly ULP coprocessor is running. When woking up, the CPU does not keep its state, //! and the program starts from the beginning. //! -//! When enter either light or deep sleep, one or more wakeup sources must be enabled. +//! When entering either light or deep sleep, one or more wakeup sources must be enabled. //! In this driver, the various wakeup sources are defined as different structs, which //! can be added to a light or deep sleep struct. This struct can then be used to perform //! a sleep operation with the given wakeup sources. @@ -37,11 +37,11 @@ pub struct TimerWakeup { } impl TimerWakeup { - pub fn new(duration: Duration) -> Self { + pub const fn new(duration: Duration) -> Self { Self { duration } } - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_enable_timer_wakeup(self.duration.as_micros() as u64) })?; Ok(()) } @@ -59,7 +59,7 @@ pub struct RtcWakeup<'a> { #[cfg(any(esp32, esp32s2, esp32s3))] impl<'a> RtcWakeup<'a> { - pub fn new(pins: &'a [RtcWakeupPin], wake_level: RtcWakeLevel) -> Self { + pub const fn new(pins: &'a [RtcWakeupPin], wake_level: RtcWakeLevel) -> Self { Self { pins, wake_level } } } @@ -74,7 +74,7 @@ impl<'a> RtcWakeup<'a> { m } - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { #[cfg(any(esp32, esp32s3))] for pin in self.pins { if pin.pullup || pin.pulldown { @@ -121,7 +121,7 @@ impl<'a> fmt::Debug for RtcWakeup<'a> { } #[cfg(any(esp32, esp32s2, esp32s3))] -#[derive(Debug)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum RtcWakeLevel { AllLow, AnyHigh, @@ -146,7 +146,7 @@ pub struct RtcWakeupPin { #[cfg(any(esp32, esp32s2, esp32s3))] impl RtcWakeupPin { - pub fn new(pin: AnyInputPin, pullup: bool, pulldown: bool) -> Self { + pub const fn new(pin: AnyInputPin, pullup: bool, pulldown: bool) -> Self { Self { pin, pullup, @@ -177,13 +177,13 @@ pub struct GpioWakeup<'a> { } impl<'a> GpioWakeup<'a> { - pub fn new(pins: &'a [GpioWakeupPin<'a>]) -> Self { + pub const fn new(pins: &'a [GpioWakeupPin<'a>]) -> Self { Self { pins } } } impl<'a> GpioWakeup<'a> { - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { for pin in self.pins.iter() { let intr_level = match pin.wake_level { Level::Low => gpio_int_type_t_GPIO_INTR_LOW_LEVEL, @@ -202,7 +202,7 @@ pub struct GpioWakeupPin<'a> { } impl<'a> GpioWakeupPin<'a> { - pub fn new( + pub const fn new( pin: PinDriver<'a, AnyInputPin, Input>, wake_level: Level, ) -> Result { @@ -229,11 +229,11 @@ pub struct UartWakeup<'a> { } impl<'a> UartWakeup<'a> { - pub fn new(uart: &'a UartDriver<'a>, threshold: i32) -> Self { + pub const fn new(uart: &'a UartDriver<'a>, threshold: i32) -> Self { Self { uart, threshold } } - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { esp!(unsafe { uart_set_wakeup_threshold(self.uart.port(), self.threshold) })?; esp!(unsafe { esp_sleep_enable_uart_wakeup(self.uart.port()) })?; Ok(()) @@ -249,11 +249,11 @@ impl<'a> fmt::Debug for UartWakeup<'a> { /// Will wake up the CPU when a touchpad is touched. #[cfg(any(esp32, esp32s2, esp32s3))] #[derive(Debug)] -pub struct TouchWakeup {} +pub struct TouchWakeup; #[cfg(any(esp32, esp32s2, esp32s3))] impl TouchWakeup { - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_enable_touchpad_wakeup() })?; Ok(()) } @@ -262,11 +262,11 @@ impl TouchWakeup { /// Will let the ULP co-processor wake up the CPU. Requires that the ULP is started. #[cfg(any(esp32, esp32s2, esp32s3))] #[derive(Debug)] -pub struct UlpWakeup {} +pub struct UlpWakeup; #[cfg(any(esp32, esp32s2, esp32s3))] impl UlpWakeup { - pub fn apply(&self) -> Result<(), EspError> { + fn apply(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_enable_ulp_wakeup() })?; Ok(()) } From f4694a4743f6228db7cbf9e85bfbcdb15b623add Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Thu, 7 Sep 2023 08:51:54 +0000 Subject: [PATCH 3/8] better description of deep sleep v4 and v5 differences --- src/sleep.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sleep.rs b/src/sleep.rs index 652abb55ec9..54873e3c79d 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -361,12 +361,17 @@ impl<'a> DeepSleep<'a> { Ok(()) } + // In esp idf version 5.x the esp_deep_sleep_start() function CAN return + // if deep sleep was rejected. So in order for this function to return ! + // it needs to panic. This cause unreachable code for v4.4 and earlier, + // thus the warning is disabled. + #[allow(unreachable_code)] pub fn sleep(&self) -> ! { - unsafe { esp_deep_sleep_start() }; - - // Ensure that funciton never returns for esp idf version 5.x - #[cfg(esp_idf_version_major = "5")] - panic!(); + unsafe { esp_deep_sleep_start() } + // Normally not reached, but if it is, then panic will restart the + // chip, which is similar to what would happen with a very short deep + // sleep. + panic!("Failed to deep sleep, will panic instead"); } pub fn prepare_and_sleep(&self) -> Result<(), EspError> { From f43ee1489e7dc5f369b1db0a8f4d8ef2626be25f Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Fri, 8 Sep 2023 07:19:58 +0000 Subject: [PATCH 4/8] update to chains of rtc/gpio pins --- examples/deep_sleep.rs | 46 ++-- examples/light_sleep.rs | 84 ++++-- src/sleep.rs | 586 +++++++++++++++++++++++++++++++++------- 3 files changed, 568 insertions(+), 148 deletions(-) diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs index 1710022e738..3f688d86896 100644 --- a/examples/deep_sleep.rs +++ b/examples/deep_sleep.rs @@ -8,6 +8,9 @@ //! the previous run. use core::time::Duration; +#[cfg(any(esp32, esp32s2, esp32s3))] +use esp_idf_hal::gpio::PinDriver; +#[cfg(any(esp32, esp32s2, esp32s3))] use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::reset::{ResetReason, WakeupReason}; use esp_idf_hal::sleep::*; @@ -31,24 +34,35 @@ fn main() -> anyhow::Result<()> { // RTC wakeup definitions #[cfg(esp32)] - let rtc_pins = [ - RtcWakeupPin::new(peripherals.pins.gpio4.into(), false, true), - RtcWakeupPin::new(peripherals.pins.gpio27.into(), false, true), - ]; + let rtc0 = PinDriver::rtc_input(peripherals.pins.gpio26)?; + #[cfg(esp32)] + let rtc1 = PinDriver::rtc_input(peripherals.pins.gpio27)?; + + #[cfg(any(esp32s2, esp32s3))] + let rtc0 = PinDriver::rtc_input(peripherals.pins.gpio1)?; #[cfg(any(esp32s2, esp32s3))] - let rtc_pins = [ - RtcWakeupPin::new(peripherals.pins.gpio1.into(), false, true), - RtcWakeupPin::new(peripherals.pins.gpio2.into(), false, true), - ]; + let rtc1 = PinDriver::rtc_input(peripherals.pins.gpio2)?; + + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_pin0 = RtcWakeupPin { pindriver: &rtc0 }; #[cfg(any(esp32, esp32s2, esp32s3))] - let rtc_wakeup = Some(RtcWakeup::new(&rtc_pins, RtcWakeLevel::AnyHigh)); - - let dsleep = DeepSleep { - timer: Some(TimerWakeup::new(Duration::from_secs(5))), - #[cfg(any(esp32, esp32s2, esp32s3))] - rtc: rtc_wakeup, - ..Default::default() - }; + let rtc_pin1 = RtcWakeupPin { pindriver: &rtc1 }; + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_wakeup = Some(RtcWakeup { + pins: EmptyRtcWakeupPins::chain(rtc_pin0).chain(rtc_pin1), + wake_level: RtcWakeLevel::AnyHigh, + }); + + #[cfg(any(esp32, esp32s2, esp32s3))] + let dsleep = make_deep_sleep_rtc_pins( + Some(TimerWakeup::new(Duration::from_secs(5))), + rtc_wakeup, + None, + None, + ); + + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + let dsleep = make_deep_sleep_no_pins(Some(TimerWakeup::new(Duration::from_secs(5)))); println!("Deep sleep with: {:?}", dsleep); dsleep.prepare()?; diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs index 414e5cf5c68..96631bd49a0 100644 --- a/examples/light_sleep.rs +++ b/examples/light_sleep.rs @@ -4,7 +4,7 @@ //! Prints wakeup reason and sleep time on wakeup. use core::time::Duration; -use esp_idf_hal::gpio::{self, AnyInputPin, PinDriver}; +use esp_idf_hal::gpio::{self, PinDriver}; use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::prelude::*; use esp_idf_hal::reset::WakeupReason; @@ -31,46 +31,61 @@ fn main() -> anyhow::Result<()> { esp_idf_sys::link_patches(); // run in a thread with increased stack size to prevent overflow - let builder = std::thread::Builder::new().stack_size(8 * 1024); + let builder = std::thread::Builder::new().stack_size(10 * 1024); let th = builder.spawn(move || -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); // RTC wakeup definitions #[cfg(esp32)] - let rtc_pins = [ - RtcWakeupPin::new(peripherals.pins.gpio26.into(), false, true), - RtcWakeupPin::new(peripherals.pins.gpio27.into(), false, true), - ]; + let rtc0 = PinDriver::rtc_input(peripherals.pins.gpio26)?; + #[cfg(esp32)] + let rtc1 = PinDriver::rtc_input(peripherals.pins.gpio27)?; + #[cfg(any(esp32s2, esp32s3))] - let rtc_pins = [ - RtcWakeupPin::new(peripherals.pins.gpio1.into(), false, true), - RtcWakeupPin::new(peripherals.pins.gpio2.into(), false, true), - ]; + let rtc0 = PinDriver::rtc_input(peripherals.pins.gpio1)?; + #[cfg(any(esp32s2, esp32s3))] + let rtc1 = PinDriver::rtc_input(peripherals.pins.gpio2)?; + #[cfg(any(esp32, esp32s2, esp32s3))] - let rtc_wakeup = Some(RtcWakeup::new(&rtc_pins, RtcWakeLevel::AnyHigh)); + let rtc_pin0 = RtcWakeupPin { pindriver: &rtc0 }; + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_pin1 = RtcWakeupPin { pindriver: &rtc1 }; + #[cfg(any(esp32, esp32s2, esp32s3))] + let rtc_wakeup = Some(RtcWakeup { + pins: EmptyRtcWakeupPins::chain(rtc_pin0).chain(rtc_pin1), + wake_level: RtcWakeLevel::AnyHigh, + }); // GPIO wakeup definitions #[cfg(esp32)] - let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio16))?; + let gpio0 = PinDriver::input(peripherals.pins.gpio12)?; #[cfg(esp32)] - let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio17))?; + let gpio1 = PinDriver::input(peripherals.pins.gpio14)?; #[cfg(any(esp32s2, esp32s3))] - let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio37))?; + let gpio0 = PinDriver::input(peripherals.pins.gpio37)?; #[cfg(any(esp32s2, esp32s3))] - let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio38))?; + let gpio1 = PinDriver::input(peripherals.pins.gpio38)?; #[cfg(esp32c3)] - let gpio_pin1 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio6))?; + let gpio0 = PinDriver::input(peripherals.pins.gpio6)?; #[cfg(esp32c3)] - let gpio_pin2 = PinDriver::input(AnyInputPin::from(peripherals.pins.gpio7))?; + let gpio1 = PinDriver::input(peripherals.pins.gpio7)?; - let gpio_pins = [ - GpioWakeupPin::new(gpio_pin1, Level::High)?, - GpioWakeupPin::new(gpio_pin2, Level::High)?, - ]; #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] - let gpio_wakeup = Some(GpioWakeup::new(&gpio_pins)); + let gpio_pin0 = GpioWakeupPin { + pindriver: &gpio0, + wake_level: Level::High, + }; + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + let gpio_pin1 = GpioWakeupPin { + pindriver: &gpio1, + wake_level: Level::High, + }; + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + let gpio_wakeup = Some(GpioWakeup { + pins: EmptyGpioWakeupPins::chain(gpio_pin0).chain(gpio_pin1), + }); #[cfg(not(any(esp32, esp32c3, esp32s2, esp32s3)))] let gpio_wakeup: Option = None; @@ -108,14 +123,22 @@ fn main() -> anyhow::Result<()> { #[cfg(not(any(esp32, esp32s2, esp32s3, esp32c3)))] let uart_wakeup: Option = None; - let lsleep = LightSleep { - timer: Some(TimerWakeup::new(Duration::from_secs(5))), - #[cfg(any(esp32, esp32s2, esp32s3))] - rtc: rtc_wakeup, - gpio: gpio_wakeup, - uart: uart_wakeup, - ..Default::default() - }; + #[cfg(any(esp32, esp32s2, esp32s3))] + let lsleep = make_light_sleep_rtc_gpio_pins( + Some(TimerWakeup::new(Duration::from_secs(5))), + rtc_wakeup, + gpio_wakeup, + uart_wakeup, + None, + None, + ); + + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + let lsleep = make_light_sleep_gpio_pins( + Some(TimerWakeup::new(Duration::from_secs(5))), + gpio_wakeup, + uart_wakeup, + ); loop { println!("Light sleep with: {:?}", lsleep); @@ -124,6 +147,7 @@ fn main() -> anyhow::Result<()> { let time_before = Instant::now(); let err = lsleep.sleep(); + match err { Ok(_) => print_wakeup_result(time_before), Err(e) => { diff --git a/src/sleep.rs b/src/sleep.rs index 54873e3c79d..352e930dc9e 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -20,14 +20,13 @@ //! use core::fmt; -#[cfg(not(any(esp32, esp32s2, esp32s3)))] use core::marker::PhantomData; use core::time::Duration; use esp_idf_sys::*; +use crate::gpio::{GPIOMode, InputPin, Level, PinDriver}; #[cfg(any(esp32, esp32s2, esp32s3))] -use crate::gpio::Pin; -use crate::gpio::{AnyInputPin, Input, Level, PinDriver}; +use crate::gpio::{RTCMode, RTCPin}; use crate::uart::UartDriver; /// Will wake the CPU up after a given duration @@ -52,64 +51,52 @@ impl TimerWakeup { /// Pullup and pulldown can be enabled for each pin. This settings will be applied when /// deep sleep is enabled. #[cfg(any(esp32, esp32s2, esp32s3))] -pub struct RtcWakeup<'a> { - pub pins: &'a [RtcWakeupPin], +pub struct RtcWakeup

+where + P: RtcWakeupPins, +{ + pub pins: P, pub wake_level: RtcWakeLevel, } #[cfg(any(esp32, esp32s2, esp32s3))] -impl<'a> RtcWakeup<'a> { - pub const fn new(pins: &'a [RtcWakeupPin], wake_level: RtcWakeLevel) -> Self { - Self { pins, wake_level } - } -} - -#[cfg(any(esp32, esp32s2, esp32s3))] -impl<'a> RtcWakeup<'a> { +impl

RtcWakeup

+where + P: RtcWakeupPins, +{ fn mask(&self) -> u64 { let mut m: u64 = 0; for pin in self.pins.iter() { - m |= 1 << pin.pin.pin(); + m |= 1 << pin; } m } fn apply(&self) -> Result<(), EspError> { #[cfg(any(esp32, esp32s3))] - for pin in self.pins { - if pin.pullup || pin.pulldown { - esp!(unsafe { - esp_sleep_pd_config( - esp_sleep_pd_domain_t_ESP_PD_DOMAIN_RTC_PERIPH, - esp_sleep_pd_option_t_ESP_PD_OPTION_ON, - ) - })?; - - if pin.pullup { - esp!(unsafe { rtc_gpio_pullup_en(pin.pin.pin()) })?; - } - if pin.pulldown { - esp!(unsafe { rtc_gpio_pulldown_en(pin.pin.pin()) })?; - } - } - } + esp!(unsafe { + esp_sleep_pd_config( + esp_sleep_pd_domain_t_ESP_PD_DOMAIN_RTC_PERIPH, + esp_sleep_pd_option_t_ESP_PD_OPTION_ON, + ) + })?; - if !self.pins.is_empty() { - /* Do not use ext0, as ext1 with one pin does the same */ - esp!(unsafe { esp_sleep_enable_ext1_wakeup(self.mask(), self.wake_level.mode()) })?; - } + esp!(unsafe { esp_sleep_enable_ext1_wakeup(self.mask(), self.wake_level.mode()) })?; Ok(()) } } #[cfg(any(esp32, esp32s2, esp32s3))] -impl<'a> fmt::Debug for RtcWakeup<'a> { +impl

fmt::Debug for RtcWakeup

+where + P: RtcWakeupPins, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "RtcWakeup {{ pins: [")?; for pin in self.pins.iter() { - write!(f, "{:?} ", pin.pin.pin())?; + write!(f, "{:?} ", pin)?; } write!( f, @@ -136,88 +123,321 @@ impl RtcWakeLevel { } } } +#[cfg(any(esp32, esp32s2, esp32s3))] +pub trait RtcPinDriver {} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl<'d, P, M> RtcPinDriver for PinDriver<'d, P, M> +where + P: RTCPin + InputPin + 'd, + M: RTCMode + 'd, +{ +} + +pub trait RtcWakeupPinTrait { + type PinDriver; + fn pin(&self) -> i32; +} + +pub struct DummyRtcWakeupPin; +impl RtcWakeupPinTrait for DummyRtcWakeupPin { + type PinDriver = DummyRtcWakeupPin; + fn pin(&self) -> i32 { + 0 + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub trait RtcWakeupPins { + type WakeupPin: RtcWakeupPinTrait; + type Iterator<'a>: Iterator + where + Self: 'a; + + fn iter(&self) -> Self::Iterator<'_>; +} +#[cfg(not(any(esp32, esp32s2, esp32s3)))] +pub trait RtcWakeupPins {} #[cfg(any(esp32, esp32s2, esp32s3))] -pub struct RtcWakeupPin { - pub pin: AnyInputPin, // this should use RtcInput somehow - pub pullup: bool, - pub pulldown: bool, +impl

RtcWakeupPins for P +where + P: RtcWakeupPinTrait, + P::PinDriver: RtcPinDriver, +{ + type WakeupPin = P; + + type Iterator<'a> = core::iter::Once where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + core::iter::once(self.pin()) + } } +pub struct EmptyRtcWakeupPins(PhantomData); + #[cfg(any(esp32, esp32s2, esp32s3))] -impl RtcWakeupPin { - pub const fn new(pin: AnyInputPin, pullup: bool, pulldown: bool) -> Self { - Self { - pin, - pullup, - pulldown, +impl EmptyRtcWakeupPins { + pub fn chain(other: O) -> ChainedRtcWakeupPins + where + A: RtcWakeupPinTrait, + O: RtcWakeupPins, + { + ChainedRtcWakeupPins { + first: Self(PhantomData), + second: other, } } } #[cfg(any(esp32, esp32s2, esp32s3))] -impl fmt::Debug for RtcWakeupPin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "RtcWakeupPin {{ pin: {:?} pullup {:?} pulldown {:?}}}", - self.pin.pin(), - self.pullup, - self.pulldown - ) +impl RtcWakeupPins for EmptyRtcWakeupPins +where + A: RtcWakeupPinTrait, +{ + type WakeupPin = A; + + type Iterator<'a> = core::iter::Empty where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + core::iter::empty() } } -/// Will wake up the CPU based on changes in GPIO pins. Is only available for light sleep. -/// It takes PinDriver as input, which means that any required configurations on the pin -/// can be done before adding it to the GpioWakeup struct. -#[derive(Debug)] -pub struct GpioWakeup<'a> { - pub pins: &'a [GpioWakeupPin<'a>], +#[cfg(not(any(esp32, esp32s2, esp32s3)))] +impl RtcWakeupPins for EmptyRtcWakeupPins {} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub struct ChainedRtcWakeupPins { + first: F, + second: S, } -impl<'a> GpioWakeup<'a> { - pub const fn new(pins: &'a [GpioWakeupPin<'a>]) -> Self { - Self { pins } +#[cfg(any(esp32, esp32s2, esp32s3))] +impl ChainedRtcWakeupPins { + pub fn chain(self, other: O) -> ChainedRtcWakeupPins + where + F: RtcWakeupPins, + S: RtcWakeupPins, + O: RtcWakeupPins, + { + ChainedRtcWakeupPins { + first: self, + second: other, + } } } -impl<'a> GpioWakeup<'a> { +#[cfg(any(esp32, esp32s2, esp32s3))] +impl RtcWakeupPins for ChainedRtcWakeupPins +where + F: RtcWakeupPins, + S: RtcWakeupPins, +{ + type WakeupPin = F::WakeupPin; + + type Iterator<'a> = core::iter::Chain, S::Iterator<'a>> where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + self.first.iter().chain(self.second.iter()) + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub struct RtcWakeupPin<'d, P, M> +where + P: InputPin + 'd, + M: RTCMode + 'd, +{ + pub pindriver: &'d PinDriver<'d, P, M>, +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +impl<'d, P, M> RtcWakeupPinTrait for RtcWakeupPin<'d, P, M> +where + P: InputPin + 'd, + M: RTCMode + 'd, +{ + type PinDriver = PinDriver<'d, P, M>; + + fn pin(&self) -> i32 { + self.pindriver.pin() + } +} + +/// Will wake up the CPU based on changes in GPIO pins. Is only available for light sleep. +/// It takes PinDriver as input, which means that any required configurations on the pin +/// can be done before adding it to the GpioWakeup struct. +pub struct GpioWakeup

+where + P: GpioWakeupPins, +{ + pub pins: P, +} + +impl

GpioWakeup

+where + P: GpioWakeupPins, +{ fn apply(&self) -> Result<(), EspError> { for pin in self.pins.iter() { - let intr_level = match pin.wake_level { + let intr_level = match pin.1 { Level::Low => gpio_int_type_t_GPIO_INTR_LOW_LEVEL, Level::High => gpio_int_type_t_GPIO_INTR_HIGH_LEVEL, }; - esp!(unsafe { gpio_wakeup_enable(pin.pin.pin(), intr_level) })?; + esp!(unsafe { gpio_wakeup_enable(pin.0, intr_level) })?; } esp!(unsafe { esp_sleep_enable_gpio_wakeup() })?; Ok(()) } } -pub struct GpioWakeupPin<'a> { - pub pin: PinDriver<'a, AnyInputPin, Input>, - pub wake_level: Level, +impl

fmt::Debug for GpioWakeup

+where + P: GpioWakeupPins, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "GpioWakeup {{ pins: [")?; + + for pin in self.pins.iter() { + write!(f, "({} {:?}), ", pin.0, pin.1)?; + } + write!(f, "") + } +} + +pub trait GpioPinDriver {} + +impl<'d, P, M> GpioPinDriver for PinDriver<'d, P, M> +where + P: InputPin + 'd, + M: GPIOMode + 'd, +{ } -impl<'a> GpioWakeupPin<'a> { - pub const fn new( - pin: PinDriver<'a, AnyInputPin, Input>, - wake_level: Level, - ) -> Result { - Ok(Self { pin, wake_level }) +pub trait GpioWakeupPinTrait { + type PinDriver; + fn pin(&self) -> i32; + fn wake_level(&self) -> Level; +} + +pub struct DummyGpioWakeupPin; +impl GpioWakeupPinTrait for DummyGpioWakeupPin { + type PinDriver = DummyGpioWakeupPin; + fn pin(&self) -> i32 { + 0 + } + fn wake_level(&self) -> Level { + Level::Low } } -impl<'a> fmt::Debug for GpioWakeupPin<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "GpioWakeupPin {{ pin: {:?} wake_level: {:?} }}", - self.pin.pin(), - self.wake_level - ) +pub trait GpioWakeupPins { + type WakeupPin: GpioWakeupPinTrait; + type Iterator<'a>: Iterator + where + Self: 'a; + + fn iter(&self) -> Self::Iterator<'_>; +} + +impl

GpioWakeupPins for P +where + P: GpioWakeupPinTrait, + P::PinDriver: GpioPinDriver, +{ + type WakeupPin = P; + + type Iterator<'a> = core::iter::Once<(i32, Level)> where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + core::iter::once((self.pin(), self.wake_level())) + } +} + +pub struct EmptyGpioWakeupPins(PhantomData); + +impl EmptyGpioWakeupPins { + pub fn chain(other: O) -> ChainedGpioWakeupPins + where + A: GpioWakeupPinTrait, + O: GpioWakeupPins, + { + ChainedGpioWakeupPins { + first: Self(PhantomData), + second: other, + } + } +} + +impl GpioWakeupPins for EmptyGpioWakeupPins +where + A: GpioWakeupPinTrait, +{ + type WakeupPin = A; + + type Iterator<'a> = core::iter::Empty<(i32, Level)> where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + core::iter::empty() + } +} + +pub struct ChainedGpioWakeupPins { + first: F, + second: S, +} + +impl ChainedGpioWakeupPins { + pub fn chain(self, other: O) -> ChainedGpioWakeupPins + where + F: GpioWakeupPins, + S: GpioWakeupPins, + O: GpioWakeupPins, + { + ChainedGpioWakeupPins { + first: self, + second: other, + } + } +} + +impl GpioWakeupPins for ChainedGpioWakeupPins +where + F: GpioWakeupPins, + S: GpioWakeupPins, +{ + type WakeupPin = F::WakeupPin; + + type Iterator<'a> = core::iter::Chain, S::Iterator<'a>> where Self: 'a; + + fn iter(&self) -> Self::Iterator<'_> { + self.first.iter().chain(self.second.iter()) + } +} + +pub struct GpioWakeupPin<'d, P, M> +where + P: InputPin + 'd, + M: GPIOMode + 'd, +{ + pub pindriver: &'d PinDriver<'d, P, M>, + pub wake_level: Level, +} + +impl<'d, P, M> GpioWakeupPinTrait for GpioWakeupPin<'d, P, M> +where + P: InputPin + 'd, + M: GPIOMode + 'd, +{ + type PinDriver = PinDriver<'d, P, M>; + + fn pin(&self) -> i32 { + self.pindriver.pin() + } + + fn wake_level(&self) -> Level { + self.wake_level } } @@ -273,20 +493,109 @@ impl UlpWakeup { } /// Struct for light sleep. Add wakeup sources to this struct, and then call sleep(). -#[derive(Debug, Default)] -pub struct LightSleep<'a> { - pub timer: Option, +pub struct LightSleep<'a, R, G> +where + R: RtcWakeupPins, + G: GpioWakeupPins, +{ + timer: Option, #[cfg(any(esp32, esp32s2, esp32s3))] - pub rtc: Option>, - pub gpio: Option>, - pub uart: Option>, + rtc: Option>, + gpio: Option>, + uart: Option>, #[cfg(any(esp32, esp32s2, esp32s3))] - pub touch: Option, + touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] - pub ulp: Option, + ulp: Option, + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + _p: PhantomData, +} + +pub fn make_light_sleep_no_pins( + timer: Option, + uart: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, +) -> LightSleep, EmptyGpioWakeupPins> { + LightSleep { + timer, + #[cfg(any(esp32, esp32s2, esp32s3))] + rtc: None, + gpio: None, + uart, + #[cfg(any(esp32, esp32s2, esp32s3))] + touch, + #[cfg(any(esp32, esp32s2, esp32s3))] + ulp, + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + _p: PhantomData, + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub fn make_light_sleep_rtc_pins( + timer: Option, + rtc: Option>, + uart: Option, + touch: Option, + ulp: Option, +) -> LightSleep> { + LightSleep { + timer, + rtc, + gpio: None, + uart, + touch, + ulp, + } +} + +pub fn make_light_sleep_gpio_pins( + timer: Option, + gpio: Option>, + uart: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, +) -> LightSleep, G> { + LightSleep { + timer, + #[cfg(any(esp32, esp32s2, esp32s3))] + rtc: None, + gpio, + uart, + #[cfg(any(esp32, esp32s2, esp32s3))] + touch, + #[cfg(any(esp32, esp32s2, esp32s3))] + ulp, + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + _p: PhantomData, + } +} + +#[cfg(any(esp32, esp32s2, esp32s3))] +pub fn make_light_sleep_rtc_gpio_pins( + timer: Option, + rtc: Option>, + gpio: Option>, + uart: Option, + touch: Option, + ulp: Option, +) -> LightSleep { + LightSleep { + timer, + rtc, + gpio, + uart, + touch, + ulp, + } } -impl<'a> LightSleep<'a> { +impl<'a, R, P> LightSleep<'a, R, P> +where + R: RtcWakeupPins, + P: GpioWakeupPins, +{ pub fn sleep(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; @@ -322,21 +631,78 @@ impl<'a> LightSleep<'a> { } } +impl<'a, R, P> fmt::Debug for LightSleep<'a, R, P> +where + R: RtcWakeupPins, + P: GpioWakeupPins, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LightSleep: {{timer: {:?}, ", self.timer)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "rtc: {:?}, ", self.rtc)?; + write!(f, "gpio: {:?}, ", self.gpio)?; + write!(f, "uart: {:?}, ", self.uart)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "touch: {:?}, ", self.touch)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "ulp: {:?}, ", self.ulp)?; + write!(f, "}}") + } +} + /// Struct for deep sleep. Add wakeup sources to this struct, and then call sleep(). -#[derive(Debug, Default)] -pub struct DeepSleep<'a> { +pub struct DeepSleep +where + R: RtcWakeupPins, +{ pub timer: Option, #[cfg(any(esp32, esp32s2, esp32s3))] - pub rtc: Option>, + pub rtc: Option>, #[cfg(any(esp32, esp32s2, esp32s3))] pub touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] pub ulp: Option, #[cfg(not(any(esp32, esp32s2, esp32s3)))] - pub _p: PhantomData<&'a ()>, + pub _p: PhantomData, +} + +pub fn make_deep_sleep_no_pins( + timer: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, + #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, +) -> DeepSleep> { + DeepSleep { + timer, + #[cfg(any(esp32, esp32s2, esp32s3))] + rtc: None, + #[cfg(any(esp32, esp32s2, esp32s3))] + touch, + #[cfg(any(esp32, esp32s2, esp32s3))] + ulp, + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + _p: PhantomData, + } } -impl<'a> DeepSleep<'a> { +#[cfg(any(esp32, esp32s2, esp32s3))] +pub fn make_deep_sleep_rtc_pins( + timer: Option, + rtc: Option>, + touch: Option, + ulp: Option, +) -> DeepSleep { + DeepSleep { + timer, + rtc, + touch, + ulp, + } +} + +impl DeepSleep +where + R: RtcWakeupPins, +{ pub fn prepare(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; @@ -365,12 +731,12 @@ impl<'a> DeepSleep<'a> { // if deep sleep was rejected. So in order for this function to return ! // it needs to panic. This cause unreachable code for v4.4 and earlier, // thus the warning is disabled. - #[allow(unreachable_code)] + #[allow(unreachable_code)] pub fn sleep(&self) -> ! { - unsafe { esp_deep_sleep_start() } + unsafe { esp_deep_sleep_start() } // Normally not reached, but if it is, then panic will restart the // chip, which is similar to what would happen with a very short deep - // sleep. + // sleep. panic!("Failed to deep sleep, will panic instead"); } @@ -379,3 +745,19 @@ impl<'a> DeepSleep<'a> { self.sleep(); } } + +impl fmt::Debug for DeepSleep +where + R: RtcWakeupPins, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DeepSleep: {{timer: {:?}, ", self.timer)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "rtc: {:?}, ", self.rtc)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "touch: {:?}, ", self.touch)?; + #[cfg(any(esp32, esp32s2, esp32s3))] + write!(f, "ulp: {:?}, ", self.ulp)?; + write!(f, "}}") + } +} From dc5d9564dddfd35c019f8a170f2308d34a622898 Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Thu, 14 Sep 2023 11:15:45 +0200 Subject: [PATCH 5/8] minor fixes --- examples/deep_sleep.rs | 3 ++- examples/light_sleep.rs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs index 3f688d86896..c3c65b073fe 100644 --- a/examples/deep_sleep.rs +++ b/examples/deep_sleep.rs @@ -1,7 +1,8 @@ //! Tests deep sleep //! //! Enables multiple deep sleep wakeup sources and then enter deep sleep. -//! There is no loop here, since the program will not continue after deep sleep. +//! There is no loop here, since the program will not continue after deep sleep, +//! it always starts from the beginning after a deep sleep wake-up. //! For ESP32c3, only timer wakeup is supported. //! The program starts by printing reset and wakeup reason, since the deep //! sleep effectively ends the program, this is how we get information about diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs index 96631bd49a0..07480ae8085 100644 --- a/examples/light_sleep.rs +++ b/examples/light_sleep.rs @@ -4,7 +4,7 @@ //! Prints wakeup reason and sleep time on wakeup. use core::time::Duration; -use esp_idf_hal::gpio::{self, PinDriver}; +use esp_idf_hal::gpio::{self, AnyIOPin, PinDriver}; use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::prelude::*; use esp_idf_hal::reset::WakeupReason; @@ -87,7 +87,7 @@ fn main() -> anyhow::Result<()> { pins: EmptyGpioWakeupPins::chain(gpio_pin0).chain(gpio_pin1), }); #[cfg(not(any(esp32, esp32c3, esp32s2, esp32s3)))] - let gpio_wakeup: Option = None; + let gpio_wakeup = None::; // UART definitions let config = Config::new().baudrate(Hertz(115_200)); @@ -96,8 +96,8 @@ fn main() -> anyhow::Result<()> { peripherals.uart0, peripherals.pins.gpio4, peripherals.pins.gpio3, - Option::::None, - Option::::None, + None::, + None::, &config, )?; #[cfg(any(esp32s2, esp32s3))] From 1bfd77b8fb79e935bda0d3c550584f9d56ba46ca Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Tue, 19 Sep 2023 18:55:50 +0000 Subject: [PATCH 6/8] use esp_idf_hal::sys::link_patches() --- examples/deep_sleep.rs | 2 +- examples/light_sleep.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs index c3c65b073fe..40d3f40ba8d 100644 --- a/examples/deep_sleep.rs +++ b/examples/deep_sleep.rs @@ -26,7 +26,7 @@ fn print_wakeup_result() { } fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); print_wakeup_result(); diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs index 07480ae8085..6073ef16510 100644 --- a/examples/light_sleep.rs +++ b/examples/light_sleep.rs @@ -28,7 +28,7 @@ fn print_wakeup_result(time_before: Instant) { } fn main() -> anyhow::Result<()> { - esp_idf_sys::link_patches(); + esp_idf_hal::sys::link_patches(); // run in a thread with increased stack size to prevent overflow let builder = std::thread::Builder::new().stack_size(10 * 1024); From 8ac288b95429312a91cbc38fb43034005b1aeadd Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Tue, 19 Sep 2023 20:52:37 +0200 Subject: [PATCH 7/8] Simplify pin chain scheme by removing unueccessary parts --- examples/light_sleep.rs | 4 +- src/sleep.rs | 105 ++++++++-------------------------------- 2 files changed, 22 insertions(+), 87 deletions(-) diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs index 6073ef16510..1a17ec4fc99 100644 --- a/examples/light_sleep.rs +++ b/examples/light_sleep.rs @@ -4,7 +4,9 @@ //! Prints wakeup reason and sleep time on wakeup. use core::time::Duration; -use esp_idf_hal::gpio::{self, AnyIOPin, PinDriver}; +#[cfg(any(esp32, esp32s2, esp32s3))] +use esp_idf_hal::gpio::AnyIOPin; +use esp_idf_hal::gpio::{self, PinDriver}; use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::prelude::*; use esp_idf_hal::reset::WakeupReason; diff --git a/src/sleep.rs b/src/sleep.rs index 352e930dc9e..aed19434825 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -20,7 +20,6 @@ //! use core::fmt; -use core::marker::PhantomData; use core::time::Duration; use esp_idf_sys::*; @@ -28,6 +27,8 @@ use crate::gpio::{GPIOMode, InputPin, Level, PinDriver}; #[cfg(any(esp32, esp32s2, esp32s3))] use crate::gpio::{RTCMode, RTCPin}; use crate::uart::UartDriver; +#[cfg(not(any(esp32, esp32s2, esp32s3)))] +use core::marker::PhantomData; /// Will wake the CPU up after a given duration #[derive(Debug)] @@ -123,33 +124,13 @@ impl RtcWakeLevel { } } } -#[cfg(any(esp32, esp32s2, esp32s3))] -pub trait RtcPinDriver {} - -#[cfg(any(esp32, esp32s2, esp32s3))] -impl<'d, P, M> RtcPinDriver for PinDriver<'d, P, M> -where - P: RTCPin + InputPin + 'd, - M: RTCMode + 'd, -{ -} pub trait RtcWakeupPinTrait { - type PinDriver; fn pin(&self) -> i32; } -pub struct DummyRtcWakeupPin; -impl RtcWakeupPinTrait for DummyRtcWakeupPin { - type PinDriver = DummyRtcWakeupPin; - fn pin(&self) -> i32 { - 0 - } -} - #[cfg(any(esp32, esp32s2, esp32s3))] pub trait RtcWakeupPins { - type WakeupPin: RtcWakeupPinTrait; type Iterator<'a>: Iterator where Self: 'a; @@ -163,10 +144,7 @@ pub trait RtcWakeupPins {} impl

RtcWakeupPins for P where P: RtcWakeupPinTrait, - P::PinDriver: RtcPinDriver, { - type WakeupPin = P; - type Iterator<'a> = core::iter::Once where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -174,29 +152,23 @@ where } } -pub struct EmptyRtcWakeupPins(PhantomData); +pub struct EmptyRtcWakeupPins; #[cfg(any(esp32, esp32s2, esp32s3))] -impl EmptyRtcWakeupPins { +impl EmptyRtcWakeupPins { pub fn chain(other: O) -> ChainedRtcWakeupPins where - A: RtcWakeupPinTrait, - O: RtcWakeupPins, + O: RtcWakeupPins, { ChainedRtcWakeupPins { - first: Self(PhantomData), + first: Self, second: other, } } } #[cfg(any(esp32, esp32s2, esp32s3))] -impl RtcWakeupPins for EmptyRtcWakeupPins -where - A: RtcWakeupPinTrait, -{ - type WakeupPin = A; - +impl RtcWakeupPins for EmptyRtcWakeupPins { type Iterator<'a> = core::iter::Empty where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -205,7 +177,7 @@ where } #[cfg(not(any(esp32, esp32s2, esp32s3)))] -impl RtcWakeupPins for EmptyRtcWakeupPins {} +impl RtcWakeupPins for EmptyRtcWakeupPins {} #[cfg(any(esp32, esp32s2, esp32s3))] pub struct ChainedRtcWakeupPins { @@ -234,8 +206,6 @@ where F: RtcWakeupPins, S: RtcWakeupPins, { - type WakeupPin = F::WakeupPin; - type Iterator<'a> = core::iter::Chain, S::Iterator<'a>> where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -246,7 +216,7 @@ where #[cfg(any(esp32, esp32s2, esp32s3))] pub struct RtcWakeupPin<'d, P, M> where - P: InputPin + 'd, + P: InputPin + RTCPin + 'd, M: RTCMode + 'd, { pub pindriver: &'d PinDriver<'d, P, M>, @@ -255,11 +225,9 @@ where #[cfg(any(esp32, esp32s2, esp32s3))] impl<'d, P, M> RtcWakeupPinTrait for RtcWakeupPin<'d, P, M> where - P: InputPin + 'd, + P: InputPin + RTCPin + 'd, M: RTCMode + 'd, { - type PinDriver = PinDriver<'d, P, M>; - fn pin(&self) -> i32 { self.pindriver.pin() } @@ -306,34 +274,12 @@ where } } -pub trait GpioPinDriver {} - -impl<'d, P, M> GpioPinDriver for PinDriver<'d, P, M> -where - P: InputPin + 'd, - M: GPIOMode + 'd, -{ -} - pub trait GpioWakeupPinTrait { - type PinDriver; fn pin(&self) -> i32; fn wake_level(&self) -> Level; } -pub struct DummyGpioWakeupPin; -impl GpioWakeupPinTrait for DummyGpioWakeupPin { - type PinDriver = DummyGpioWakeupPin; - fn pin(&self) -> i32 { - 0 - } - fn wake_level(&self) -> Level { - Level::Low - } -} - pub trait GpioWakeupPins { - type WakeupPin: GpioWakeupPinTrait; type Iterator<'a>: Iterator where Self: 'a; @@ -344,10 +290,7 @@ pub trait GpioWakeupPins { impl

GpioWakeupPins for P where P: GpioWakeupPinTrait, - P::PinDriver: GpioPinDriver, { - type WakeupPin = P; - type Iterator<'a> = core::iter::Once<(i32, Level)> where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -355,27 +298,21 @@ where } } -pub struct EmptyGpioWakeupPins(PhantomData); +pub struct EmptyGpioWakeupPins; -impl EmptyGpioWakeupPins { +impl EmptyGpioWakeupPins { pub fn chain(other: O) -> ChainedGpioWakeupPins where - A: GpioWakeupPinTrait, - O: GpioWakeupPins, + O: GpioWakeupPins, { ChainedGpioWakeupPins { - first: Self(PhantomData), + first: Self, second: other, } } } -impl GpioWakeupPins for EmptyGpioWakeupPins -where - A: GpioWakeupPinTrait, -{ - type WakeupPin = A; - +impl GpioWakeupPins for EmptyGpioWakeupPins { type Iterator<'a> = core::iter::Empty<(i32, Level)> where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -407,8 +344,6 @@ where F: GpioWakeupPins, S: GpioWakeupPins, { - type WakeupPin = F::WakeupPin; - type Iterator<'a> = core::iter::Chain, S::Iterator<'a>> where Self: 'a; fn iter(&self) -> Self::Iterator<'_> { @@ -430,8 +365,6 @@ where P: InputPin + 'd, M: GPIOMode + 'd, { - type PinDriver = PinDriver<'d, P, M>; - fn pin(&self) -> i32 { self.pindriver.pin() } @@ -516,7 +449,7 @@ pub fn make_light_sleep_no_pins( uart: Option, #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, -) -> LightSleep, EmptyGpioWakeupPins> { +) -> LightSleep { LightSleep { timer, #[cfg(any(esp32, esp32s2, esp32s3))] @@ -539,7 +472,7 @@ pub fn make_light_sleep_rtc_pins( uart: Option, touch: Option, ulp: Option, -) -> LightSleep> { +) -> LightSleep { LightSleep { timer, rtc, @@ -556,7 +489,7 @@ pub fn make_light_sleep_gpio_pins( uart: Option, #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, -) -> LightSleep, G> { +) -> LightSleep { LightSleep { timer, #[cfg(any(esp32, esp32s2, esp32s3))] @@ -670,7 +603,7 @@ pub fn make_deep_sleep_no_pins( timer: Option, #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, -) -> DeepSleep> { +) -> DeepSleep { DeepSleep { timer, #[cfg(any(esp32, esp32s2, esp32s3))] From 4f17dd5aaad31206e368c789a72ec02f21758199 Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Sun, 12 Nov 2023 13:25:12 +0100 Subject: [PATCH 8/8] sleep: adds gpio deep sleep for esp32c* targets --- examples/deep_sleep.rs | 31 ++++++++++--- src/sleep.rs | 98 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 12 deletions(-) diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs index 40d3f40ba8d..dff452f5ac6 100644 --- a/examples/deep_sleep.rs +++ b/examples/deep_sleep.rs @@ -9,9 +9,9 @@ //! the previous run. use core::time::Duration; -#[cfg(any(esp32, esp32s2, esp32s3))] +#[cfg(any(esp32c2, esp32c3))] +use esp_idf_hal::gpio::Level; use esp_idf_hal::gpio::PinDriver; -#[cfg(any(esp32, esp32s2, esp32s3))] use esp_idf_hal::peripherals::Peripherals; use esp_idf_hal::reset::{ResetReason, WakeupReason}; use esp_idf_hal::sleep::*; @@ -30,7 +30,6 @@ fn main() -> anyhow::Result<()> { print_wakeup_result(); - #[cfg(any(esp32, esp32s2, esp32s3))] let peripherals = Peripherals::take().unwrap(); // RTC wakeup definitions @@ -62,8 +61,30 @@ fn main() -> anyhow::Result<()> { None, ); - #[cfg(not(any(esp32, esp32s2, esp32s3)))] - let dsleep = make_deep_sleep_no_pins(Some(TimerWakeup::new(Duration::from_secs(5)))); + // GPIO wakeup definitions + #[cfg(any(esp32c2, esp32c3))] + let gpio0 = PinDriver::input(peripherals.pins.gpio0)?; + #[cfg(any(esp32c2, esp32c3))] + let gpio1 = PinDriver::input(peripherals.pins.gpio1)?; + + #[cfg(any(esp32c2, esp32c3))] + let gpio_pin0 = GpioWakeupPin { + pindriver: &gpio0, + wake_level: Level::High, + }; + #[cfg(any(esp32c2, esp32c3))] + let gpio_pin1 = GpioWakeupPin { + pindriver: &gpio1, + wake_level: Level::Low, + }; + #[cfg(any(esp32c2, esp32c3))] + let gpio_wakeup = Some(GpioDeepWakeup { + pins: EmptyGpioWakeupPins::chain(gpio_pin0).chain(gpio_pin1), + }); + + #[cfg(any(esp32c2, esp32c3))] + let dsleep = + make_deep_sleep_gpio_pins(Some(TimerWakeup::new(Duration::from_secs(5))), gpio_wakeup); println!("Deep sleep with: {:?}", dsleep); dsleep.prepare()?; diff --git a/src/sleep.rs b/src/sleep.rs index aed19434825..bd3a4830d1a 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -27,7 +27,6 @@ use crate::gpio::{GPIOMode, InputPin, Level, PinDriver}; #[cfg(any(esp32, esp32s2, esp32s3))] use crate::gpio::{RTCMode, RTCPin}; use crate::uart::UartDriver; -#[cfg(not(any(esp32, esp32s2, esp32s3)))] use core::marker::PhantomData; /// Will wake the CPU up after a given duration @@ -274,6 +273,63 @@ where } } +#[cfg(any(esp32c2, esp32c3))] +pub struct GpioDeepWakeup

+where + P: GpioWakeupPins, +{ + pub pins: P, +} + +#[cfg(any(esp32c2, esp32c3))] +impl

GpioDeepWakeup

+where + P: GpioWakeupPins, +{ + fn mask(&self, level: Level) -> u64 { + let mut m: u64 = 0; + for pin in self.pins.iter() { + if pin.1 == level { + m |= 1 << pin.0; + } + } + m + } + + fn apply(&self) -> Result<(), EspError> { + esp!(unsafe { + esp_deep_sleep_enable_gpio_wakeup( + self.mask(Level::Low), + esp_deepsleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_LOW, + ) + })?; + + esp!(unsafe { + esp_deep_sleep_enable_gpio_wakeup( + self.mask(Level::High), + esp_deepsleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_HIGH, + ) + })?; + + Ok(()) + } +} + +#[cfg(any(esp32c2, esp32c3))] +impl

fmt::Debug for GpioDeepWakeup

+where + P: GpioWakeupPins, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "GpioDeepWakeup {{ pins: [")?; + + for pin in self.pins.iter() { + write!(f, "({} {:?}), ", pin.0, pin.1)?; + } + write!(f, "") + } +} + pub trait GpioWakeupPinTrait { fn pin(&self) -> i32; fn wake_level(&self) -> Level; @@ -584,35 +640,41 @@ where } /// Struct for deep sleep. Add wakeup sources to this struct, and then call sleep(). -pub struct DeepSleep +pub struct DeepSleep where R: RtcWakeupPins, + P: GpioWakeupPins, { pub timer: Option, #[cfg(any(esp32, esp32s2, esp32s3))] pub rtc: Option>, + #[cfg(any(esp32c2, esp32c3))] + pub gpio: Option>, #[cfg(any(esp32, esp32s2, esp32s3))] pub touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] pub ulp: Option, #[cfg(not(any(esp32, esp32s2, esp32s3)))] pub _p: PhantomData, + #[cfg(not(any(esp32c2, esp32c3)))] + pub _p: PhantomData

, } pub fn make_deep_sleep_no_pins( timer: Option, #[cfg(any(esp32, esp32s2, esp32s3))] touch: Option, #[cfg(any(esp32, esp32s2, esp32s3))] ulp: Option, -) -> DeepSleep { +) -> DeepSleep { DeepSleep { timer, #[cfg(any(esp32, esp32s2, esp32s3))] rtc: None, + #[cfg(any(esp32c2, esp32c3))] + gpio: None, #[cfg(any(esp32, esp32s2, esp32s3))] touch, #[cfg(any(esp32, esp32s2, esp32s3))] ulp, - #[cfg(not(any(esp32, esp32s2, esp32s3)))] _p: PhantomData, } } @@ -623,18 +685,32 @@ pub fn make_deep_sleep_rtc_pins( rtc: Option>, touch: Option, ulp: Option, -) -> DeepSleep { +) -> DeepSleep { DeepSleep { timer, rtc, touch, ulp, + _p: PhantomData, } } -impl DeepSleep +#[cfg(any(esp32c2, esp32c3))] +pub fn make_deep_sleep_gpio_pins( + timer: Option, + gpio: Option>, +) -> DeepSleep { + DeepSleep { + timer, + gpio, + _p: PhantomData, + } +} + +impl DeepSleep where R: RtcWakeupPins, + P: GpioWakeupPins, { pub fn prepare(&self) -> Result<(), EspError> { esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; @@ -648,6 +724,11 @@ where rtc.apply()?; } + #[cfg(any(esp32c2, esp32c3))] + if let Some(gpio) = &self.gpio { + gpio.apply()?; + } + #[cfg(any(esp32, esp32s2, esp32s3))] if let Some(touch) = &self.touch { touch.apply()?; @@ -679,14 +760,17 @@ where } } -impl fmt::Debug for DeepSleep +impl fmt::Debug for DeepSleep where R: RtcWakeupPins, + P: GpioWakeupPins, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DeepSleep: {{timer: {:?}, ", self.timer)?; #[cfg(any(esp32, esp32s2, esp32s3))] write!(f, "rtc: {:?}, ", self.rtc)?; + #[cfg(any(esp32c2, esp32c3))] + write!(f, "gpio: {:?}, ", self.gpio)?; #[cfg(any(esp32, esp32s2, esp32s3))] write!(f, "touch: {:?}, ", self.touch)?; #[cfg(any(esp32, esp32s2, esp32s3))]