From 80871a9eebed46639a9be525b70bb05a6c047154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 25 Jul 2023 16:56:15 +0200 Subject: [PATCH] Implement nonfunctional gpio wakeup --- src/board/hardware/v2.rs | 1 + src/board/startup.rs | 3 +- src/main.rs | 24 +++++--- src/sleep.rs | 115 +++++++++++++++++++++++++++------------ 4 files changed, 97 insertions(+), 46 deletions(-) diff --git a/src/board/hardware/v2.rs b/src/board/hardware/v2.rs index cdcb852c..d19aafd5 100644 --- a/src/board/hardware/v2.rs +++ b/src/board/hardware/v2.rs @@ -214,6 +214,7 @@ impl super::startup::StartupResources { ) }), clocks, + rtc, peripheral_clock_control: system.peripheral_clock_control, misc_pins: MiscPins { diff --git a/src/board/startup.rs b/src/board/startup.rs index 0d7468b9..02c19e4c 100644 --- a/src/board/startup.rs +++ b/src/board/startup.rs @@ -15,7 +15,7 @@ use crate::board::{ prelude::*, spi::{dma::WithDmaSpi3, SpiMode}, system::PeripheralClockControl, - Spi, + Rtc, Spi, }, utils::{DummyOutputPin, SpiDeviceWrapper}, wifi::WifiDriver, @@ -39,6 +39,7 @@ pub struct StartupResources { pub misc_pins: MiscPins, pub wifi: &'static mut WifiDriver, + pub rtc: Rtc<'static>, } impl StartupResources { diff --git a/src/main.rs b/src/main.rs index 5d5b2a01..af7e7b70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,13 +35,13 @@ use crate::{ use crate::{ board::{ config::{Config, ConfigFile}, - hal::{self, entry, prelude::interrupt}, + hal::{self, entry, gpio::RTCPin, prelude::interrupt, rtc_cntl::sleep::WakeupLevel, Delay}, initialized::{BatteryMonitor, BatteryState, Board, ConfigPartition}, startup::StartupResources, BATTERY_MODEL, }, interrupt::{InterruptExecutor, SwInterrupt0}, - sleep::{enable_gpio_wakeup, start_deep_sleep, RtcioWakeupType}, + sleep::RtcioWakeupSource, states::{ adc_setup, app_error, charging, display_menu, initialize, main_menu, measure, wifi_ap, }, @@ -264,15 +264,18 @@ async fn main_task(spawner: Spawner, resources: StartupResources) { Timer::after(Duration::from_millis(100)).await; let is_charging = board.battery_monitor.is_plugged(); - let (_, _, _, touch) = board.frontend.split(); + let (_, _, _, mut touch) = board.frontend.split(); + let mut rtc = resources.rtc; #[cfg(feature = "hw_v1")] let mut charger_pin = board.battery_monitor.charger_status; #[cfg(feature = "hw_v2")] - let charger_pin = board.battery_monitor.vbus_detect; + let mut charger_pin = board.battery_monitor.vbus_detect; - enable_gpio_wakeup(&touch, RtcioWakeupType::LowLevel); + let mut wakeup_pins = heapless::Vec::<(&mut dyn RTCPin, WakeupLevel), 2>::new(); + + wakeup_pins.push((&mut touch, WakeupLevel::Low)).ok(); if is_charging { #[cfg(feature = "hw_v1")] @@ -284,7 +287,7 @@ async fn main_task(spawner: Spawner, resources: StartupResources) { // Wake up momentarily when charger is disconnected #[cfg(feature = "hw_v2")] - enable_gpio_wakeup(&charger_pin, RtcioWakeupType::LowLevel); + wakeup_pins.push((&mut charger_pin, WakeupLevel::Low)).ok(); } else { // We want to wake up when the charger is connected, or the electrodes are touched. @@ -295,16 +298,19 @@ async fn main_task(spawner: Spawner, resources: StartupResources) { { charger_pin.rtcio_pad_hold(true); charger_pin.rtcio_pullup(true); - enable_gpio_wakeup(&charger_pin, RtcioWakeupType::LowLevel); + + wakeup_pins.push((&mut charger_pin, WakeupLevel::Low)).ok(); } // In v2, the charger status is not connected to an RTC IO pin, so we use the VBUS // detect pin instead. This is a high level signal when the charger is connected. #[cfg(feature = "hw_v2")] - enable_gpio_wakeup(&charger_pin, RtcioWakeupType::HighLevel); + wakeup_pins.push((&mut charger_pin, WakeupLevel::High)).ok(); } - start_deep_sleep(); + let rtcio = RtcioWakeupSource::new(&mut wakeup_pins); + + rtc.sleep_deep(&[&rtcio], &mut Delay::new(&board.clocks)); // Shouldn't reach this. If we do, we just exit the task, which means the executor // will have nothing else to do. Not ideal, but again, we shouldn't reach this. diff --git a/src/sleep.rs b/src/sleep.rs index e4c2cf4f..35b78696 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -1,17 +1,93 @@ #![allow(unused)] +use core::cell::RefCell; + +use esp32s3_hal::{rtc_cntl::sleep::RtcSleepConfig, Rtc}; + use crate::board::{ - hal::gpio::{GpioPin, RTCPin}, + hal::{ + gpio::{GpioPin, RTCPin, RtcFunction}, + rtc_cntl::sleep::{WakeSource, WakeTriggers, WakeupLevel}, + }, pac, }; -pub enum RtcioWakeupType { +enum RtcioWakeupType { Disable = 0, LowLevel = 4, HighLevel = 5, } -pub fn enable_gpio_wakeup(_pin: &GpioPin, level: RtcioWakeupType) { +impl From for RtcioWakeupType { + fn from(value: WakeupLevel) -> Self { + match value { + WakeupLevel::Low => Self::LowLevel, + WakeupLevel::High => Self::HighLevel, + } + } +} + +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), + } + } + + fn apply_pin(&self, pin: &mut dyn RTCPin, level: WakeupLevel) { + let rtcio = unsafe { &*pac::RTC_IO::PTR }; + + rtcio.pin[pin.number() as usize].modify(|_, w| { + w.wakeup_enable() + .set_bit() + .int_type() + .variant(RtcioWakeupType::from(level) as u8) + }); + } +} + +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 { &*pac::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); + } + } +} + +fn enable_gpio_wakeup(_pin: &GpioPin, level: RtcioWakeupType) { let sens = unsafe { &*pac::SENS::PTR }; // TODO: disable clock when not in use @@ -28,36 +104,3 @@ pub fn enable_gpio_wakeup(_pin: &GpioPin, level: pub fn disable_gpio_wakeup(pin: &GpioPin) { enable_gpio_wakeup(pin, RtcioWakeupType::Disable) } - -// Assumptions: S3, Quad Flash/PSRAM, 2nd core stopped -pub fn start_deep_sleep() { - // TODO: S2: disable brownout detector - // TODO: flush log buffers? - - let rtc_ctrl = unsafe { &*pac::RTC_CNTL::PTR }; - - rtc_ctrl.dig_pwc.modify(|_, w| w.dg_wrap_pd_en().set_bit()); - rtc_ctrl - .sdio_conf - .modify(|_, w| w.sdio_reg_pd_en().set_bit()); - - // Enter Deep Sleep - const WAKEUP_SOURCE_GPIO: u32 = 0x4; - rtc_ctrl - .wakeup_state - .modify(|_, w| w.wakeup_ena().variant(WAKEUP_SOURCE_GPIO)); - - rtc_ctrl.int_clr_rtc.write(|w| { - w.slp_reject_int_clr() - .set_bit() - .slp_wakeup_int_clr() - .set_bit() - }); - - rtc_ctrl - .int_clr_rtc - .write(|w| unsafe { w.bits(0x003F_FFFF) }); - - /* Start entry into sleep mode */ - rtc_ctrl.state0.modify(|_, w| w.sleep_en().set_bit()); -}