Skip to content

Commit

Permalink
Implement gpio wakeup
Browse files Browse the repository at this point in the history
  • Loading branch information
bugadani committed Jul 25, 2023
1 parent 498cb3c commit bd0ecdc
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/board/hardware/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl super::startup::StartupResources {
)
}),
clocks,
rtc,
peripheral_clock_control: system.peripheral_clock_control,

misc_pins: MiscPins {
Expand Down
3 changes: 2 additions & 1 deletion src/board/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::board::{
prelude::*,
spi::{dma::WithDmaSpi3, SpiMode},
system::PeripheralClockControl,
Spi,
Rtc, Spi,
},
utils::{DummyOutputPin, SpiDeviceWrapper},
wifi::WifiDriver,
Expand All @@ -39,6 +39,7 @@ pub struct StartupResources {

pub misc_pins: MiscPins,
pub wifi: &'static mut WifiDriver,
pub rtc: Rtc<'static>,
}

impl StartupResources {
Expand Down
24 changes: 15 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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")]
Expand All @@ -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.

Expand All @@ -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.
Expand Down
117 changes: 81 additions & 36 deletions src/sleep.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,95 @@
#![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<MODE, const PIN: u8>(_pin: &GpioPin<MODE, PIN>, level: RtcioWakeupType) {
impl From<WakeupLevel> 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 };

pin.rtc_set_config(true, true, RtcFunction::Rtc);

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<MODE, const PIN: u8>(_pin: &GpioPin<MODE, PIN>, level: RtcioWakeupType) {
let sens = unsafe { &*pac::SENS::PTR };

// TODO: disable clock when not in use
Expand All @@ -28,36 +106,3 @@ pub fn enable_gpio_wakeup<MODE, const PIN: u8>(_pin: &GpioPin<MODE, PIN>, level:
pub fn disable_gpio_wakeup<MODE, const PIN: u8>(pin: &GpioPin<MODE, PIN>) {
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());
}

0 comments on commit bd0ecdc

Please sign in to comment.