Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3: GPIO wakeup source with RTC_IO pins #690

Merged
merged 5 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

### Changed

Expand Down
6 changes: 3 additions & 3 deletions esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand Down Expand Up @@ -65,7 +65,7 @@ impl WakeSource for TimerWakeupSource {
}
}

impl<P: Pin + RTCPin> WakeSource for Ext0WakeupSource<'_, P> {
impl<P: RTCPin> 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);
Expand All @@ -91,7 +91,7 @@ impl<P: Pin + RTCPin> WakeSource for Ext0WakeupSource<'_, P> {
}
}

impl<P: Pin + RTCPin> Drop for Ext0WakeupSource<'_, P> {
impl<P: RTCPin> 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
Expand Down
60 changes: 56 additions & 4 deletions esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -110,7 +110,7 @@ impl WakeSource for TimerWakeupSource {
}
}

impl<P: Pin + RTCPin> WakeSource for Ext0WakeupSource<'_, P> {
impl<P: RTCPin> 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);
Expand All @@ -136,7 +136,7 @@ impl<P: Pin + RTCPin> WakeSource for Ext0WakeupSource<'_, P> {
}
}

impl<P: Pin + RTCPin> Drop for Ext0WakeupSource<'_, P> {
impl<P: RTCPin> 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
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 21 additions & 6 deletions esp-hal-common/src/rtc_cntl/sleep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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),
Expand All @@ -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);
Expand Down
79 changes: 79 additions & 0 deletions esp32s3-hal/examples/sleep_timer_rtcio.rs
Original file line number Diff line number Diff line change
@@ -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);
}