feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+1226
File diff suppressed because it is too large.
Load diff
+74
@@ -0,0 +1,74 @@
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
peripherals::RTC_CNTL,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
pub(crate) fn init() {}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRtc);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::ptr();
|
||||
rtc_cntl.store1().write(|w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
/// SOC Reset Reason.
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// SDIO module resets the digital core
|
||||
CoreSdio = 0x06,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0Mwdt1` and `Cpu1Mwdt1`, however they have the
|
||||
/// same values.
|
||||
CpuMwdt0 = 0x0B,
|
||||
/// Software resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0Sw` and `Cpu1Sw`, however they have the same
|
||||
/// values.
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0RtcWdt` and `Cpu1RtcWdt`, however they have
|
||||
/// the same values.
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// CPU0 resets CPU1 by DPORT_APPCPU_RESETTING
|
||||
Cpu1Cpu0 = 0x0E,
|
||||
/// Reset when the VDD voltage is not stable
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM},
|
||||
regi2c_write_mask,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u32 = 0x6d;
|
||||
const I2C_DIG_REG_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_ULP: u32 = 0x61;
|
||||
const I2C_ULP_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3;
|
||||
|
||||
const I2C_ULP_IR_FORCE_XPD_CK: u32 = 0;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_MSB: u32 = 2;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2;
|
||||
|
||||
pub(crate) fn init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.timer1()
|
||||
.modify(|_, w| w.pll_buf_wait().bits(20u8).ck8m_wait().bits(20u8));
|
||||
|
||||
rtc_cntl.timer5().modify(|_, w| w.min_slp_val().bits(2u8));
|
||||
}
|
||||
|
||||
calibrate_ocode();
|
||||
|
||||
set_rtc_dig_dbias();
|
||||
|
||||
clock_control_init();
|
||||
|
||||
power_control_init();
|
||||
|
||||
unsafe {
|
||||
rtc_cntl.int_ena().write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
unsafe {
|
||||
// from esp_clk_init:
|
||||
let rtc_cntl = &*esp32c2::RTC_CNTL::ptr();
|
||||
// clk_ll_rc_fast_enable();
|
||||
rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().clear_bit());
|
||||
rtc_cntl.timer1().modify(|_, w| w.ck8m_wait().bits(5));
|
||||
// esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
|
||||
crate::rom::ets_delay_us(50);
|
||||
}
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRtc);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::ptr();
|
||||
rtc_cntl.store1().write(|w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
fn calibrate_ocode() {}
|
||||
|
||||
fn set_rtc_dig_dbias() {}
|
||||
|
||||
/// Perform clock control related initialization
|
||||
fn clock_control_init() {
|
||||
let extmem = unsafe { &*EXTMEM::ptr() };
|
||||
let spi_mem_0 = unsafe { &*SPI0::ptr() };
|
||||
let spi_mem_1 = unsafe { &*SPI1::ptr() };
|
||||
|
||||
// Clear CMMU clock force on
|
||||
extmem
|
||||
.cache_mmu_power_ctrl()
|
||||
.modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
|
||||
|
||||
// Clear tag clock force on
|
||||
extmem
|
||||
.icache_tag_power_ctrl()
|
||||
.modify(|_, w| w.icache_tag_mem_force_on().clear_bit());
|
||||
|
||||
// Clear register clock force on
|
||||
spi_mem_0.clock_gate().modify(|_, w| w.clk_en().clear_bit());
|
||||
spi_mem_1.clock_gate().modify(|_, w| w.clk_en().clear_bit());
|
||||
}
|
||||
|
||||
/// Perform power control related initialization
|
||||
fn power_control_init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
let system = unsafe { &*SYSTEM::ptr() };
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().clear_bit());
|
||||
|
||||
// Cancel XTAL force PU if no need to force power up
|
||||
// Cannot cancel XTAL force PU if PLL is force power on
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().clear_bit());
|
||||
|
||||
// Force PD APLL
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.plla_force_pu()
|
||||
.clear_bit()
|
||||
.plla_force_pd()
|
||||
.set_bit()
|
||||
// Open SAR_I2C protect function to avoid SAR_I2C
|
||||
// Reset when rtc_ldo is low.
|
||||
.i2c_reset_por_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// Cancel BBPLL force PU if setting no force power up
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bbpll_force_pu()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pu()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.rtc_cntl()
|
||||
.modify(|_, w| w.regulator_force_pu().clear_bit());
|
||||
|
||||
// If this mask is enabled, all soc memories cannot enter power down mode.
|
||||
// We should control soc memory power down mode from RTC,
|
||||
// so we will not touch this register any more.
|
||||
system
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
rtc_sleep_pu();
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.dg_wrap_force_noiso().clear_bit());
|
||||
|
||||
// Cancel digital PADS force no iso
|
||||
system
|
||||
.cpu_per_conf()
|
||||
.modify(|_, w| w.cpu_wait_mode_force_on().clear_bit());
|
||||
|
||||
// If SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0,
|
||||
// the CPU clock will be closed when CPU enter WAITI mode.
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Configure whether certain peripherals are powered down in deep sleep
|
||||
fn rtc_sleep_pu() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
let apb_ctrl = unsafe { &*APB_CTRL::ptr() };
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().clear_bit());
|
||||
|
||||
apb_ctrl.front_end_mem_pd().modify(|_, w| {
|
||||
w.dc_mem_force_pu()
|
||||
.clear_bit()
|
||||
.pbus_mem_force_pu()
|
||||
.clear_bit()
|
||||
.agc_mem_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
apb_ctrl
|
||||
.mem_power_up()
|
||||
.modify(|_, w| unsafe { w.sram_power_up().bits(0u8).rom_power_up().bits(0u8) });
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
/// SOC Reset Reason.
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU 0
|
||||
Cpu0Mwdt0 = 0x0B,
|
||||
/// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU 0
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// Glitch on clock resets the digital core and rtc module
|
||||
SysClkGlitch = 0x13,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
/// JTAG resets the CPU 0
|
||||
Cpu0Jtag = 0x18,
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
clock::XtalClock,
|
||||
peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM},
|
||||
regi2c_write_mask,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u32 = 0x6d;
|
||||
const I2C_DIG_REG_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_ULP: u32 = 0x61;
|
||||
const I2C_ULP_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3;
|
||||
|
||||
const I2C_ULP_IR_FORCE_XPD_CK: u32 = 0;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_MSB: u32 = 2;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2;
|
||||
|
||||
pub(crate) fn init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| w.pvtmon_pu().clear_bit());
|
||||
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.timer1()
|
||||
.modify(|_, w| w.pll_buf_wait().bits(20u8).ck8m_wait().bits(20u8));
|
||||
rtc_cntl.timer5().modify(|_, w| w.min_slp_val().bits(2u8));
|
||||
|
||||
// Set default powerup & wait time
|
||||
rtc_cntl.timer3().modify(|_, w| {
|
||||
w.wifi_powerup_timer()
|
||||
.bits(1u8)
|
||||
.wifi_wait_timer()
|
||||
.bits(1u16)
|
||||
.bt_powerup_timer()
|
||||
.bits(1u8)
|
||||
.bt_wait_timer()
|
||||
.bits(1u16)
|
||||
});
|
||||
rtc_cntl.timer4().modify(|_, w| {
|
||||
w.cpu_top_powerup_timer()
|
||||
.bits(1u8)
|
||||
.cpu_top_wait_timer()
|
||||
.bits(1u16)
|
||||
.dg_wrap_powerup_timer()
|
||||
.bits(1u8)
|
||||
.dg_wrap_wait_timer()
|
||||
.bits(1u16)
|
||||
});
|
||||
rtc_cntl.timer6().modify(|_, w| {
|
||||
w.dg_peri_powerup_timer()
|
||||
.bits(1u8)
|
||||
.dg_peri_wait_timer()
|
||||
.bits(1u16)
|
||||
});
|
||||
}
|
||||
|
||||
calibrate_ocode();
|
||||
|
||||
set_rtc_dig_dbias();
|
||||
|
||||
clock_control_init();
|
||||
|
||||
power_control_init();
|
||||
|
||||
unsafe {
|
||||
rtc_cntl.int_ena().write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M));
|
||||
|
||||
unsafe {
|
||||
// from esp_clk_init:
|
||||
let rtc_cntl = &*esp32c3::RTC_CNTL::ptr();
|
||||
// clk_ll_rc_fast_enable();
|
||||
rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().clear_bit());
|
||||
rtc_cntl.timer1().modify(|_, w| w.ck8m_wait().bits(5));
|
||||
// esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
|
||||
crate::rom::ets_delay_us(50);
|
||||
}
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRtc);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::ptr();
|
||||
rtc_cntl.store1().write(|w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
fn calibrate_ocode() {}
|
||||
|
||||
fn set_rtc_dig_dbias() {}
|
||||
|
||||
/// Perform clock control related initialization
|
||||
fn clock_control_init() {
|
||||
let extmem = unsafe { &*EXTMEM::ptr() };
|
||||
let spi_mem_0 = unsafe { &*SPI0::ptr() };
|
||||
let spi_mem_1 = unsafe { &*SPI1::ptr() };
|
||||
|
||||
// Clear CMMU clock force on
|
||||
extmem
|
||||
.cache_mmu_power_ctrl()
|
||||
.modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
|
||||
|
||||
// Clear tag clock force on
|
||||
extmem
|
||||
.icache_tag_power_ctrl()
|
||||
.modify(|_, w| w.icache_tag_mem_force_on().clear_bit());
|
||||
|
||||
// Clear register clock force on
|
||||
spi_mem_0.clock_gate().modify(|_, w| w.clk_en().clear_bit());
|
||||
spi_mem_1.clock_gate().modify(|_, w| w.clk_en().clear_bit());
|
||||
}
|
||||
|
||||
/// Perform power control related initialization
|
||||
fn power_control_init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
let system = unsafe { &*SYSTEM::ptr() };
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().clear_bit());
|
||||
|
||||
// Cancel XTAL force PU if no need to force power up
|
||||
// Cannot cancel XTAL force PU if PLL is force power on
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().clear_bit());
|
||||
|
||||
// Force PD APLL
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.plla_force_pu()
|
||||
.clear_bit()
|
||||
.plla_force_pd()
|
||||
.set_bit()
|
||||
// Open SAR_I2C protect function to avoid SAR_I2C
|
||||
// Reset when rtc_ldo is low.
|
||||
.reset_por_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// Cancel BBPLL force PU if setting no force power up
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bbpll_force_pu()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pu()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
rtc_cntl.rtc_cntl().modify(|_, w| {
|
||||
w.regulator_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pd()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
// If this mask is enabled, all soc memories cannot enter power down mode.
|
||||
// We should control soc memory power down mode from RTC,
|
||||
// so we will not touch this register any more.
|
||||
system
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
rtc_sleep_pu();
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.dg_wrap_force_pu()
|
||||
.clear_bit()
|
||||
.wifi_force_pu()
|
||||
.clear_bit()
|
||||
.bt_force_pu()
|
||||
.clear_bit()
|
||||
.cpu_top_force_pu()
|
||||
.clear_bit()
|
||||
.dg_peri_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_wrap_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.bt_force_noiso()
|
||||
.clear_bit()
|
||||
.cpu_top_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_peri_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// Cancel digital PADS force no iso
|
||||
system
|
||||
.cpu_per_conf()
|
||||
.modify(|_, w| w.cpu_wait_mode_force_on().clear_bit());
|
||||
|
||||
// If SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0,
|
||||
// the CPU clock will be closed when CPU enter WAITI mode.
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
/// Configure whether certain peripherals are powered down in deep sleep
|
||||
fn rtc_sleep_pu() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
let apb_ctrl = unsafe { &*APB_CTRL::ptr() };
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.lslp_mem_force_pu()
|
||||
.clear_bit()
|
||||
.fastmem_force_lpu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
apb_ctrl.front_end_mem_pd().modify(|_, w| {
|
||||
w.dc_mem_force_pu()
|
||||
.clear_bit()
|
||||
.pbus_mem_force_pu()
|
||||
.clear_bit()
|
||||
.agc_mem_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
apb_ctrl
|
||||
.mem_power_up()
|
||||
.modify(|_, w| unsafe { w.sram_power_up().bits(0u8).rom_power_up().bits(0u8) });
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
/// SOC Reset Reason.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
///
|
||||
/// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or
|
||||
/// `ChipSuperWdt`, however that is not really compatible with Rust-style
|
||||
/// enums.
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU 0
|
||||
Cpu0Mwdt0 = 0x0B,
|
||||
/// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU 0
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Main watch dog 1 resets CPU 0
|
||||
Cpu0Mwdt1 = 0x11,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// Glitch on clock resets the digital core and rtc module
|
||||
SysClkGlitch = 0x13,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
/// USB UART resets the digital core
|
||||
CoreUsbUart = 0x15,
|
||||
/// USB JTAG resets the digital core
|
||||
CoreUsbJtag = 0x16,
|
||||
/// Glitch on power resets the digital core
|
||||
CorePwrGlitch = 0x17,
|
||||
}
|
||||
+1969
File diff suppressed because it is too large.
Load diff
+628
@@ -0,0 +1,628 @@
|
||||
use fugit::HertzU32;
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
clock::{clocks_ll::regi2c_write_mask, Clock, XtalClock},
|
||||
peripherals::{LPWR, LP_AON, PCR, PMU, TIMG0},
|
||||
rtc_cntl::RtcClock,
|
||||
};
|
||||
|
||||
const I2C_PMU: u8 = 0x6d;
|
||||
const I2C_PMU_HOSTID: u8 = 0;
|
||||
|
||||
const I2C_PMU_EN_I2C_RTC_DREG: u8 = 8;
|
||||
const I2C_PMU_EN_I2C_RTC_DREG_MSB: u8 = 0;
|
||||
const I2C_PMU_EN_I2C_RTC_DREG_LSB: u8 = 0;
|
||||
|
||||
const I2C_PMU_EN_I2C_DIG_DREG: u8 = 8;
|
||||
const I2C_PMU_EN_I2C_DIG_DREG_MSB: u8 = 1;
|
||||
const I2C_PMU_EN_I2C_DIG_DREG_LSB: u8 = 1;
|
||||
|
||||
const I2C_PMU_EN_I2C_RTC_DREG_SLP: u8 = 8;
|
||||
const I2C_PMU_EN_I2C_RTC_DREG_SLP_MSB: u8 = 2;
|
||||
const I2C_PMU_EN_I2C_RTC_DREG_SLP_LSB: u8 = 2;
|
||||
|
||||
const I2C_PMU_EN_I2C_DIG_DREG_SLP: u8 = 8;
|
||||
const I2C_PMU_EN_I2C_DIG_DREG_SLP_MSB: u8 = 3;
|
||||
const I2C_PMU_EN_I2C_DIG_DREG_SLP_LSB: u8 = 3;
|
||||
|
||||
const I2C_PMU_OR_XPD_RTC_REG: u8 = 8;
|
||||
const I2C_PMU_OR_XPD_RTC_REG_MSB: u8 = 4;
|
||||
const I2C_PMU_OR_XPD_RTC_REG_LSB: u8 = 4;
|
||||
|
||||
const I2C_PMU_OR_XPD_DIG_REG: u8 = 8;
|
||||
const I2C_PMU_OR_XPD_DIG_REG_MSB: u8 = 5;
|
||||
const I2C_PMU_OR_XPD_DIG_REG_LSB: u8 = 5;
|
||||
|
||||
const I2C_PMU_OR_XPD_TRX: u8 = 15;
|
||||
const I2C_PMU_OR_XPD_TRX_MSB: u8 = 2;
|
||||
const I2C_PMU_OR_XPD_TRX_LSB: u8 = 2;
|
||||
|
||||
pub(crate) fn init() {
|
||||
// * No peripheral reg i2c power up required on the target */
|
||||
unsafe {
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_EN_I2C_RTC_DREG,
|
||||
I2C_PMU_EN_I2C_RTC_DREG_MSB,
|
||||
I2C_PMU_EN_I2C_RTC_DREG_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_EN_I2C_DIG_DREG,
|
||||
I2C_PMU_EN_I2C_DIG_DREG_MSB,
|
||||
I2C_PMU_EN_I2C_DIG_DREG_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_EN_I2C_RTC_DREG_SLP,
|
||||
I2C_PMU_EN_I2C_RTC_DREG_SLP_MSB,
|
||||
I2C_PMU_EN_I2C_RTC_DREG_SLP_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_EN_I2C_DIG_DREG_SLP,
|
||||
I2C_PMU_EN_I2C_DIG_DREG_SLP_MSB,
|
||||
I2C_PMU_EN_I2C_DIG_DREG_SLP_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_OR_XPD_RTC_REG,
|
||||
I2C_PMU_OR_XPD_RTC_REG_MSB,
|
||||
I2C_PMU_OR_XPD_RTC_REG_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_OR_XPD_DIG_REG,
|
||||
I2C_PMU_OR_XPD_DIG_REG_MSB,
|
||||
I2C_PMU_OR_XPD_DIG_REG_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_PMU,
|
||||
I2C_PMU_HOSTID,
|
||||
I2C_PMU_OR_XPD_TRX,
|
||||
I2C_PMU_OR_XPD_TRX_MSB,
|
||||
I2C_PMU_OR_XPD_TRX_LSB,
|
||||
0,
|
||||
);
|
||||
|
||||
let pmu = &*crate::peripherals::PMU::PTR;
|
||||
|
||||
pmu.power_pd_top_cntl().write(|w| w.bits(0));
|
||||
pmu.power_pd_hpaon_cntl().write(|w| w.bits(0));
|
||||
pmu.power_pd_hpcpu_cntl().write(|w| w.bits(0));
|
||||
pmu.power_pd_hpperi_reserve().write(|w| w.bits(0));
|
||||
pmu.power_pd_hpwifi_cntl().write(|w| w.bits(0));
|
||||
pmu.power_pd_lpperi_cntl().write(|w| w.bits(0));
|
||||
|
||||
pmu.hp_active_hp_regulator0()
|
||||
.modify(|_, w| w.hp_active_hp_regulator_dbias().bits(25));
|
||||
pmu.hp_sleep_lp_regulator0()
|
||||
.modify(|_, w| w.hp_sleep_lp_regulator_dbias().bits(26));
|
||||
|
||||
pmu.slp_wakeup_cntl5()
|
||||
.modify(|_, w| w.lp_ana_wait_target().bits(15));
|
||||
pmu.slp_wakeup_cntl7()
|
||||
.modify(|_, w| w.ana_wait_target().bits(1700));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq32M));
|
||||
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClockRcFast);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRcSlow);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let lp_aon = &*LP_AON::ptr();
|
||||
lp_aon.store1().modify(|_, w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
/// SOC Reset Reason.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
///
|
||||
/// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or
|
||||
/// `ChipSuperWdt`, however that is not really compatible with Rust-style
|
||||
/// enums.
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU 0
|
||||
Cpu0Mwdt0 = 0x0B,
|
||||
/// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU 0
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Main watch dog 1 resets CPU 0
|
||||
Cpu0Mwdt1 = 0x11,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// Glitch on clock resets the digital core and rtc module
|
||||
SysClkGlitch = 0x13,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
/// USB UART resets the digital core
|
||||
CoreUsbUart = 0x15,
|
||||
/// USB JTAG resets the digital core
|
||||
CoreUsbJtag = 0x16,
|
||||
/// Glitch on power resets the digital core
|
||||
CorePwrGlitch = 0x17,
|
||||
}
|
||||
|
||||
/// RTC SLOW_CLK frequency values
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum RtcFastClock {
|
||||
/// Select RC_FAST_CLK as RTC_FAST_CLK source
|
||||
RtcFastClockRcFast = 0,
|
||||
#[allow(dead_code)]
|
||||
/// Select XTAL_D2_CLK as RTC_FAST_CLK source
|
||||
RtcFastClockXtalD2 = 1,
|
||||
}
|
||||
|
||||
impl Clock for RtcFastClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
RtcFastClock::RtcFastClockXtalD2 => HertzU32::Hz(16_000_000),
|
||||
RtcFastClock::RtcFastClockRcFast => HertzU32::Hz(8_000_000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RTC SLOW_CLK frequency values
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum RtcSlowClock {
|
||||
/// Select RC_SLOW_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClockRcSlow = 0,
|
||||
/// Select XTAL32K_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClock32kXtal = 1,
|
||||
/// Select RC32K_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClock32kRc = 2,
|
||||
/// Select OSC_SLOW_CLK (external slow clock) as RTC_SLOW_CLK source
|
||||
RtcSlowOscSlow = 3,
|
||||
}
|
||||
|
||||
impl Clock for RtcSlowClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => HertzU32::Hz(150_000),
|
||||
RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768),
|
||||
RtcSlowClock::RtcSlowClock32kRc => HertzU32::Hz(32_768),
|
||||
RtcSlowClock::RtcSlowOscSlow => HertzU32::Hz(32_768),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// Clock source to be calibrated using rtc_clk_cal function
|
||||
pub(crate) enum RtcCalSel {
|
||||
/// Currently selected RTC SLOW_CLK
|
||||
RtcCalRtcMux = -1,
|
||||
/// Internal 150kHz RC oscillator
|
||||
RtcCalRcSlow = 0,
|
||||
/// External 32kHz XTAL, as one type of 32k clock
|
||||
RtcCal32kXtal = 1,
|
||||
/// Internal 32kHz RC oscillator, as one type of 32k clock
|
||||
RtcCal32kRc = 2,
|
||||
/// External slow clock signal input by lp_pad_gpio0, as one type of 32k
|
||||
/// clock
|
||||
RtcCal32kOscSlow = 3,
|
||||
/// Internal 20MHz RC oscillator
|
||||
RtcCalRcFast,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum RtcCaliClkSel {
|
||||
CaliClkRcSlow = 0,
|
||||
CaliClkRcFast = 1,
|
||||
CaliClk32k = 2,
|
||||
}
|
||||
|
||||
/// RTC Watchdog Timer driver
|
||||
impl RtcClock {
|
||||
/// Get main XTAL frequency.
|
||||
/// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the
|
||||
/// bootloader, as passed to rtc_clk_init function.
|
||||
pub fn xtal_freq() -> XtalClock {
|
||||
match Self::read_xtal_freq_mhz() {
|
||||
None | Some(32) => XtalClock::RtcXtalFreq32M,
|
||||
Some(other) => XtalClock::RtcXtalFreqOther(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fast_freq(fast_freq: RtcFastClock) {
|
||||
// components/hal/esp32s2/include/hal/clk_tree_ll.h
|
||||
unsafe {
|
||||
let lp_clkrst = &*LPWR::PTR;
|
||||
lp_clkrst.lp_clk_conf().modify(|_, w| {
|
||||
w.fast_clk_sel().bits(match fast_freq {
|
||||
RtcFastClock::RtcFastClockRcFast => 0b00,
|
||||
RtcFastClock::RtcFastClockXtalD2 => 0b01,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
crate::rom::ets_delay_us(3);
|
||||
}
|
||||
|
||||
fn set_slow_freq(slow_freq: RtcSlowClock) {
|
||||
unsafe {
|
||||
let lp_clkrst = &*LPWR::PTR;
|
||||
|
||||
lp_clkrst
|
||||
.lp_clk_conf()
|
||||
.modify(|_, w| w.slow_clk_sel().bits(slow_freq as u8));
|
||||
lp_clkrst.clk_to_hp().modify(|_, w| {
|
||||
w.icg_hp_xtal32k()
|
||||
.bit(matches!(slow_freq, RtcSlowClock::RtcSlowClock32kXtal))
|
||||
.icg_hp_xtal32k()
|
||||
.bit(matches!(slow_freq, RtcSlowClock::RtcSlowClock32kXtal))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RTC_SLOW_CLK source
|
||||
pub fn slow_freq() -> RtcSlowClock {
|
||||
let lp_clrst = unsafe { &*LPWR::ptr() };
|
||||
|
||||
let slow_freq = lp_clrst.lp_clk_conf().read().slow_clk_sel().bits();
|
||||
match slow_freq {
|
||||
0 => RtcSlowClock::RtcSlowClockRcSlow,
|
||||
1 => RtcSlowClock::RtcSlowClock32kXtal,
|
||||
2 => RtcSlowClock::RtcSlowClock32kRc,
|
||||
3 => RtcSlowClock::RtcSlowOscSlow,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
let xtal_freq = RtcClock::xtal_freq();
|
||||
let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64;
|
||||
let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64;
|
||||
let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider;
|
||||
|
||||
(period_64 & u32::MAX as u64) as u32
|
||||
}
|
||||
|
||||
/// Calibration of RTC_SLOW_CLK is performed using a special feature of
|
||||
/// TIMG0. This feature counts the number of XTAL clock cycles within a
|
||||
/// given number of RTC_SLOW_CLK cycles.
|
||||
fn calibrate_internal(mut cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
const SOC_CLK_RC_FAST_FREQ_APPROX: u32 = 17_500_000;
|
||||
const SOC_CLK_RC_SLOW_FREQ_APPROX: u32 = 136_000;
|
||||
const SOC_CLK_XTAL32K_FREQ_APPROX: u32 = 32768;
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match cal_clk {
|
||||
RtcCalSel::RtcCalRtcMux => match RtcClock::slow_freq() {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
_ => cal_clk,
|
||||
},
|
||||
RtcCalSel::RtcCal32kOscSlow => RtcCalSel::RtcCalRtcMux,
|
||||
_ => cal_clk,
|
||||
};
|
||||
}
|
||||
|
||||
let lp_clkrst = unsafe { &*LPWR::ptr() };
|
||||
let pcr = unsafe { &*PCR::ptr() };
|
||||
let pmu = unsafe { &*PMU::ptr() };
|
||||
|
||||
let clk_src = RtcClock::slow_freq();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match clk_src {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow,
|
||||
};
|
||||
}
|
||||
|
||||
let cali_clk_sel;
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match clk_src {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCalRcSlow,
|
||||
}
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClkRcFast;
|
||||
} else if cal_clk == RtcCalSel::RtcCalRcSlow {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClkRcSlow;
|
||||
} else {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClk32k;
|
||||
match cal_clk {
|
||||
RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => {}
|
||||
RtcCalSel::RtcCal32kRc => {
|
||||
pcr.ctrl_32k_conf()
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(0) });
|
||||
}
|
||||
RtcCalSel::RtcCal32kXtal => {
|
||||
pcr.ctrl_32k_conf()
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(1) });
|
||||
}
|
||||
RtcCalSel::RtcCal32kOscSlow => {
|
||||
pcr.ctrl_32k_conf()
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(2) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable requested clock (150k is always on)
|
||||
// Some delay is required before the time is stable
|
||||
// Only enable if originaly was disabled
|
||||
// If clock is already on, do nothing
|
||||
|
||||
let dig_32k_xtal_enabled = lp_clkrst.clk_to_hp().read().icg_hp_xtal32k().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_xtal32k().set_bit());
|
||||
}
|
||||
|
||||
// TODO: very hacky
|
||||
// in ESP-IDF these are not called in this function but the fields are set
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_xtal32k().set_bit());
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_xtal32k().set_bit());
|
||||
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
|
||||
let rc_fast_enabled = pmu
|
||||
.hp_sleep_lp_ck_power()
|
||||
.read()
|
||||
.hp_sleep_xpd_fosc_clk()
|
||||
.bit_is_set();
|
||||
let dig_rc_fast_enabled = lp_clkrst.clk_to_hp().read().icg_hp_fosc().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
if !rc_fast_enabled {
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit());
|
||||
crate::rom::ets_delay_us(50);
|
||||
}
|
||||
|
||||
if !dig_rc_fast_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_fosc().set_bit());
|
||||
crate::rom::ets_delay_us(5);
|
||||
}
|
||||
}
|
||||
|
||||
let rc32k_enabled = pmu
|
||||
.hp_sleep_lp_ck_power()
|
||||
.read()
|
||||
.hp_sleep_xpd_rc32k()
|
||||
.bit_is_set();
|
||||
let dig_rc32k_enabled = lp_clkrst.clk_to_hp().read().icg_hp_osc32k().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kRc {
|
||||
if !rc32k_enabled {
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
crate::rom::ets_delay_us(300);
|
||||
}
|
||||
|
||||
if !dig_rc32k_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_osc32k().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is already running calibration process
|
||||
// TODO: &mut TIMG0 for calibration
|
||||
let timg0 = unsafe { &*TIMG0::ptr() };
|
||||
|
||||
if timg0
|
||||
.rtccalicfg()
|
||||
.read()
|
||||
.rtc_cali_start_cycling()
|
||||
.bit_is_set()
|
||||
{
|
||||
timg0
|
||||
.rtccalicfg2()
|
||||
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) });
|
||||
|
||||
// Set small timeout threshold to accelerate the generation of timeot
|
||||
// Internal circuit will be reset when timeout occurs and will not affect the
|
||||
// next calibration
|
||||
while !timg0.rtccalicfg().read().rtc_cali_rdy().bit_is_set()
|
||||
&& !timg0.rtccalicfg2().read().rtc_cali_timeout().bit_is_set()
|
||||
{}
|
||||
}
|
||||
|
||||
// Prepare calibration
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| unsafe { w.rtc_cali_clk_sel().bits(cali_clk_sel.clone() as u8) });
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| w.rtc_cali_start_cycling().clear_bit());
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| unsafe { w.rtc_cali_max().bits(slowclk_cycles as u16) });
|
||||
|
||||
let expected_freq = match cali_clk_sel {
|
||||
RtcCaliClkSel::CaliClk32k => {
|
||||
timg0.rtccalicfg2().modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
|
||||
});
|
||||
SOC_CLK_XTAL32K_FREQ_APPROX
|
||||
}
|
||||
RtcCaliClkSel::CaliClkRcFast => {
|
||||
timg0
|
||||
.rtccalicfg2()
|
||||
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(0x01FFFFFF) });
|
||||
SOC_CLK_RC_FAST_FREQ_APPROX
|
||||
}
|
||||
_ => {
|
||||
timg0.rtccalicfg2().modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 10)
|
||||
});
|
||||
SOC_CLK_RC_SLOW_FREQ_APPROX
|
||||
}
|
||||
};
|
||||
|
||||
let us_time_estimate = (HertzU32::MHz(slowclk_cycles) / expected_freq).to_Hz();
|
||||
|
||||
// Start calibration
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit());
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| w.rtc_cali_start().set_bit());
|
||||
|
||||
// Wait for calibration to finish up to another us_time_estimate
|
||||
crate::rom::ets_delay_us(us_time_estimate);
|
||||
|
||||
let cal_val = loop {
|
||||
if timg0.rtccalicfg().read().rtc_cali_rdy().bit_is_set() {
|
||||
break timg0.rtccalicfg1().read().rtc_cali_value().bits();
|
||||
}
|
||||
|
||||
if timg0.rtccalicfg2().read().rtc_cali_timeout().bit_is_set() {
|
||||
// Timed out waiting for calibration
|
||||
break 0;
|
||||
}
|
||||
};
|
||||
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit());
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_xtal32k().clear_bit());
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
if rc_fast_enabled {
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit());
|
||||
crate::rom::ets_delay_us(50);
|
||||
}
|
||||
|
||||
if dig_rc_fast_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_fosc().set_bit());
|
||||
crate::rom::ets_delay_us(5);
|
||||
}
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kRc {
|
||||
if rc32k_enabled {
|
||||
pmu.hp_sleep_lp_ck_power()
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
crate::rom::ets_delay_us(300);
|
||||
}
|
||||
if dig_rc32k_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp()
|
||||
.modify(|_, w| w.icg_hp_osc32k().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
cal_val
|
||||
}
|
||||
|
||||
pub(crate) fn cycles_to_1ms() -> u16 {
|
||||
let period_13q19 = RtcClock::calibrate(
|
||||
match RtcClock::slow_freq() {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRtcMux,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow,
|
||||
// RtcSlowClock::RtcCalRcFast => RtcCalSel::RtcCalRcFast,
|
||||
},
|
||||
1024,
|
||||
);
|
||||
|
||||
// 100_000_000 is used to get rid of `float` calculations
|
||||
let period = (100_000_000 * period_13q19 as u64) / (1 << RtcClock::CAL_FRACT);
|
||||
|
||||
(100_000_000 * 1000 / period) as u16
|
||||
}
|
||||
|
||||
pub(crate) fn estimate_xtal_frequency() -> u32 {
|
||||
let timg0 = unsafe { crate::peripherals::TIMG0::steal() };
|
||||
while timg0.rtccalicfg().read().rtc_cali_rdy().bit_is_clear() {}
|
||||
|
||||
timg0.rtccalicfg().modify(|_, w| unsafe {
|
||||
w.rtc_cali_clk_sel()
|
||||
.bits(0) // RTC_SLOW_CLK
|
||||
.rtc_cali_max()
|
||||
.bits(100)
|
||||
.rtc_cali_start_cycling()
|
||||
.clear_bit()
|
||||
.rtc_cali_start()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
timg0
|
||||
.rtccalicfg()
|
||||
.modify(|_, w| w.rtc_cali_start().set_bit());
|
||||
|
||||
while timg0.rtccalicfg().read().rtc_cali_rdy().bit_is_clear() {}
|
||||
|
||||
(timg0.rtccalicfg1().read().rtc_cali_value().bits()
|
||||
* (RtcSlowClock::RtcSlowClockRcSlow.frequency().to_Hz() / 100))
|
||||
/ 1_000_000
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
clock::XtalClock,
|
||||
peripherals::RTC_CNTL,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
pub(crate) fn init() {}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M));
|
||||
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRtc);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::ptr();
|
||||
rtc_cntl.store1().write(|w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
/// SOC Reset Reason.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
///
|
||||
/// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or
|
||||
/// `ChipSuperWdt`, however that is not really compatible with Rust-style
|
||||
/// enums.
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU 0
|
||||
Cpu0Mwdt0 = 0x0B,
|
||||
/// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU 0
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Main watch dog 1 resets CPU 0
|
||||
Cpu0Mwdt1 = 0x11,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// Glitch on clock resets the digital core and rtc module
|
||||
SysClkGlitch = 0x13,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
clock::XtalClock,
|
||||
peripherals::RTC_CNTL,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
pub(crate) fn init() {}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M));
|
||||
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRtc);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::ptr();
|
||||
rtc_cntl.store1().write(|w| w.bits(cal_val));
|
||||
}
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
/// SOC Reset Reason.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
///
|
||||
/// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or
|
||||
/// `ChipSuperWdt`, however that is not really compatible with Rust-style
|
||||
/// enums.
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0Mwdt0` and `Cpu1Mwdt0`, however they have the
|
||||
/// same values.
|
||||
CpuMwdt0 = 0x0B,
|
||||
/// Software resets CPU by RTC_CNTL_SW_(PRO|APP)CPU_RST
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0Sw` and `Cpu1Sw`, however they have the same
|
||||
/// values.
|
||||
CpuSw = 0x0C,
|
||||
/// RTC watch dog resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0RtcWdt` and `Cpu1RtcWdt`, however they have
|
||||
/// the same values.
|
||||
CpuRtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Main watch dog 1 resets CPU
|
||||
///
|
||||
/// In ESP-IDF there are `Cpu0Mwdt1` and `Cpu1Mwdt1`, however they have the
|
||||
/// same values.
|
||||
CpuMwdt1 = 0x11,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// Glitch on clock resets the digital core and rtc module
|
||||
SysClkGlitch = 0x13,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
/// USB UART resets the digital core
|
||||
CoreUsbUart = 0x15,
|
||||
/// USB JTAG resets the digital core
|
||||
CoreUsbJtag = 0x16,
|
||||
/// Glitch on power resets the digital core
|
||||
CorePwrGlitch = 0x17,
|
||||
}
|
||||
+609
@@ -0,0 +1,609 @@
|
||||
use super::{Ext0WakeupSource, Ext1WakeupSource, TimerWakeupSource, WakeSource, WakeTriggers};
|
||||
use crate::{
|
||||
gpio::{RtcFunction, RtcPin},
|
||||
rtc_cntl::{sleep::WakeupLevel, Clock, Rtc, RtcClock},
|
||||
};
|
||||
|
||||
// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP,
|
||||
// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values.
|
||||
// Valid if RTC_CNTL_DBG_ATTEN is 0.
|
||||
/// RTC digital bias setting corresponding to 0.90V.
|
||||
pub const RTC_CNTL_DBIAS_0V90: u8 = 0;
|
||||
/// RTC digital bias setting corresponding to 0.95V.
|
||||
pub const RTC_CNTL_DBIAS_0V95: u8 = 1;
|
||||
/// RTC digital bias setting corresponding to 1.00V.
|
||||
pub const RTC_CNTL_DBIAS_1V00: u8 = 2;
|
||||
/// RTC digital bias setting corresponding to 1.05V.
|
||||
pub const RTC_CNTL_DBIAS_1V05: u8 = 3;
|
||||
/// RTC digital bias setting corresponding to 1.10V.
|
||||
pub const RTC_CNTL_DBIAS_1V10: u8 = 4;
|
||||
/// RTC digital bias setting corresponding to 1.15V.
|
||||
pub const RTC_CNTL_DBIAS_1V15: u8 = 5;
|
||||
/// RTC digital bias setting corresponding to 1.20V.
|
||||
pub const RTC_CNTL_DBIAS_1V20: u8 = 6;
|
||||
/// RTC digital bias setting corresponding to 1.25V.
|
||||
pub const RTC_CNTL_DBIAS_1V25: u8 = 7;
|
||||
|
||||
// Various delays to be programmed into power control state machines
|
||||
/// Time (in microseconds) for waiting the XTL buffer to stabilize during sleep.
|
||||
pub const RTC_CNTL_XTL_BUF_WAIT_SLP_US: u32 = 1000;
|
||||
/// Cycles to wait for PLL buffer stabilization.
|
||||
pub const RTC_CNTL_PLL_BUF_WAIT_SLP_CYCLES: u8 = 1;
|
||||
/// Cycles to wait for the 8MHz clock to stabilize.
|
||||
pub const RTC_CNTL_CK8M_WAIT_SLP_CYCLES: u8 = 4;
|
||||
/// Delay in cycles for wakeup signal to be applied.
|
||||
pub const RTC_CNTL_WAKEUP_DELAY_CYCLES: u8 = 7;
|
||||
/// Power-up cycles for other blocks.
|
||||
pub const RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES: u8 = 1;
|
||||
/// Wait cycles for other blocks.
|
||||
pub const RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES: u16 = 1;
|
||||
/// Minimum sleep value (in cycles).
|
||||
pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 128;
|
||||
/// Default debug attenuation value.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEFAULT: u8 = 3;
|
||||
|
||||
/// Power-up cycles for RTC memory.
|
||||
pub const RTC_MEM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES;
|
||||
/// Wait cycles for RTC memory.
|
||||
pub const RTC_MEM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES;
|
||||
/// Power-up cycles for ROM and RAM.
|
||||
pub const ROM_RAM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES;
|
||||
/// Wait cycles for ROM and RAM.
|
||||
pub const ROM_RAM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES;
|
||||
/// Power-up cycles for Wi-Fi.
|
||||
pub const WIFI_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES;
|
||||
/// Wait cycles for Wi-Fi.
|
||||
pub const WIFI_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES;
|
||||
/// Power-up cycles for RTC components.
|
||||
pub const RTC_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES;
|
||||
/// Wait cycles for RTC components.
|
||||
pub const RTC_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES;
|
||||
/// Power-up cycles for the digital wrap components.
|
||||
pub const DG_WRAP_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES;
|
||||
/// Wait cycles for the digital wrap components.
|
||||
pub const DG_WRAP_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES;
|
||||
|
||||
/// Default wait cycles for the 8MHz clock.
|
||||
pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default wait cycles to enable the 8MHz clock.
|
||||
pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5;
|
||||
impl WakeSource for TimerWakeupSource {
|
||||
fn apply(
|
||||
&self,
|
||||
rtc: &Rtc<'_>,
|
||||
triggers: &mut WakeTriggers,
|
||||
_sleep_config: &mut RtcSleepConfig,
|
||||
) {
|
||||
triggers.set_timer(true);
|
||||
let rtc_cntl = unsafe { &*esp32::RTC_CNTL::ptr() };
|
||||
let clock_freq = RtcClock::slow_freq();
|
||||
// TODO: maybe add sleep time adjustlemnt like idf
|
||||
// TODO: maybe add check to prevent overflow?
|
||||
let clock_hz = clock_freq.frequency().to_Hz() as u64;
|
||||
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
|
||||
// "alarm" time in slow rtc ticks
|
||||
let now = rtc.time_since_boot_raw();
|
||||
let time_in_ticks = now + ticks;
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.slp_timer0()
|
||||
.write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32));
|
||||
|
||||
rtc_cntl.slp_timer1().write(|w| {
|
||||
w.slp_val_hi()
|
||||
.bits(((time_in_ticks >> 32) & 0xffff) as u16)
|
||||
.main_timer_alarm_en()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
triggers.set_ext0(true);
|
||||
|
||||
// set pin to RTC function
|
||||
self.pin
|
||||
.borrow_mut()
|
||||
.rtc_set_config(true, true, RtcFunction::Rtc);
|
||||
|
||||
unsafe {
|
||||
let rtc_io = &*esp32::RTC_IO::ptr();
|
||||
// set pin register field
|
||||
rtc_io
|
||||
.ext_wakeup0()
|
||||
.modify(|_, w| w.sel().bits(self.pin.borrow().rtc_number()));
|
||||
// set level register field
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_r, w| w.ext_wakeup0_lv().bit(self.level == WakeupLevel::High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// to IO_MUX)
|
||||
self.pin
|
||||
.borrow_mut()
|
||||
.rtc_set_config(true, false, RtcFunction::Rtc);
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeSource for Ext1WakeupSource<'_, '_> {
|
||||
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);
|
||||
triggers.set_ext1(true);
|
||||
|
||||
// set pins to RTC function
|
||||
let mut pins = self.pins.borrow_mut();
|
||||
let mut bits = 0u32;
|
||||
for pin in pins.iter_mut() {
|
||||
pin.rtc_set_config(true, true, RtcFunction::Rtc);
|
||||
bits |= 1 << pin.rtc_number();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
// clear previous wakeup status
|
||||
rtc_cntl
|
||||
.ext_wakeup1()
|
||||
.modify(|_, w| w.status_clr().set_bit());
|
||||
// set pin register field
|
||||
rtc_cntl.ext_wakeup1().modify(|_, w| w.sel().bits(bits));
|
||||
// set level register field
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_r, w| w.ext_wakeup1_lv().bit(self.level == WakeupLevel::High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ext1WakeupSource<'_, '_> {
|
||||
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 in pins.iter_mut() {
|
||||
pin.rtc_set_config(true, false, RtcFunction::Rtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
/// Configuration for the RTC sleep behavior.
|
||||
pub struct RtcSleepConfig(u32);
|
||||
impl Debug;
|
||||
/// force normal voltage in sleep mode (digital domain memory)
|
||||
pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0;
|
||||
/// force normal voltage in sleep mode (RTC memory)
|
||||
pub rtc_mem_inf_fpu, set_rtc_mem_inf_fpu: 1;
|
||||
/// keep low voltage in sleep mode (even if ULP/touch is used)
|
||||
pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 2;
|
||||
/// power down RTC fast memory
|
||||
pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 3;
|
||||
/// power down RTC slow memory
|
||||
pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 4;
|
||||
/// power down RTC peripherals
|
||||
pub rtc_peri_pd_en, set_rtc_peri_pd_en: 5;
|
||||
/// power down WiFi
|
||||
pub wifi_pd_en, set_wifi_pd_en: 6;
|
||||
/// Power down Internal 8M oscillator
|
||||
pub int_8m_pd_en, set_int_8m_pd_en: 7;
|
||||
/// power down main RAM and ROM
|
||||
pub rom_mem_pd_en, set_rom_mem_pd_en: 8;
|
||||
/// power down digital domain
|
||||
pub deep_slp, set_deep_slp: 9;
|
||||
/// enable WDT flashboot mode
|
||||
pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 10;
|
||||
/// bias for digital domain, in active mode
|
||||
pub u8, dig_dbias_wak, set_dig_dbias_wak: 13, 11;
|
||||
/// bias for digital domain, in sleep mode
|
||||
pub u8, dig_dbias_slp, set_dig_dbias_slp: 16, 14;
|
||||
/// bias for RTC domain, in active mode
|
||||
pub u8, rtc_dbias_wak, set_rtc_dbias_wak: 19, 17;
|
||||
/// bias for RTC domain, in sleep mode
|
||||
pub u8, rtc_dbias_slp, set_rtc_dbias_slp: 22, 20;
|
||||
/// remove all peripheral force power up flags
|
||||
pub lslp_meminf_pd, set_lslp_meminf_pd: 23;
|
||||
/// power down VDDSDIO regulator
|
||||
pub vddsdio_pd_en, set_vddsdio_pd_en: 24;
|
||||
/// keep main XTAL powered up in sleep
|
||||
pub xtal_fpu, set_xtal_fpu: 25;
|
||||
/// enable deep sleep reject
|
||||
pub deep_slp_reject, set_deep_slp_reject: 26;
|
||||
/// enable light sleep reject
|
||||
pub light_slp_reject, set_light_slp_reject: 27;
|
||||
}
|
||||
|
||||
impl Default for RtcSleepConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_lslp_meminf_pd(true);
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_dig_dbias_wak(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_rtc_dbias_wak(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
impl RtcSleepConfig {
|
||||
/// Configures the RTC for deep sleep mode.
|
||||
pub fn deep() -> Self {
|
||||
let mut cfg = Self::default();
|
||||
cfg.set_deep_slp(true);
|
||||
cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_0V90);
|
||||
// cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_0V90);
|
||||
cfg.set_vddsdio_pd_en(true);
|
||||
cfg.set_int_8m_pd_en(true);
|
||||
cfg.set_xtal_fpu(false);
|
||||
cfg.set_wifi_pd_en(true);
|
||||
cfg.set_rom_mem_pd_en(true);
|
||||
cfg.set_rtc_peri_pd_en(true);
|
||||
cfg.set_rtc_fastmem_pd_en(true);
|
||||
cfg.set_rtc_slowmem_pd_en(true);
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
|
||||
// settings derived from esp-idf after basic boot
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bias_core_force_pu()
|
||||
.clear_bit()
|
||||
.bias_core_folw_8m()
|
||||
.set_bit()
|
||||
.bias_i2c_force_pu()
|
||||
.clear_bit()
|
||||
.bias_i2c_folw_8m()
|
||||
.set_bit()
|
||||
.bias_force_nosleep()
|
||||
.clear_bit()
|
||||
.bias_sleep_folw_8m()
|
||||
.set_bit()
|
||||
.xtl_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.reg().modify(|_, w| {
|
||||
w.force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pd()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.slowmem_force_pu()
|
||||
.clear_bit()
|
||||
.fastmem_force_pu()
|
||||
.clear_bit()
|
||||
.force_noiso()
|
||||
.clear_bit()
|
||||
.slowmem_force_noiso()
|
||||
.clear_bit()
|
||||
.fastmem_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.dg_wrap_force_pu()
|
||||
.clear_bit()
|
||||
.wifi_force_pu()
|
||||
.clear_bit()
|
||||
.wifi_force_pd()
|
||||
.set_bit()
|
||||
.inter_ram4_force_pu()
|
||||
.clear_bit()
|
||||
.inter_ram3_force_pu()
|
||||
.clear_bit()
|
||||
.inter_ram2_force_pu()
|
||||
.clear_bit()
|
||||
.inter_ram1_force_pu()
|
||||
.clear_bit()
|
||||
.inter_ram0_force_pu()
|
||||
.clear_bit()
|
||||
.rom0_force_pu()
|
||||
.clear_bit()
|
||||
.lslp_mem_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_wrap_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_iso()
|
||||
.set_bit()
|
||||
.inter_ram4_force_noiso()
|
||||
.clear_bit()
|
||||
.inter_ram3_force_noiso()
|
||||
.clear_bit()
|
||||
.inter_ram2_force_noiso()
|
||||
.clear_bit()
|
||||
.inter_ram1_force_noiso()
|
||||
.clear_bit()
|
||||
.inter_ram0_force_noiso()
|
||||
.clear_bit()
|
||||
.rom0_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.int_ena().modify(|_, w| w.brown_out().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply(&self) {
|
||||
// like esp-idf rtc_sleep_init()
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.timer5().modify(|_, w| {
|
||||
w.min_slp_val()
|
||||
.bits(RTC_CNTL_MIN_SLP_VAL_MIN)
|
||||
// set rtc memory timer
|
||||
.rtcmem_powerup_timer()
|
||||
.bits(RTC_MEM_POWERUP_CYCLES)
|
||||
.rtcmem_wait_timer()
|
||||
.bits(RTC_MEM_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl.timer3().modify(|_, w| {
|
||||
w
|
||||
// set rom&ram timer
|
||||
.rom_ram_powerup_timer()
|
||||
.bits(ROM_RAM_POWERUP_CYCLES)
|
||||
.rom_ram_wait_timer()
|
||||
.bits(ROM_RAM_WAIT_CYCLES)
|
||||
// set wifi timer
|
||||
.wifi_powerup_timer()
|
||||
.bits(WIFI_POWERUP_CYCLES)
|
||||
.wifi_wait_timer()
|
||||
.bits(WIFI_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl.timer4().modify(|_, w| {
|
||||
w
|
||||
// set rtc peri timer
|
||||
.powerup_timer()
|
||||
.bits(RTC_POWERUP_CYCLES)
|
||||
.wait_timer()
|
||||
.bits(RTC_WAIT_CYCLES)
|
||||
// set digital wrap timer
|
||||
.dg_wrap_powerup_timer()
|
||||
.bits(DG_WRAP_POWERUP_CYCLES)
|
||||
.dg_wrap_wait_timer()
|
||||
.bits(DG_WRAP_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().bit(self.lslp_mem_inf_fpu()));
|
||||
|
||||
// remove all peripheral force power up flags
|
||||
if self.lslp_meminf_pd() {
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.slowmem_force_pu()
|
||||
.clear_bit()
|
||||
.fastmem_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// esp-idf also clears these:
|
||||
|
||||
(*esp32::DPORT::ptr())
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
(*esp32::I2S0::ptr())
|
||||
.pd_conf()
|
||||
.modify(|_, w| w.plc_mem_force_pu().clear_bit().fifo_force_pu().clear_bit());
|
||||
|
||||
(*esp32::BB::ptr())
|
||||
.bbpd_ctrl()
|
||||
.modify(|_, w| w.fft_force_pu().clear_bit().dc_est_force_pu().clear_bit());
|
||||
|
||||
(*esp32::NRX::ptr()).nrxpd_ctrl().modify(|_, w| {
|
||||
w.rx_rot_force_pu()
|
||||
.clear_bit()
|
||||
.vit_force_pu()
|
||||
.clear_bit()
|
||||
.demap_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
// (&*esp32::FE::ptr()).gen_ctrl.modify(|_, w| w
|
||||
// .iq_est_force_pu().clear_bit()
|
||||
// );
|
||||
//
|
||||
// (&*esp32::FE2::ptr()).tx_interp_ctrl.modify(|_, w| w
|
||||
// .inf_force_pu().clear_bit()
|
||||
// );
|
||||
}
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.slowmem_folw_cpu()
|
||||
.bit(self.rtc_mem_inf_follow_cpu())
|
||||
.fastmem_folw_cpu()
|
||||
.bit(self.rtc_mem_inf_follow_cpu())
|
||||
// TODO: does this need to be optional based on if there is something stored in
|
||||
// fastmem?
|
||||
//.fastmem_pd_en().bit(self.rtc_fastmem_pd_en())
|
||||
.fastmem_force_pu()
|
||||
.bit(!self.rtc_fastmem_pd_en())
|
||||
.fastmem_force_lpu()
|
||||
.bit(!self.rtc_fastmem_pd_en())
|
||||
.fastmem_force_noiso()
|
||||
.bit(!self.rtc_fastmem_pd_en())
|
||||
.slowmem_pd_en()
|
||||
.bit(self.rtc_slowmem_pd_en())
|
||||
.slowmem_force_pu()
|
||||
.bit(!self.rtc_slowmem_pd_en())
|
||||
.slowmem_force_noiso()
|
||||
.bit(!self.rtc_slowmem_pd_en())
|
||||
.slowmem_force_lpu()
|
||||
.bit(!self.rtc_slowmem_pd_en())
|
||||
.pd_en()
|
||||
.bit(self.rtc_peri_pd_en())
|
||||
});
|
||||
|
||||
// rtc_cntl.dig_pwc.modify(|_, w| w
|
||||
// .wifi_pd_en().bit(self.wifi_pd_en())
|
||||
// .rom0_pd_en().bit(self.rom_mem_pd_en())
|
||||
// );
|
||||
|
||||
if self.deep_slp() {
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_wrap_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_pad_force_iso()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.dg_wrap_pd_en()
|
||||
.set_bit()
|
||||
.dg_wrap_force_pu()
|
||||
.clear_bit()
|
||||
.dg_wrap_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bias_force_nosleep()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.ckgen_i2c_pu()
|
||||
.clear_bit()
|
||||
.pll_i2c_pu()
|
||||
.clear_bit()
|
||||
.rfrx_pbus_pu()
|
||||
.clear_bit()
|
||||
.txrf_i2c_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
} else {
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().clear_bit());
|
||||
|
||||
rtc_cntl.bias_conf().modify(|_, w| w.dbg_atten().bits(0));
|
||||
}
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu()));
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().bit(!self.int_8m_pd_en()));
|
||||
|
||||
// enable VDDSDIO control by state machine
|
||||
|
||||
rtc_cntl.sdio_conf().modify(|_, w| {
|
||||
w.sdio_force()
|
||||
.clear_bit()
|
||||
.sdio_pd_en()
|
||||
.bit(self.vddsdio_pd_en())
|
||||
});
|
||||
|
||||
rtc_cntl.reg().modify(|_, w| {
|
||||
w.dbias_slp()
|
||||
.bits(self.rtc_dbias_slp())
|
||||
.dbias_wak()
|
||||
.bits(self.rtc_dbias_wak())
|
||||
.dig_dbias_slp()
|
||||
.bits(self.dig_dbias_slp())
|
||||
.dig_dbias_wak()
|
||||
.bits(self.dig_dbias_wak())
|
||||
});
|
||||
|
||||
rtc_cntl.slp_reject_conf().modify(|_, w| {
|
||||
w.deep_slp_reject_en()
|
||||
.bit(self.deep_slp_reject())
|
||||
.light_slp_reject_en()
|
||||
.bit(self.light_slp_reject())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl
|
||||
.reset_state()
|
||||
.modify(|_, w| w.procpu_stat_vector_sel().set_bit());
|
||||
|
||||
// set bits for what can wake us up
|
||||
rtc_cntl
|
||||
.wakeup_state()
|
||||
.modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0));
|
||||
|
||||
rtc_cntl
|
||||
.state0()
|
||||
.write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn finish_sleep(&self) {
|
||||
// In deep sleep mode, we never get here
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.int_clr().write(|w| {
|
||||
w.slp_reject()
|
||||
.clear_bit_by_one()
|
||||
.slp_wakeup()
|
||||
.clear_bit_by_one()
|
||||
});
|
||||
|
||||
// restore DBG_ATTEN to the default value
|
||||
|
||||
rtc_cntl
|
||||
.bias_conf()
|
||||
.modify(|_, w| w.dbg_atten().bits(RTC_CNTL_DBG_ATTEN_DEFAULT));
|
||||
}
|
||||
}
|
||||
}
|
||||
+834
@@ -0,0 +1,834 @@
|
||||
use super::{TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel};
|
||||
use crate::{
|
||||
gpio::{RtcFunction, RtcPinWithResistors},
|
||||
regi2c_write_mask,
|
||||
rtc_cntl::{sleep::RtcioWakeupSource, Clock, Rtc, RtcClock},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u32 = 0x6D;
|
||||
const I2C_DIG_REG_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP: u32 = 5;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG: u32 = 6;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP: u32 = 7;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3;
|
||||
|
||||
const I2C_ULP_IR_FORCE_XPD_CK: u8 = 0;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_MSB: u8 = 2;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_LSB: u8 = 2;
|
||||
|
||||
const I2C_ULP: u8 = 0x61;
|
||||
const I2C_ULP_HOSTID: u8 = 0;
|
||||
|
||||
// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP,
|
||||
// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values.
|
||||
// Valid if RTC_CNTL_DBG_ATTEN is 0.
|
||||
/// Digital bias voltage level of 0.90V.
|
||||
pub const RTC_CNTL_DBIAS_0V90: u32 = 13;
|
||||
/// Digital bias voltage level of 0.95V.
|
||||
pub const RTC_CNTL_DBIAS_0V95: u32 = 16;
|
||||
/// Digital bias voltage level of 1.00V.
|
||||
pub const RTC_CNTL_DBIAS_1V00: u32 = 18;
|
||||
/// Digital bias voltage level of 1.05V.
|
||||
pub const RTC_CNTL_DBIAS_1V05: u32 = 20;
|
||||
/// Digital bias voltage level of 1.10V.
|
||||
pub const RTC_CNTL_DBIAS_1V10: u32 = 23;
|
||||
/// Digital bias voltage level of 1.15V.
|
||||
pub const RTC_CNTL_DBIAS_1V15: u32 = 25;
|
||||
/// Digital bias voltage level of 1.20V.
|
||||
pub const RTC_CNTL_DBIAS_1V20: u32 = 28;
|
||||
/// Digital bias voltage level of 1.25V.
|
||||
pub const RTC_CNTL_DBIAS_1V25: u32 = 30;
|
||||
/// Digital bias voltage level of approximately 1.34V.
|
||||
pub const RTC_CNTL_DBIAS_1V30: u32 = 31;
|
||||
|
||||
/// Default attenuation setting during light sleep, with a voltage drop.
|
||||
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT: u8 = 5;
|
||||
/// No attenuation (no voltage drop) during light sleep.
|
||||
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP: u8 = 0;
|
||||
/// Default attenuation setting during deep sleep, with maximum voltage drop.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15;
|
||||
/// No attenuation (no voltage drop) during deep sleep.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP: u8 = 0;
|
||||
|
||||
/// Default bias setting during sleep mode.
|
||||
pub const RTC_CNTL_BIASSLP_SLEEP_DEFAULT: u8 = 1;
|
||||
/// Keeps the bias for ultra-low power sleep mode always on.
|
||||
pub const RTC_CNTL_BIASSLP_SLEEP_ON: u8 = 0;
|
||||
|
||||
/// Default power-down current setting during sleep mode.
|
||||
pub const RTC_CNTL_PD_CUR_SLEEP_DEFAULT: u8 = 1;
|
||||
/// Keeps the power-down current setting for sleep mode always on.
|
||||
pub const RTC_CNTL_PD_CUR_SLEEP_ON: u8 = 0;
|
||||
|
||||
/// Default driver bias setting for the digital domain during sleep mode.
|
||||
pub const RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT: u8 = 254;
|
||||
|
||||
/// Default debug attenuation setting for the monitor mode.
|
||||
pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0;
|
||||
/// Default bias setting for sleep mode in the monitor mode.
|
||||
pub const RTC_CNTL_BIASSLP_MONITOR_DEFAULT: bool = false;
|
||||
/// Default power-down current setting for the monitor mode.
|
||||
pub const RTC_CNTL_PD_CUR_MONITOR_DEFAULT: bool = false;
|
||||
|
||||
/// Default number of cycles to wait for the PLL buffer to stabilize.
|
||||
pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default number of cycles to wait for the XTL buffer to stabilize.
|
||||
pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100;
|
||||
/// Default number of cycles to wait for the internal 8MHz clock to stabilize.
|
||||
pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default number of cycles required to enable the internal 8MHz clock.
|
||||
pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5;
|
||||
|
||||
/// Minimum number of cycles for sleep duration.
|
||||
pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2;
|
||||
|
||||
/// Power-up cycles for other hardware blocks.
|
||||
pub const OTHER_BLOCKS_POWERUP: u8 = 1;
|
||||
/// Wait cycles for other hardware blocks to stabilize.
|
||||
pub const OTHER_BLOCKS_WAIT: u16 = 1;
|
||||
|
||||
/// Disables GPIO interrupt.
|
||||
pub const GPIO_INTR_DISABLE: u8 = 0;
|
||||
/// Sets GPIO interrupt to trigger on a low level signal.
|
||||
pub const GPIO_INTR_LOW_LEVEL: u8 = 4;
|
||||
/// Sets GPIO interrupt to trigger on a high level signal.
|
||||
pub const GPIO_INTR_HIGH_LEVEL: u8 = 5;
|
||||
|
||||
/// Specifies the function configuration for GPIO pins.
|
||||
pub const PIN_FUNC_GPIO: u8 = 1;
|
||||
/// Index for signaling GPIO output.
|
||||
pub const SIG_GPIO_OUT_IDX: u32 = 128;
|
||||
/// Maximum number of GPIO pins supported.
|
||||
pub const GPIO_NUM_MAX: usize = 22;
|
||||
|
||||
impl WakeSource for TimerWakeupSource {
|
||||
fn apply(
|
||||
&self,
|
||||
rtc: &Rtc<'_>,
|
||||
triggers: &mut WakeTriggers,
|
||||
_sleep_config: &mut RtcSleepConfig,
|
||||
) {
|
||||
triggers.set_timer(true);
|
||||
let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() };
|
||||
let clock_freq = RtcClock::slow_freq();
|
||||
// TODO: maybe add sleep time adjustlemnt like idf
|
||||
// TODO: maybe add check to prevent overflow?
|
||||
let clock_hz = clock_freq.frequency().to_Hz() as u64;
|
||||
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
|
||||
// "alarm" time in slow rtc ticks
|
||||
let now = rtc.time_since_boot_raw();
|
||||
let time_in_ticks = now + ticks;
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.slp_timer0()
|
||||
.write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32));
|
||||
|
||||
rtc_cntl
|
||||
.int_clr()
|
||||
.write(|w| w.main_timer().clear_bit_by_one());
|
||||
|
||||
rtc_cntl.slp_timer1().write(|w| {
|
||||
w.slp_val_hi()
|
||||
.bits(((time_in_ticks >> 32) & 0xffff) as u16)
|
||||
.main_timer_alarm_en()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RtcioWakeupSource<'_, '_> {
|
||||
fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) {
|
||||
// The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare
|
||||
let level = match level {
|
||||
WakeupLevel::High => {
|
||||
pin.rtcio_pullup(false);
|
||||
pin.rtcio_pulldown(true);
|
||||
GPIO_INTR_HIGH_LEVEL
|
||||
}
|
||||
WakeupLevel::Low => {
|
||||
pin.rtcio_pullup(true);
|
||||
pin.rtcio_pulldown(false);
|
||||
GPIO_INTR_LOW_LEVEL
|
||||
}
|
||||
};
|
||||
pin.rtcio_pad_hold(true);
|
||||
|
||||
// apply_wakeup does the same as idf's esp_deep_sleep_enable_gpio_wakeup
|
||||
unsafe {
|
||||
pin.apply_wakeup(true, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn isolate_digital_gpio() {
|
||||
// like esp_sleep_isolate_digital_gpio
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
let io_mux = unsafe { &*crate::peripherals::IO_MUX::ptr() };
|
||||
let gpio = unsafe { &*crate::peripherals::GPIO::ptr() };
|
||||
|
||||
let dig_iso = &rtc_cntl.dig_iso().read();
|
||||
let deep_sleep_hold_is_en =
|
||||
!dig_iso.dg_pad_force_unhold().bit() && dig_iso.dg_pad_autohold_en().bit();
|
||||
if !deep_sleep_hold_is_en {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: assert that the task stack is not in external ram
|
||||
|
||||
for pin_num in 0..GPIO_NUM_MAX {
|
||||
let pin_hold = rtc_cntl.dig_pad_hold().read().bits() & (1 << pin_num) != 0;
|
||||
if !pin_hold {
|
||||
// input disable, like gpio_ll_input_disable
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_ie().clear_bit());
|
||||
// output disable, like gpio_ll_output_disable
|
||||
unsafe {
|
||||
gpio.func_out_sel_cfg(pin_num)
|
||||
.modify(|_, w| w.bits(SIG_GPIO_OUT_IDX));
|
||||
}
|
||||
|
||||
// disable pull-up and pull-down
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_wpu().clear_bit());
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_wpd().clear_bit());
|
||||
|
||||
// make pad work as gpio (otherwise, deep_sleep bottom current will rise)
|
||||
io_mux
|
||||
.gpio(pin_num)
|
||||
.modify(|_, w| unsafe { w.mcu_sel().bits(RtcFunction::Digital 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;
|
||||
}
|
||||
|
||||
triggers.set_gpio(true);
|
||||
|
||||
// If deep sleep is enabled, esp_start_sleep calls
|
||||
// gpio_deep_sleep_wakeup_prepare which sets these pullup and
|
||||
// pulldown values. But later in esp_start_sleep it calls
|
||||
// esp_sleep_isolate_digital_gpio, which disables the pullup and pulldown (but
|
||||
// only if it isn't held).
|
||||
// But it looks like gpio_deep_sleep_wakeup_prepare enables hold for all pins
|
||||
// in the wakeup mask.
|
||||
//
|
||||
// So: all pins in the wake mask should get this treatment here, and all pins
|
||||
// not in the wake mask should get
|
||||
// - pullup and pulldowns disabled
|
||||
// - input and output disabled, and
|
||||
// - their func should get set to GPIO.
|
||||
// But this last block of things gets skipped if hold is disabled globally (see
|
||||
// gpio_ll_deep_sleep_hold_is_en)
|
||||
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::PTR };
|
||||
|
||||
rtc_cntl
|
||||
.cntl_gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_pin_clk_gate().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_, w| w.gpio_wakeup_filter().set_bit());
|
||||
|
||||
if sleep_config.deep_slp() {
|
||||
for (pin, level) in pins.iter_mut() {
|
||||
self.apply_pin(*pin, *level);
|
||||
}
|
||||
|
||||
isolate_digital_gpio();
|
||||
}
|
||||
|
||||
// like rtc_cntl_ll_gpio_clear_wakeup_status, as called from
|
||||
// gpio_deep_sleep_wakeup_prepare
|
||||
rtc_cntl
|
||||
.cntl_gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_wakeup_status_clr().set_bit());
|
||||
rtc_cntl
|
||||
.cntl_gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_wakeup_status_clr().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// 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)]
|
||||
/// RTC Configuration.
|
||||
pub struct RtcConfig(u32);
|
||||
impl Debug;
|
||||
/// Number of rtc_fast_clk cycles to wait for 8M clock to be ready
|
||||
pub u8, ck8m_wait, set_ck8m_wait: 7, 0;
|
||||
/// Number of rtc_fast_clk cycles to wait for XTAL clock to be ready
|
||||
pub u8, xtal_wait, set_xtal_wait: 15, 8;
|
||||
/// Number of rtc_fast_clk cycles to wait for PLL clock to be ready
|
||||
pub u8, pll_wait, set_pll_wait: 23, 16;
|
||||
/// Perform clock control related initialization.
|
||||
pub clkctl_init, set_clkctl_init: 24;
|
||||
/// Perform power control related initialization.
|
||||
pub pwrctl_init, set_pwrctl_init: 25;
|
||||
/// Force power down RTC_DBOOST
|
||||
pub rtc_dboost_fpd, set_rtc_dboost_fpd: 26;
|
||||
/// Keep the XTAL oscillator powered up in sleep.
|
||||
pub xtal_fpu, set_xtal_fpu: 27;
|
||||
/// Keep the BBPLL oscillator powered up in sleep.
|
||||
pub bbpll_fpu, set_bbpll_fpu: 28;
|
||||
/// Enable clock gating when the CPU is in wait-for-interrupt state.
|
||||
pub cpu_waiti_clk_gate, set_cpu_waiti_clk_gate: 29;
|
||||
/// Calibrate Ocode to make bandgap voltage more precise.
|
||||
pub cali_ocode, set_cali_ocode: 30;
|
||||
}
|
||||
|
||||
impl Default for RtcConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_ck8m_wait(RTC_CNTL_CK8M_WAIT_DEFAULT);
|
||||
cfg.set_xtal_wait(RTC_CNTL_XTL_BUF_WAIT_DEFAULT);
|
||||
cfg.set_pll_wait(RTC_CNTL_PLL_BUF_WAIT_DEFAULT);
|
||||
cfg.set_clkctl_init(true);
|
||||
cfg.set_pwrctl_init(true);
|
||||
cfg.set_rtc_dboost_fpd(true);
|
||||
cfg.set_cpu_waiti_clk_gate(true);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
/// Configuration for RTC initialization.
|
||||
pub struct RtcInitConfig(u128);
|
||||
impl Debug;
|
||||
/// Number of cycles required to power up WiFi
|
||||
pub u8, wifi_powerup_cycles, set_wifi_powerup_cycles: 6, 0;
|
||||
/// Number of wait cycles for WiFi to stabilize
|
||||
pub u16, wifi_wait_cycles, set_wifi_wait_cycles: 15, 7;
|
||||
/// Number of cycles required to power up Bluetooth
|
||||
pub u8, bt_powerup_cycles, set_bt_powerup_cycles: 22, 16;
|
||||
/// Number of wait cycles for Bluetooth to stabilize
|
||||
pub u16, bt_wait_cycles, set_bt_wait_cycles: 31, 23;
|
||||
/// Number of cycles required to power up the top CPU
|
||||
pub u8, cpu_top_powerup_cycles, set_cpu_top_powerup_cycles: 38, 32;
|
||||
/// Number of wait cycles for the top CPU to stabilize
|
||||
pub u16, cpu_top_wait_cycles, set_cpu_top_wait_cycles: 47, 39;
|
||||
/// Number of cycles required to power up the digital wrapper
|
||||
pub u8, dg_wrap_powerup_cycles, set_dg_wrap_powerup_cycles: 54, 48;
|
||||
/// Number of wait cycles for the digital wrapper to stabilize
|
||||
pub u16, dg_wrap_wait_cycles, set_dg_wrap_wait_cycles: 63, 55;
|
||||
/// Number of cycles required to power up the digital peripherals
|
||||
pub u8, dg_peri_powerup_cycles, set_dg_peri_powerup_cycles: 70, 64;
|
||||
/// Number of wait cycles for the digital peripherals to stabilize
|
||||
pub u16, dg_peri_wait_cycles, set_dg_peri_wait_cycles: 79, 71;
|
||||
}
|
||||
|
||||
impl Default for RtcInitConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_wifi_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_bt_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_cpu_top_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_dg_wrap_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_dg_peri_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_wifi_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_bt_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_cpu_top_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_dg_wrap_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_dg_peri_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
/// Configuration for RTC sleep mode.
|
||||
pub struct RtcSleepConfig(u64);
|
||||
impl Debug;
|
||||
/// force normal voltage in sleep mode (digital domain memory)
|
||||
pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0;
|
||||
/// keep low voltage in sleep mode (even if ULP/touch is used)
|
||||
pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1;
|
||||
/// power down RTC fast memory
|
||||
pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2;
|
||||
/// power down RTC slow memory
|
||||
pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3;
|
||||
/// power down RTC peripherals
|
||||
pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4;
|
||||
/// power down WiFi
|
||||
pub wifi_pd_en, set_wifi_pd_en: 5;
|
||||
/// power down BT
|
||||
pub bt_pd_en, set_bt_pd_en: 6;
|
||||
/// power down CPU, but not restart when lightsleep.
|
||||
pub cpu_pd_en, set_cpu_pd_en: 7;
|
||||
/// Power down Internal 8M oscillator
|
||||
pub int_8m_pd_en, set_int_8m_pd_en: 8;
|
||||
/// power down digital peripherals
|
||||
pub dig_peri_pd_en, set_dig_peri_pd_en: 9;
|
||||
/// power down digital domain
|
||||
pub deep_slp, set_deep_slp: 10;
|
||||
/// enable WDT flashboot mode
|
||||
pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 11;
|
||||
/// set bias for digital domain, in sleep mode
|
||||
pub u32, dig_dbias_slp, set_dig_dbias_slp: 16, 12;
|
||||
/// set bias for RTC domain, in sleep mode
|
||||
pub u32, rtc_dbias_slp, set_rtc_dbias_slp: 21, 17;
|
||||
/// voltage parameter, in sleep mode
|
||||
pub u8, dbg_atten_slp, set_dbg_atten_slp: 25, 22;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub bias_sleep_monitor, set_bias_sleep_monitor: 26;
|
||||
/// circuit control parameter, in sleep mode
|
||||
pub bias_sleep_slp, set_bias_sleep_slp: 27;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub pd_cur_slp, set_pd_cur_slp: 28;
|
||||
/// power down VDDSDIO regulator
|
||||
pub vddsdio_pd_en, set_vddsdio_pd_en: 29;
|
||||
/// keep main XTAL powered up in sleep
|
||||
pub xtal_fpu, set_xtal_fpu: 30;
|
||||
/// keep rtc regulator powered up in sleep
|
||||
pub rtc_regulator_fpu, set_rtc_regulator_fpu: 31;
|
||||
/// enable deep sleep reject
|
||||
pub deep_slp_reject, set_deep_slp_reject: 32;
|
||||
/// enable light sleep reject
|
||||
pub light_slp_reject, set_light_slp_reject: 33;
|
||||
}
|
||||
|
||||
impl Default for RtcSleepConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
const DR_REG_NRX_BASE: u32 = 0x6001CC00;
|
||||
const DR_REG_FE_BASE: u32 = 0x60006000;
|
||||
const DR_REG_FE2_BASE: u32 = 0x60005000;
|
||||
|
||||
const NRXPD_CTRL: u32 = DR_REG_NRX_BASE + 0x00d4;
|
||||
const FE_GEN_CTRL: u32 = DR_REG_FE_BASE + 0x0090;
|
||||
const FE2_TX_INTERP_CTRL: u32 = DR_REG_FE2_BASE + 0x00f0;
|
||||
|
||||
const SYSCON_SRAM_POWER_UP: u8 = 0x0000000F;
|
||||
const SYSCON_ROM_POWER_UP: u8 = 0x00000003;
|
||||
|
||||
const NRX_RX_ROT_FORCE_PU: u32 = 1 << 5;
|
||||
const NRX_VIT_FORCE_PU: u32 = 1 << 3;
|
||||
const NRX_DEMAP_FORCE_PU: u32 = 1 << 1;
|
||||
|
||||
const FE_IQ_EST_FORCE_PU: u32 = 1 << 5;
|
||||
const FE2_TX_INF_FORCE_PU: u32 = 1 << 10;
|
||||
|
||||
fn modify_register(reg: u32, mask: u32, value: u32) {
|
||||
let reg = reg as *mut u32;
|
||||
|
||||
unsafe { reg.write_volatile((reg.read_volatile() & !mask) | value) };
|
||||
}
|
||||
|
||||
fn register_modify_bits(reg: u32, bits: u32, set: bool) {
|
||||
if set {
|
||||
modify_register(reg, bits, bits);
|
||||
} else {
|
||||
modify_register(reg, bits, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn rtc_sleep_pu(val: bool) {
|
||||
let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() };
|
||||
let syscon = unsafe { &*esp32c2::APB_CTRL::ptr() };
|
||||
let bb = unsafe { &*esp32c2::BB::ptr() };
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().bit(val));
|
||||
|
||||
syscon.front_end_mem_pd().modify(|_r, w| {
|
||||
w.dc_mem_force_pu()
|
||||
.bit(val)
|
||||
.pbus_mem_force_pu()
|
||||
.bit(val)
|
||||
.agc_mem_force_pu()
|
||||
.bit(val)
|
||||
});
|
||||
|
||||
bb.bbpd_ctrl()
|
||||
.modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val));
|
||||
|
||||
register_modify_bits(
|
||||
NRXPD_CTRL,
|
||||
NRX_RX_ROT_FORCE_PU | NRX_VIT_FORCE_PU | NRX_DEMAP_FORCE_PU,
|
||||
val,
|
||||
);
|
||||
|
||||
register_modify_bits(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, val);
|
||||
|
||||
register_modify_bits(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, val);
|
||||
|
||||
syscon.mem_power_up().modify(|_r, w| unsafe {
|
||||
w.sram_power_up()
|
||||
.bits(if val { SYSCON_SRAM_POWER_UP } else { 0 })
|
||||
.rom_power_up()
|
||||
.bits(if val { SYSCON_ROM_POWER_UP } else { 0 })
|
||||
});
|
||||
}
|
||||
|
||||
impl RtcSleepConfig {
|
||||
/// Configures the RTC for deep sleep mode.
|
||||
pub fn deep() -> Self {
|
||||
// Set up for ultra-low power sleep. Wakeup sources may modify these settings.
|
||||
let mut cfg = Self::default();
|
||||
|
||||
cfg.set_lslp_mem_inf_fpu(false);
|
||||
cfg.set_rtc_mem_inf_follow_cpu(true); // ?
|
||||
cfg.set_rtc_fastmem_pd_en(true);
|
||||
cfg.set_rtc_slowmem_pd_en(true);
|
||||
cfg.set_rtc_peri_pd_en(true);
|
||||
cfg.set_wifi_pd_en(true);
|
||||
cfg.set_bt_pd_en(true);
|
||||
cfg.set_cpu_pd_en(true);
|
||||
cfg.set_int_8m_pd_en(true);
|
||||
|
||||
cfg.set_dig_peri_pd_en(true);
|
||||
cfg.set_dig_dbias_slp(0); // because of dig_peri_pd_en
|
||||
|
||||
cfg.set_deep_slp(true);
|
||||
cfg.set_wdt_flashboot_mod_en(false);
|
||||
cfg.set_vddsdio_pd_en(true);
|
||||
cfg.set_xtal_fpu(false);
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
|
||||
// because of dig_peri_pd_en
|
||||
cfg.set_rtc_regulator_fpu(false);
|
||||
cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT);
|
||||
|
||||
// because of xtal_fpu
|
||||
cfg.set_bias_sleep_monitor(true);
|
||||
cfg.set_bias_sleep_slp(true);
|
||||
cfg.set_pd_cur_slp(true);
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
|
||||
let cfg = RtcConfig::default();
|
||||
|
||||
// settings derived from esp_clk_init -> rtc_init
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c2::RTC_CNTL::ptr();
|
||||
let extmem = &*esp32c2::EXTMEM::ptr();
|
||||
let system = &*esp32c2::SYSTEM::ptr();
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
|
||||
rtc_cntl.timer1().modify(|_, w| {
|
||||
w.pll_buf_wait()
|
||||
.bits(cfg.pll_wait())
|
||||
.ck8m_wait()
|
||||
.bits(cfg.ck8m_wait())
|
||||
});
|
||||
|
||||
// Moved from rtc sleep to rtc init to save sleep function running time
|
||||
// set shortest possible sleep time limit
|
||||
|
||||
rtc_cntl
|
||||
.timer5()
|
||||
.modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN));
|
||||
|
||||
// TODO: something about cali_ocode
|
||||
|
||||
// Reset RTC bias to default value (needed if waking up from deep sleep)
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
RTC_CNTL_DBIAS_1V10
|
||||
);
|
||||
|
||||
// LDO dbias initialization
|
||||
// TODO: this modifies g_rtc_dbias_pvt_non_240m and g_dig_dbias_pvt_non_240m.
|
||||
// We're using a high enough default but we should read from the efuse.
|
||||
// set_rtc_dig_dbias();
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
|
||||
if cfg.clkctl_init() {
|
||||
// clear CMMU clock force on
|
||||
|
||||
extmem
|
||||
.cache_mmu_power_ctrl()
|
||||
.modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
|
||||
// clear tag clock force on
|
||||
|
||||
extmem
|
||||
.icache_tag_power_ctrl()
|
||||
.modify(|_, w| w.icache_tag_mem_force_on().clear_bit());
|
||||
// clear register clock force on
|
||||
// clear register clock force on
|
||||
(*esp32c2::SPI0::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
(*esp32c2::SPI1::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
}
|
||||
|
||||
if cfg.pwrctl_init() {
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(cfg.xtal_fpu() || cfg.bbpll_fpu()));
|
||||
|
||||
// cancel bbpll force pu if setting no force power up
|
||||
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bbpll_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
.bbpll_i2c_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
.bb_i2c_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.rtc_cntl()
|
||||
.modify(|_, w| w.regulator_force_pu().clear_bit());
|
||||
|
||||
// If this mask is enabled, all soc memories cannot enter power down mode
|
||||
// We should control soc memory power down mode from RTC, so we will not touch
|
||||
// this register any more
|
||||
|
||||
system
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
// If this pd_cfg is set to 1, all memory won't enter low power mode during
|
||||
// light sleep If this pd_cfg is set to 0, all memory will enter low
|
||||
// power mode during light sleep
|
||||
rtc_sleep_pu(false);
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.dg_wrap_force_noiso().clear_bit());
|
||||
|
||||
// if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0 , the cpu clk will be closed when cpu
|
||||
// enter WAITI mode
|
||||
|
||||
system
|
||||
.cpu_per_conf()
|
||||
.modify(|_, w| w.cpu_wait_mode_force_on().bit(!cfg.cpu_waiti_clk_gate()));
|
||||
|
||||
// cancel digital PADS force no iso
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
rtc_cntl.int_ena().write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply(&self) {
|
||||
// like esp-idf rtc_sleep_init()
|
||||
let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() };
|
||||
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
assert!(!self.pd_cur_slp() || self.bias_sleep_slp());
|
||||
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
self.rtc_dbias_slp()
|
||||
);
|
||||
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_DIG_DREG_SLEEP,
|
||||
self.dig_dbias_slp()
|
||||
);
|
||||
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dbg_atten_monitor()
|
||||
.bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT)
|
||||
// We have config values for this in self, so I don't know why we're setting
|
||||
// hardcoded defaults for these next two. It's what IDF does...
|
||||
.bias_sleep_monitor()
|
||||
.bit(RTC_CNTL_BIASSLP_MONITOR_DEFAULT)
|
||||
.pd_cur_monitor()
|
||||
.bit(RTC_CNTL_PD_CUR_MONITOR_DEFAULT)
|
||||
});
|
||||
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dbg_atten_deep_slp()
|
||||
.bits(self.dbg_atten_slp())
|
||||
.bias_sleep_deep_slp()
|
||||
.bit(self.bias_sleep_slp())
|
||||
.pd_cur_deep_slp()
|
||||
.bit(self.pd_cur_slp())
|
||||
});
|
||||
|
||||
if self.deep_slp() {
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().set_bit());
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.ckgen_i2c_pu()
|
||||
.clear_bit()
|
||||
.pll_i2c_pu()
|
||||
.clear_bit()
|
||||
.rfrx_pbus_pu()
|
||||
.clear_bit()
|
||||
.txrf_i2c_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.bb_i2c_force_pu().clear_bit());
|
||||
} else {
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dg_vdd_drv_b_slp_en()
|
||||
.set_bit()
|
||||
.dg_vdd_drv_b_slp()
|
||||
.bits(RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT)
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
rtc_cntl
|
||||
.rtc_cntl()
|
||||
.modify(|_, w| w.regulator_force_pu().bit(self.rtc_regulator_fpu()));
|
||||
|
||||
rtc_cntl.clk_conf().modify(|_, w| {
|
||||
w.ck8m_force_pu()
|
||||
.bit(!self.int_8m_pd_en())
|
||||
.ck8m_force_nogating()
|
||||
.bit(!self.int_8m_pd_en())
|
||||
});
|
||||
|
||||
// enable VDDSDIO control by state machine
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.vdd_spi_pwr_force()
|
||||
.clear_bit()
|
||||
.vdd_spi_pd_en()
|
||||
.bit(self.vddsdio_pd_en())
|
||||
});
|
||||
|
||||
rtc_cntl.slp_reject_conf().modify(|_, w| {
|
||||
w.deep_slp_reject_en()
|
||||
.bit(self.deep_slp_reject())
|
||||
.light_slp_reject_en()
|
||||
.bit(self.light_slp_reject())
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu()));
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.xtal_global_force_nogating().bit(self.xtal_fpu()));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c2::RTC_CNTL::ptr();
|
||||
|
||||
// set bits for what can wake us up
|
||||
rtc_cntl
|
||||
.wakeup_state()
|
||||
.modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into()));
|
||||
|
||||
rtc_cntl
|
||||
.state0()
|
||||
.write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn finish_sleep(&self) {
|
||||
// In deep sleep mode, we never get here
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c2::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.int_clr().write(|w| {
|
||||
w.slp_reject()
|
||||
.clear_bit_by_one()
|
||||
.slp_wakeup()
|
||||
.clear_bit_by_one()
|
||||
});
|
||||
|
||||
// restore config if it is a light sleep
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+939
@@ -0,0 +1,939 @@
|
||||
use super::{TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel};
|
||||
use crate::{
|
||||
gpio::{RtcFunction, RtcPinWithResistors},
|
||||
regi2c_write_mask,
|
||||
rtc_cntl::{sleep::RtcioWakeupSource, Clock, Rtc, RtcClock},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u32 = 0x6D;
|
||||
const I2C_DIG_REG_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP: u32 = 5;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG: u32 = 6;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP: u32 = 7;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3;
|
||||
|
||||
const I2C_ULP_IR_FORCE_XPD_CK: u8 = 0;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_MSB: u8 = 2;
|
||||
const I2C_ULP_IR_FORCE_XPD_CK_LSB: u8 = 2;
|
||||
|
||||
const I2C_ULP: u8 = 0x61;
|
||||
const I2C_ULP_HOSTID: u8 = 0;
|
||||
|
||||
// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP,
|
||||
// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values.
|
||||
// Valid if RTC_CNTL_DBG_ATTEN is 0.
|
||||
/// Digital bias voltage level of 0.90V.
|
||||
pub const RTC_CNTL_DBIAS_0V90: u32 = 13;
|
||||
/// Digital bias voltage level of 0.95V.
|
||||
pub const RTC_CNTL_DBIAS_0V95: u32 = 16;
|
||||
/// Digital bias voltage level of 1.00V.
|
||||
pub const RTC_CNTL_DBIAS_1V00: u32 = 18;
|
||||
/// Digital bias voltage level of 1.05V.
|
||||
pub const RTC_CNTL_DBIAS_1V05: u32 = 20;
|
||||
/// Digital bias voltage level of 1.10V.
|
||||
pub const RTC_CNTL_DBIAS_1V10: u32 = 23;
|
||||
/// Digital bias voltage level of 1.15V.
|
||||
pub const RTC_CNTL_DBIAS_1V15: u32 = 25;
|
||||
/// Digital bias voltage level of 1.20V.
|
||||
pub const RTC_CNTL_DBIAS_1V20: u32 = 28;
|
||||
/// Digital bias voltage level of 1.25V.
|
||||
pub const RTC_CNTL_DBIAS_1V25: u32 = 30;
|
||||
/// Digital bias voltage level of approximately 1.34V.
|
||||
pub const RTC_CNTL_DBIAS_1V30: u32 = 31;
|
||||
|
||||
/// Default attenuation setting during light sleep, with a voltage drop.
|
||||
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT: u8 = 5;
|
||||
/// No attenuation (no voltage drop) during light sleep.
|
||||
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP: u8 = 0;
|
||||
/// Default attenuation setting during deep sleep, with maximum voltage drop.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15;
|
||||
/// No attenuation (no voltage drop) during deep sleep.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP: u8 = 0;
|
||||
|
||||
/// Default bias setting during sleep mode.
|
||||
pub const RTC_CNTL_BIASSLP_SLEEP_DEFAULT: u8 = 1;
|
||||
/// Keeps the bias for ultra-low power sleep mode always on.
|
||||
pub const RTC_CNTL_BIASSLP_SLEEP_ON: u8 = 0;
|
||||
|
||||
/// Default power-down current setting during sleep mode.
|
||||
pub const RTC_CNTL_PD_CUR_SLEEP_DEFAULT: u8 = 1;
|
||||
/// Keeps the power-down current setting for sleep mode always on.
|
||||
pub const RTC_CNTL_PD_CUR_SLEEP_ON: u8 = 0;
|
||||
|
||||
/// Default driver bias setting for the digital domain during sleep mode.
|
||||
pub const RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT: u8 = 254;
|
||||
|
||||
/// Default debug attenuation setting for the monitor mode.
|
||||
pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0;
|
||||
/// Default bias setting for sleep mode in the monitor mode.
|
||||
pub const RTC_CNTL_BIASSLP_MONITOR_DEFAULT: bool = false;
|
||||
/// Default power-down current setting for the monitor mode.
|
||||
pub const RTC_CNTL_PD_CUR_MONITOR_DEFAULT: bool = false;
|
||||
|
||||
/// Default number of cycles to wait for the PLL buffer to stabilize.
|
||||
pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default number of cycles to wait for the XTL buffer to stabilize.
|
||||
pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100;
|
||||
/// Default number of cycles to wait for the internal 8MHz clock to stabilize.
|
||||
pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default number of cycles required to enable the internal 8MHz clock.
|
||||
pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5;
|
||||
|
||||
/// Minimum number of cycles for sleep duration.
|
||||
pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2;
|
||||
|
||||
/// Power-up cycles for other hardware blocks.
|
||||
pub const OTHER_BLOCKS_POWERUP: u8 = 1;
|
||||
/// Wait cycles for other hardware blocks to stabilize.
|
||||
pub const OTHER_BLOCKS_WAIT: u16 = 1;
|
||||
|
||||
/// Disables GPIO interrupt.
|
||||
pub const GPIO_INTR_DISABLE: u8 = 0;
|
||||
/// Sets GPIO interrupt to trigger on a low level signal.
|
||||
pub const GPIO_INTR_LOW_LEVEL: u8 = 4;
|
||||
/// Sets GPIO interrupt to trigger on a high level signal.
|
||||
pub const GPIO_INTR_HIGH_LEVEL: u8 = 5;
|
||||
|
||||
/// Specifies the function configuration for GPIO pins.
|
||||
pub const PIN_FUNC_GPIO: u8 = 1;
|
||||
/// Index for signaling GPIO output.
|
||||
pub const SIG_GPIO_OUT_IDX: u32 = 128;
|
||||
/// Maximum number of GPIO pins supported.
|
||||
pub const GPIO_NUM_MAX: usize = 22;
|
||||
|
||||
impl WakeSource for TimerWakeupSource {
|
||||
fn apply(
|
||||
&self,
|
||||
rtc: &Rtc<'_>,
|
||||
triggers: &mut WakeTriggers,
|
||||
_sleep_config: &mut RtcSleepConfig,
|
||||
) {
|
||||
triggers.set_timer(true);
|
||||
let rtc_cntl = unsafe { &*esp32c3::RTC_CNTL::ptr() };
|
||||
let clock_freq = RtcClock::slow_freq();
|
||||
// TODO: maybe add sleep time adjustlemnt like idf
|
||||
// TODO: maybe add check to prevent overflow?
|
||||
let clock_hz = clock_freq.frequency().to_Hz() as u64;
|
||||
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
|
||||
// "alarm" time in slow rtc ticks
|
||||
let now = rtc.time_since_boot_raw();
|
||||
let time_in_ticks = now + ticks;
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.slp_timer0()
|
||||
.write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32));
|
||||
|
||||
rtc_cntl
|
||||
.int_clr()
|
||||
.write(|w| w.main_timer().clear_bit_by_one());
|
||||
|
||||
rtc_cntl.slp_timer1().write(|w| {
|
||||
w.slp_val_hi()
|
||||
.bits(((time_in_ticks >> 32) & 0xffff) as u16)
|
||||
.main_timer_alarm_en()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RtcioWakeupSource<'_, '_> {
|
||||
fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) {
|
||||
// The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare
|
||||
let level = match level {
|
||||
WakeupLevel::High => {
|
||||
pin.rtcio_pullup(false);
|
||||
pin.rtcio_pulldown(true);
|
||||
GPIO_INTR_HIGH_LEVEL
|
||||
}
|
||||
WakeupLevel::Low => {
|
||||
pin.rtcio_pullup(true);
|
||||
pin.rtcio_pulldown(false);
|
||||
GPIO_INTR_LOW_LEVEL
|
||||
}
|
||||
};
|
||||
pin.rtcio_pad_hold(true);
|
||||
|
||||
// apply_wakeup does the same as idf's esp_deep_sleep_enable_gpio_wakeup
|
||||
unsafe {
|
||||
pin.apply_wakeup(true, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn isolate_digital_gpio() {
|
||||
// like esp_sleep_isolate_digital_gpio
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
let io_mux = unsafe { &*crate::peripherals::IO_MUX::ptr() };
|
||||
let gpio = unsafe { &*crate::peripherals::GPIO::ptr() };
|
||||
|
||||
let dig_iso = &rtc_cntl.dig_iso().read();
|
||||
let deep_sleep_hold_is_en =
|
||||
!dig_iso.dg_pad_force_unhold().bit() && dig_iso.dg_pad_autohold_en().bit();
|
||||
if !deep_sleep_hold_is_en {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: assert that the task stack is not in external ram
|
||||
|
||||
for pin_num in 0..GPIO_NUM_MAX {
|
||||
let pin_hold = rtc_cntl.dig_pad_hold().read().bits() & (1 << pin_num) != 0;
|
||||
if !pin_hold {
|
||||
// input disable, like gpio_ll_input_disable
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_ie().clear_bit());
|
||||
// output disable, like gpio_ll_output_disable
|
||||
unsafe {
|
||||
gpio.func_out_sel_cfg(pin_num)
|
||||
.modify(|_, w| w.bits(SIG_GPIO_OUT_IDX));
|
||||
}
|
||||
|
||||
// disable pull-up and pull-down
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_wpu().clear_bit());
|
||||
io_mux.gpio(pin_num).modify(|_, w| w.fun_wpd().clear_bit());
|
||||
|
||||
// make pad work as gpio (otherwise, deep_sleep bottom current will rise)
|
||||
io_mux
|
||||
.gpio(pin_num)
|
||||
.modify(|_, w| unsafe { w.mcu_sel().bits(RtcFunction::Digital 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;
|
||||
}
|
||||
|
||||
triggers.set_gpio(true);
|
||||
|
||||
// If deep sleep is enabled, esp_start_sleep calls
|
||||
// gpio_deep_sleep_wakeup_prepare which sets these pullup and
|
||||
// pulldown values. But later in esp_start_sleep it calls
|
||||
// esp_sleep_isolate_digital_gpio, which disables the pullup and pulldown (but
|
||||
// only if it isn't held).
|
||||
// But it looks like gpio_deep_sleep_wakeup_prepare enables hold for all pins
|
||||
// in the wakeup mask.
|
||||
//
|
||||
// So: all pins in the wake mask should get this treatment here, and all pins
|
||||
// not in the wake mask should get
|
||||
// - pullup and pulldowns disabled
|
||||
// - input and output disabled, and
|
||||
// - their func should get set to GPIO.
|
||||
// But this last block of things gets skipped if hold is disabled globally (see
|
||||
// gpio_ll_deep_sleep_hold_is_en)
|
||||
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::PTR };
|
||||
|
||||
rtc_cntl
|
||||
.gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_pin_clk_gate().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_, w| w.gpio_wakeup_filter().set_bit());
|
||||
|
||||
if sleep_config.deep_slp() {
|
||||
for (pin, level) in pins.iter_mut() {
|
||||
self.apply_pin(*pin, *level);
|
||||
}
|
||||
|
||||
isolate_digital_gpio();
|
||||
}
|
||||
|
||||
// like rtc_cntl_ll_gpio_clear_wakeup_status, as called from
|
||||
// gpio_deep_sleep_wakeup_prepare
|
||||
rtc_cntl
|
||||
.gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_wakeup_status_clr().set_bit());
|
||||
rtc_cntl
|
||||
.gpio_wakeup()
|
||||
.modify(|_, w| w.gpio_wakeup_status_clr().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// 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)]
|
||||
/// RTC Configuration.
|
||||
pub struct RtcConfig(u32);
|
||||
impl Debug;
|
||||
/// Number of rtc_fast_clk cycles to wait for 8M clock to be ready
|
||||
pub u8, ck8m_wait, set_ck8m_wait: 7, 0;
|
||||
/// Number of rtc_fast_clk cycles to wait for XTAL clock to be ready
|
||||
pub u8, xtal_wait, set_xtal_wait: 15, 8;
|
||||
/// Number of rtc_fast_clk cycles to wait for PLL clock to be ready
|
||||
pub u8, pll_wait, set_pll_wait: 23, 16;
|
||||
/// Perform clock control related initialization.
|
||||
pub clkctl_init, set_clkctl_init: 24;
|
||||
/// Perform power control related initialization.
|
||||
pub pwrctl_init, set_pwrctl_init: 25;
|
||||
/// Force power down `RTC_DBOOST`.
|
||||
pub rtc_dboost_fpd, set_rtc_dboost_fpd: 26;
|
||||
/// Keep the XTAL oscillator powered up in sleep.
|
||||
pub xtal_fpu, set_xtal_fpu: 27;
|
||||
/// Keep the BBPLL oscillator powered up in sleep.
|
||||
pub bbpll_fpu, set_bbpll_fpu: 28;
|
||||
/// Enable clock gating when the CPU is in wait-for-interrupt state.
|
||||
pub cpu_waiti_clk_gate, set_cpu_waiti_clk_gate: 29;
|
||||
/// Calibrate Ocode to make bandgap voltage more precise.
|
||||
pub cali_ocode, set_cali_ocode: 30;
|
||||
}
|
||||
|
||||
impl Default for RtcConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_ck8m_wait(RTC_CNTL_CK8M_WAIT_DEFAULT);
|
||||
cfg.set_xtal_wait(RTC_CNTL_XTL_BUF_WAIT_DEFAULT);
|
||||
cfg.set_pll_wait(RTC_CNTL_PLL_BUF_WAIT_DEFAULT);
|
||||
cfg.set_clkctl_init(true);
|
||||
cfg.set_pwrctl_init(true);
|
||||
cfg.set_rtc_dboost_fpd(true);
|
||||
cfg.set_cpu_waiti_clk_gate(true);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
/// Configuration for RTC initialization.
|
||||
pub struct RtcInitConfig(u128);
|
||||
impl Debug;
|
||||
/// Number of cycles required to power up WiFi
|
||||
pub u8, wifi_powerup_cycles, set_wifi_powerup_cycles: 6, 0;
|
||||
/// Number of wait cycles for WiFi to stabilize
|
||||
pub u16, wifi_wait_cycles, set_wifi_wait_cycles: 15, 7;
|
||||
/// Number of cycles required to power up Bluetooth
|
||||
pub u8, bt_powerup_cycles, set_bt_powerup_cycles: 22, 16;
|
||||
/// Number of wait cycles for Bluetooth to stabilize
|
||||
pub u16, bt_wait_cycles, set_bt_wait_cycles: 31, 23;
|
||||
/// Number of cycles required to power up the top CPU
|
||||
pub u8, cpu_top_powerup_cycles, set_cpu_top_powerup_cycles: 38, 32;
|
||||
/// Number of wait cycles for the top CPU to stabilize
|
||||
pub u16, cpu_top_wait_cycles, set_cpu_top_wait_cycles: 47, 39;
|
||||
/// Number of cycles required to power up the digital wrapper
|
||||
pub u8, dg_wrap_powerup_cycles, set_dg_wrap_powerup_cycles: 54, 48;
|
||||
/// Number of wait cycles for the digital wrapper to stabilize
|
||||
pub u16, dg_wrap_wait_cycles, set_dg_wrap_wait_cycles: 63, 55;
|
||||
/// Number of cycles required to power up the digital peripherals
|
||||
pub u8, dg_peri_powerup_cycles, set_dg_peri_powerup_cycles: 70, 64;
|
||||
/// Number of wait cycles for the digital peripherals to stabilize
|
||||
pub u16, dg_peri_wait_cycles, set_dg_peri_wait_cycles: 79, 71;
|
||||
}
|
||||
|
||||
impl Default for RtcInitConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_wifi_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_bt_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_cpu_top_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_dg_wrap_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_dg_peri_powerup_cycles(OTHER_BLOCKS_POWERUP);
|
||||
cfg.set_wifi_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_bt_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_cpu_top_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_dg_wrap_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg.set_dg_peri_wait_cycles(OTHER_BLOCKS_WAIT);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
/// Configuration for RTC sleep mode.
|
||||
pub struct RtcSleepConfig(u64);
|
||||
impl Debug;
|
||||
/// force normal voltage in sleep mode (digital domain memory)
|
||||
pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0;
|
||||
/// keep low voltage in sleep mode (even if ULP/touch is used)
|
||||
pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1;
|
||||
/// power down RTC fast memory
|
||||
pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2;
|
||||
/// power down RTC slow memory
|
||||
pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3;
|
||||
/// power down RTC peripherals
|
||||
pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4;
|
||||
/// power down WiFi
|
||||
pub wifi_pd_en, set_wifi_pd_en: 5;
|
||||
/// power down BT
|
||||
pub bt_pd_en, set_bt_pd_en: 6;
|
||||
/// power down CPU, but not restart when lightsleep.
|
||||
pub cpu_pd_en, set_cpu_pd_en: 7;
|
||||
/// Power down Internal 8M oscillator
|
||||
pub int_8m_pd_en, set_int_8m_pd_en: 8;
|
||||
/// power down digital peripherals
|
||||
pub dig_peri_pd_en, set_dig_peri_pd_en: 9;
|
||||
/// power down digital domain
|
||||
pub deep_slp, set_deep_slp: 10;
|
||||
/// enable WDT flashboot mode
|
||||
pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 11;
|
||||
/// set bias for digital domain, in sleep mode
|
||||
pub u32, dig_dbias_slp, set_dig_dbias_slp: 16, 12;
|
||||
/// set bias for RTC domain, in sleep mode
|
||||
pub u32, rtc_dbias_slp, set_rtc_dbias_slp: 21, 17;
|
||||
/// voltage parameter, in sleep mode
|
||||
pub u8, dbg_atten_slp, set_dbg_atten_slp: 25, 22;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub bias_sleep_monitor, set_bias_sleep_monitor: 26;
|
||||
/// circuit control parameter, in sleep mode
|
||||
pub bias_sleep_slp, set_bias_sleep_slp: 27;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub pd_cur_slp, set_pd_cur_slp: 28;
|
||||
/// power down VDDSDIO regulator
|
||||
pub vddsdio_pd_en, set_vddsdio_pd_en: 29;
|
||||
/// keep main XTAL powered up in sleep
|
||||
pub xtal_fpu, set_xtal_fpu: 30;
|
||||
/// keep rtc regulator powered up in sleep
|
||||
pub rtc_regulator_fpu, set_rtc_regulator_fpu: 31;
|
||||
/// enable deep sleep reject
|
||||
pub deep_slp_reject, set_deep_slp_reject: 32;
|
||||
/// enable light sleep reject
|
||||
pub light_slp_reject, set_light_slp_reject: 33;
|
||||
}
|
||||
|
||||
impl Default for RtcSleepConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
const SYSCON_SRAM_POWER_UP: u8 = 0x0000000F;
|
||||
const SYSCON_ROM_POWER_UP: u8 = 0x00000003;
|
||||
|
||||
fn rtc_sleep_pu(val: bool) {
|
||||
let rtc_cntl = unsafe { &*esp32c3::RTC_CNTL::ptr() };
|
||||
let syscon = unsafe { &*esp32c3::APB_CTRL::ptr() };
|
||||
let bb = unsafe { &*esp32c3::BB::ptr() };
|
||||
let nrx = unsafe { &*esp32c3::NRX::ptr() };
|
||||
let fe = unsafe { &*esp32c3::FE::ptr() };
|
||||
let fe2 = unsafe { &*esp32c3::FE2::ptr() };
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().bit(val).fastmem_force_lpu().bit(val));
|
||||
|
||||
syscon.front_end_mem_pd().modify(|_r, w| {
|
||||
w.dc_mem_force_pu()
|
||||
.bit(val)
|
||||
.pbus_mem_force_pu()
|
||||
.bit(val)
|
||||
.agc_mem_force_pu()
|
||||
.bit(val)
|
||||
});
|
||||
|
||||
bb.bbpd_ctrl()
|
||||
.modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val));
|
||||
|
||||
nrx.nrxpd_ctrl().modify(|_, w| {
|
||||
w.rx_rot_force_pu()
|
||||
.bit(val)
|
||||
.vit_force_pu()
|
||||
.bit(val)
|
||||
.demap_force_pu()
|
||||
.bit(val)
|
||||
});
|
||||
|
||||
fe.gen_ctrl().modify(|_, w| w.iq_est_force_pu().bit(val));
|
||||
|
||||
fe2.tx_interp_ctrl()
|
||||
.modify(|_, w| w.tx_inf_force_pu().bit(val));
|
||||
|
||||
syscon.mem_power_up().modify(|_r, w| unsafe {
|
||||
w.sram_power_up()
|
||||
.bits(if val { SYSCON_SRAM_POWER_UP } else { 0 })
|
||||
.rom_power_up()
|
||||
.bits(if val { SYSCON_ROM_POWER_UP } else { 0 })
|
||||
});
|
||||
}
|
||||
|
||||
impl RtcSleepConfig {
|
||||
/// Configures the RTC for deep sleep mode.
|
||||
pub fn deep() -> Self {
|
||||
// Set up for ultra-low power sleep. Wakeup sources may modify these settings.
|
||||
let mut cfg = Self::default();
|
||||
|
||||
cfg.set_lslp_mem_inf_fpu(false);
|
||||
cfg.set_rtc_mem_inf_follow_cpu(true); // ?
|
||||
cfg.set_rtc_fastmem_pd_en(true);
|
||||
cfg.set_rtc_slowmem_pd_en(true);
|
||||
cfg.set_rtc_peri_pd_en(true);
|
||||
cfg.set_wifi_pd_en(true);
|
||||
cfg.set_bt_pd_en(true);
|
||||
cfg.set_cpu_pd_en(true);
|
||||
cfg.set_int_8m_pd_en(true);
|
||||
|
||||
cfg.set_dig_peri_pd_en(true);
|
||||
cfg.set_dig_dbias_slp(0); // because of dig_peri_pd_en
|
||||
|
||||
cfg.set_deep_slp(true);
|
||||
cfg.set_wdt_flashboot_mod_en(false);
|
||||
cfg.set_vddsdio_pd_en(true);
|
||||
cfg.set_xtal_fpu(false);
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
|
||||
// because of dig_peri_pd_en
|
||||
cfg.set_rtc_regulator_fpu(false);
|
||||
cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT);
|
||||
|
||||
// because of xtal_fpu
|
||||
cfg.set_bias_sleep_monitor(true);
|
||||
cfg.set_bias_sleep_slp(true);
|
||||
cfg.set_pd_cur_slp(true);
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
|
||||
let cfg = RtcConfig::default();
|
||||
|
||||
// settings derived from esp_clk_init -> rtc_init
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c3::RTC_CNTL::ptr();
|
||||
let extmem = &*esp32c3::EXTMEM::ptr();
|
||||
let system = &*esp32c3::SYSTEM::ptr();
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pd().clear_bit());
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| w.pvtmon_pu().clear_bit());
|
||||
|
||||
rtc_cntl.timer1().modify(|_, w| {
|
||||
w.pll_buf_wait()
|
||||
.bits(cfg.pll_wait())
|
||||
.ck8m_wait()
|
||||
.bits(cfg.ck8m_wait())
|
||||
});
|
||||
|
||||
// Moved from rtc sleep to rtc init to save sleep function running time
|
||||
// set shortest possible sleep time limit
|
||||
|
||||
rtc_cntl
|
||||
.timer5()
|
||||
.modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN));
|
||||
|
||||
let init_cfg = RtcInitConfig::default();
|
||||
|
||||
rtc_cntl.timer3().modify(|_, w| {
|
||||
w
|
||||
// set wifi timer
|
||||
.wifi_powerup_timer()
|
||||
.bits(init_cfg.wifi_powerup_cycles())
|
||||
.wifi_wait_timer()
|
||||
.bits(init_cfg.wifi_wait_cycles())
|
||||
// set bt timer
|
||||
.bt_powerup_timer()
|
||||
.bits(init_cfg.bt_powerup_cycles())
|
||||
.bt_wait_timer()
|
||||
.bits(init_cfg.bt_wait_cycles())
|
||||
});
|
||||
|
||||
rtc_cntl.timer4().modify(|_, w| {
|
||||
w.cpu_top_powerup_timer()
|
||||
.bits(init_cfg.cpu_top_powerup_cycles())
|
||||
.cpu_top_wait_timer()
|
||||
.bits(init_cfg.cpu_top_wait_cycles())
|
||||
// set digital wrap timer
|
||||
.dg_wrap_powerup_timer()
|
||||
.bits(init_cfg.dg_wrap_powerup_cycles())
|
||||
.dg_wrap_wait_timer()
|
||||
.bits(init_cfg.dg_wrap_wait_cycles())
|
||||
});
|
||||
|
||||
rtc_cntl.timer6().modify(|_, w| {
|
||||
w.dg_peri_powerup_timer()
|
||||
.bits(init_cfg.dg_peri_powerup_cycles())
|
||||
.dg_peri_wait_timer()
|
||||
.bits(init_cfg.dg_peri_wait_cycles())
|
||||
});
|
||||
|
||||
// TODO: something about cali_ocode
|
||||
|
||||
// Reset RTC bias to default value (needed if waking up from deep sleep)
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
RTC_CNTL_DBIAS_1V10
|
||||
);
|
||||
|
||||
// LDO dbias initialization
|
||||
// TODO: this modifies g_rtc_dbias_pvt_non_240m and g_dig_dbias_pvt_non_240m.
|
||||
// We're using a high enough default but we should read from the efuse.
|
||||
// set_rtc_dig_dbias();
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
|
||||
if cfg.clkctl_init() {
|
||||
// clear CMMU clock force on
|
||||
|
||||
extmem
|
||||
.cache_mmu_power_ctrl()
|
||||
.modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
|
||||
// clear tag clock force on
|
||||
|
||||
extmem
|
||||
.icache_tag_power_ctrl()
|
||||
.modify(|_, w| w.icache_tag_mem_force_on().clear_bit());
|
||||
// clear register clock force on
|
||||
// clear register clock force on
|
||||
(*esp32c3::SPI0::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
(*esp32c3::SPI1::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
}
|
||||
|
||||
if cfg.pwrctl_init() {
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(cfg.xtal_fpu() || cfg.bbpll_fpu()));
|
||||
|
||||
// force pd APLL
|
||||
|
||||
rtc_cntl
|
||||
.ana_conf()
|
||||
.modify(|_, w| w.plla_force_pu().clear_bit().plla_force_pd().set_bit());
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w
|
||||
// open sar_i2c protect function to avoid sar_i2c reset when rtc_ldo is low.
|
||||
.reset_por_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// cancel bbpll force pu if setting no force power up
|
||||
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bbpll_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
.bbpll_i2c_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
.bb_i2c_force_pu()
|
||||
.bit(cfg.bbpll_fpu())
|
||||
});
|
||||
|
||||
rtc_cntl.rtc_cntl().modify(|_, w| {
|
||||
w.regulator_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pd()
|
||||
.bit(cfg.rtc_dboost_fpd())
|
||||
});
|
||||
|
||||
// If this mask is enabled, all soc memories cannot enter power down mode
|
||||
// We should control soc memory power down mode from RTC, so we will not touch
|
||||
// this register any more
|
||||
|
||||
system
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
// If this pd_cfg is set to 1, all memory won't enter low power mode during
|
||||
// light sleep If this pd_cfg is set to 0, all memory will enter low
|
||||
// power mode during light sleep
|
||||
rtc_sleep_pu(false);
|
||||
|
||||
rtc_cntl.dig_pwc().modify(|_, w| {
|
||||
w.dg_wrap_force_pu()
|
||||
.clear_bit()
|
||||
.wifi_force_pu()
|
||||
.clear_bit()
|
||||
.bt_force_pu()
|
||||
.clear_bit()
|
||||
.cpu_top_force_pu()
|
||||
.clear_bit()
|
||||
.dg_peri_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_wrap_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.bt_force_noiso()
|
||||
.clear_bit()
|
||||
.cpu_top_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_peri_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0 , the cpu clk will be closed when cpu
|
||||
// enter WAITI mode
|
||||
|
||||
system
|
||||
.cpu_per_conf()
|
||||
.modify(|_, w| w.cpu_wait_mode_force_on().bit(!cfg.cpu_waiti_clk_gate()));
|
||||
|
||||
// cancel digital PADS force no iso
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
// force power down modem(wifi and ble) power domain
|
||||
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.wifi_force_iso().set_bit().bt_force_iso().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pd().set_bit().bt_force_pd().set_bit());
|
||||
|
||||
rtc_cntl.int_ena().write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply(&self) {
|
||||
// like esp-idf rtc_sleep_init()
|
||||
let rtc_cntl = unsafe { &*esp32c3::RTC_CNTL::ptr() };
|
||||
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
|
||||
if self.wifi_pd_en() {
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pu().clear_bit().wifi_pd_en().set_bit());
|
||||
} else {
|
||||
rtc_cntl.dig_pwc().modify(|_, w| w.wifi_pd_en().clear_bit());
|
||||
}
|
||||
if self.bt_pd_en() {
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.bt_force_noiso().clear_bit().bt_force_iso().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.bt_force_pu().clear_bit().bt_pd_en().set_bit());
|
||||
} else {
|
||||
rtc_cntl.dig_pwc().modify(|_, w| w.bt_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.cpu_top_pd_en().bit(self.cpu_pd_en()));
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_peri_pd_en().bit(self.dig_peri_pd_en()));
|
||||
|
||||
unsafe {
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dbg_atten_monitor()
|
||||
.bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT)
|
||||
// We have config values for this in self, so I don't know why we're setting
|
||||
// hardcoded defaults for these next two. It's what IDF does...
|
||||
.bias_sleep_monitor()
|
||||
.bit(RTC_CNTL_BIASSLP_MONITOR_DEFAULT)
|
||||
.pd_cur_monitor()
|
||||
.bit(RTC_CNTL_PD_CUR_MONITOR_DEFAULT)
|
||||
});
|
||||
|
||||
assert!(!self.pd_cur_slp() || self.bias_sleep_slp());
|
||||
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
self.rtc_dbias_slp()
|
||||
);
|
||||
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_DIG_DREG_SLEEP,
|
||||
self.dig_dbias_slp()
|
||||
);
|
||||
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dbg_atten_deep_slp()
|
||||
.bits(self.dbg_atten_slp())
|
||||
.bias_sleep_deep_slp()
|
||||
.bit(self.bias_sleep_slp())
|
||||
.pd_cur_deep_slp()
|
||||
.bit(self.pd_cur_slp())
|
||||
});
|
||||
|
||||
if self.deep_slp() {
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().set_bit());
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.ckgen_i2c_pu()
|
||||
.clear_bit()
|
||||
.pll_i2c_pu()
|
||||
.clear_bit()
|
||||
.rfrx_pbus_pu()
|
||||
.clear_bit()
|
||||
.txrf_i2c_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.bb_i2c_force_pu().clear_bit());
|
||||
} else {
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dg_vdd_drv_b_slp_en()
|
||||
.set_bit()
|
||||
.dg_vdd_drv_b_slp()
|
||||
.bits(RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT)
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
// mem force pu
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.rtc_cntl()
|
||||
.modify(|_, w| w.regulator_force_pu().bit(self.rtc_regulator_fpu()));
|
||||
|
||||
rtc_cntl.clk_conf().modify(|_, w| {
|
||||
w.ck8m_force_pu()
|
||||
.bit(!self.int_8m_pd_en())
|
||||
.ck8m_force_nogating()
|
||||
.bit(!self.int_8m_pd_en())
|
||||
});
|
||||
|
||||
// enable VDDSDIO control by state machine
|
||||
|
||||
rtc_cntl.sdio_conf().modify(|_, w| {
|
||||
w.sdio_force()
|
||||
.clear_bit()
|
||||
.sdio_reg_pd_en()
|
||||
.bit(self.vddsdio_pd_en())
|
||||
});
|
||||
|
||||
rtc_cntl.slp_reject_conf().modify(|_, w| {
|
||||
w.deep_slp_reject_en()
|
||||
.bit(self.deep_slp_reject())
|
||||
.light_slp_reject_en()
|
||||
.bit(self.light_slp_reject())
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu()));
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.xtal_global_force_nogating().bit(self.xtal_fpu()));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c3::RTC_CNTL::ptr();
|
||||
|
||||
// set bits for what can wake us up
|
||||
rtc_cntl
|
||||
.wakeup_state()
|
||||
.modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into()));
|
||||
|
||||
rtc_cntl
|
||||
.state0()
|
||||
.write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn finish_sleep(&self) {
|
||||
// In deep sleep mode, we never get here
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32c3::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.int_clr().write(|w| {
|
||||
w.slp_reject()
|
||||
.clear_bit_by_one()
|
||||
.slp_wakeup()
|
||||
.clear_bit_by_one()
|
||||
});
|
||||
|
||||
// restore config if it is a light sleep
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1065
File diff suppressed because it is too large.
Load diff
+947
@@ -0,0 +1,947 @@
|
||||
use super::{
|
||||
Ext0WakeupSource,
|
||||
Ext1WakeupSource,
|
||||
TimerWakeupSource,
|
||||
WakeSource,
|
||||
WakeTriggers,
|
||||
WakeupLevel,
|
||||
};
|
||||
use crate::{
|
||||
gpio::{RtcFunction, RtcPin},
|
||||
regi2c_write_mask,
|
||||
rtc_cntl::{sleep::RtcioWakeupSource, Clock, Rtc, RtcClock},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u32 = 0x6d;
|
||||
const I2C_DIG_REG_HOSTID: u32 = 1;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP: u32 = 5;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG: u32 = 6;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP: u32 = 7;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_MSB: u32 = 4;
|
||||
const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_LSB: u32 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u32 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3;
|
||||
|
||||
// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP,
|
||||
// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values.
|
||||
// Valid if RTC_CNTL_DBG_ATTEN is 0.
|
||||
/// Digital bias setting for 0.90V.
|
||||
pub const RTC_CNTL_DBIAS_0V90: u32 = 13;
|
||||
/// Digital bias setting for 0.95V.
|
||||
pub const RTC_CNTL_DBIAS_0V95: u32 = 16;
|
||||
/// Digital bias setting for 1.00V.
|
||||
pub const RTC_CNTL_DBIAS_1V00: u32 = 18;
|
||||
/// Digital bias setting for 1.05V.
|
||||
pub const RTC_CNTL_DBIAS_1V05: u32 = 20;
|
||||
/// Digital bias setting for 1.10V.
|
||||
pub const RTC_CNTL_DBIAS_1V10: u32 = 23;
|
||||
/// Digital bias setting for 1.15V.
|
||||
pub const RTC_CNTL_DBIAS_1V15: u32 = 25;
|
||||
/// Digital bias setting for 1.20V.
|
||||
pub const RTC_CNTL_DBIAS_1V20: u32 = 28;
|
||||
/// Digital bias setting for 1.25V.
|
||||
pub const RTC_CNTL_DBIAS_1V25: u32 = 30;
|
||||
/// Digital bias setting for 1.30V. Voltage is approximately 1.34V in practice.
|
||||
pub const RTC_CNTL_DBIAS_1V30: u32 = 31;
|
||||
/// Default monitor debug attenuation value.
|
||||
pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0;
|
||||
/// ULP co-processor touch start wait time during sleep, set to maximum.
|
||||
pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_IN_SLEEP: u16 = 0xFF;
|
||||
/// ULP co-processor touch start wait time default value.
|
||||
pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT: u16 = 0x10;
|
||||
/// Default wait time for PLL buffer during startup.
|
||||
pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20;
|
||||
/// Default wait time for CK8M during startup.
|
||||
pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20;
|
||||
/// Minimum sleep value.
|
||||
pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2;
|
||||
/// Deep sleep debug attenuation setting for ultra-low power mode.
|
||||
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_ULTRA_LOW: u8 = 15;
|
||||
/// Power-up setting for other blocks.
|
||||
pub const OTHER_BLOCKS_POWERUP: u8 = 1;
|
||||
/// Wait cycles for other blocks.
|
||||
pub const OTHER_BLOCKS_WAIT: u16 = 1;
|
||||
/// WiFi power-up cycles.
|
||||
pub const WIFI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// WiFi wait cycles.
|
||||
pub const WIFI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// Bluetooth power-up cycles.
|
||||
pub const BT_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// Bluetooth wait cycles.
|
||||
pub const BT_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// RTC power-up cycles.
|
||||
pub const RTC_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// RTC wait cycles.
|
||||
pub const RTC_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// CPU top power-up cycles.
|
||||
pub const CPU_TOP_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// CPU top wait cycles.
|
||||
pub const CPU_TOP_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// DG wrap power-up cycles.
|
||||
pub const DG_WRAP_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// DG wrap wait cycles.
|
||||
pub const DG_WRAP_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// DG peripheral power-up cycles.
|
||||
pub const DG_PERI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// DG peripheral wait cycles.
|
||||
pub const DG_PERI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
/// RTC memory power-up cycles.
|
||||
pub const RTC_MEM_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP;
|
||||
/// RTC memory wait cycles.
|
||||
pub const RTC_MEM_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT;
|
||||
|
||||
impl WakeSource for TimerWakeupSource {
|
||||
fn apply(
|
||||
&self,
|
||||
rtc: &Rtc<'_>,
|
||||
triggers: &mut WakeTriggers,
|
||||
_sleep_config: &mut RtcSleepConfig,
|
||||
) {
|
||||
triggers.set_timer(true);
|
||||
let rtc_cntl = unsafe { &*esp32s3::RTC_CNTL::ptr() };
|
||||
let clock_freq = RtcClock::slow_freq();
|
||||
// TODO: maybe add sleep time adjustlemnt like idf
|
||||
// TODO: maybe add check to prevent overflow?
|
||||
let clock_hz = clock_freq.frequency().to_Hz() as u64;
|
||||
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
|
||||
// "alarm" time in slow rtc ticks
|
||||
let now = rtc.time_since_boot_raw();
|
||||
let time_in_ticks = now + ticks;
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
.slp_timer0()
|
||||
.write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32));
|
||||
|
||||
rtc_cntl
|
||||
.int_clr()
|
||||
.write(|w| w.main_timer().clear_bit_by_one());
|
||||
|
||||
rtc_cntl.slp_timer1().write(|w| {
|
||||
w.slp_val_hi()
|
||||
.bits(((time_in_ticks >> 32) & 0xffff) as u16)
|
||||
.main_timer_alarm_en()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
triggers.set_ext0(true);
|
||||
|
||||
// set pin to RTC function
|
||||
self.pin
|
||||
.borrow_mut()
|
||||
.rtc_set_config(true, true, RtcFunction::Rtc);
|
||||
|
||||
unsafe {
|
||||
let rtc_io = &*esp32s3::RTC_IO::ptr();
|
||||
// set pin register field
|
||||
rtc_io
|
||||
.ext_wakeup0()
|
||||
.modify(|_, w| w.sel().bits(self.pin.borrow().rtc_number()));
|
||||
// set level register field
|
||||
let rtc_cntl = &*esp32s3::RTC_CNTL::ptr();
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_r, w| w.ext_wakeup0_lv().bit(self.level == WakeupLevel::High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// to IO_MUX)
|
||||
self.pin
|
||||
.borrow_mut()
|
||||
.rtc_set_config(true, false, RtcFunction::Rtc);
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeSource for Ext1WakeupSource<'_, '_> {
|
||||
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);
|
||||
triggers.set_ext1(true);
|
||||
|
||||
// set pins to RTC function
|
||||
let mut pins = self.pins.borrow_mut();
|
||||
let mut bits = 0u32;
|
||||
for pin in pins.iter_mut() {
|
||||
pin.rtc_set_config(true, true, RtcFunction::Rtc);
|
||||
bits |= 1 << pin.rtc_number();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32s3::RTC_CNTL::ptr();
|
||||
// clear previous wakeup status
|
||||
rtc_cntl
|
||||
.ext_wakeup1()
|
||||
.modify(|_, w| w.ext_wakeup1_status_clr().set_bit());
|
||||
// set pin register field
|
||||
rtc_cntl
|
||||
.ext_wakeup1()
|
||||
.modify(|_, w| w.ext_wakeup1_sel().bits(bits));
|
||||
// set level register field
|
||||
rtc_cntl
|
||||
.ext_wakeup_conf()
|
||||
.modify(|_r, w| w.ext_wakeup1_lv().bit(self.level == WakeupLevel::High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ext1WakeupSource<'_, '_> {
|
||||
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 in pins.iter_mut() {
|
||||
pin.rtc_set_config(true, false, RtcFunction::Rtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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| unsafe {
|
||||
w.wakeup_enable().set_bit().int_type().bits(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! {
|
||||
/// Configuration for the RTC sleep behavior.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RtcSleepConfig(u64);
|
||||
impl Debug;
|
||||
/// force normal voltage in sleep mode (digital domain memory)
|
||||
pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0;
|
||||
/// keep low voltage in sleep mode (even if ULP/touch is used)
|
||||
pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1;
|
||||
/// power down RTC fast memory
|
||||
pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2;
|
||||
/// power down RTC slow memory
|
||||
pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3;
|
||||
/// power down RTC peripherals
|
||||
pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4;
|
||||
/// power down Modem(wifi and ble)
|
||||
pub modem_pd_en, set_modem_pd_en: 5;
|
||||
/// power down CPU, but not restart when lightsleep.
|
||||
pub cpu_pd_en, set_cpu_pd_en: 6;
|
||||
/// Power down Internal 8M oscillator
|
||||
pub int_8m_pd_en, set_int_8m_pd_en: 7;
|
||||
/// power down digital peripherals
|
||||
pub dig_peri_pd_en, set_dig_peri_pd_en: 8;
|
||||
/// power down digital domain
|
||||
pub deep_slp, set_deep_slp: 9;
|
||||
/// enable WDT flashboot mode
|
||||
pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 10;
|
||||
/// set bias for digital domain, in sleep mode
|
||||
pub u32, dig_dbias_slp, set_dig_dbias_slp: 15, 11;
|
||||
/// set bias for RTC domain, in sleep mode
|
||||
pub u32, rtc_dbias_slp, set_rtc_dbias_slp: 20, 16;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub bias_sleep_monitor, set_bias_sleep_monitor: 21;
|
||||
/// voltage parameter, in sleep mode
|
||||
pub u8, dbg_atten_slp, set_dbg_atten_slp: 25, 22;
|
||||
/// circuit control parameter, in sleep mode
|
||||
pub bias_sleep_slp, set_bias_sleep_slp: 26;
|
||||
/// circuit control parameter, in monitor mode
|
||||
pub pd_cur_monitor, set_pd_cur_monitor: 27;
|
||||
/// circuit control parameter, in sleep mode
|
||||
pub pd_cur_slp, set_pd_cur_slp: 28;
|
||||
/// power down VDDSDIO regulator
|
||||
pub vddsdio_pd_en, set_vddsdio_pd_en: 29;
|
||||
/// keep main XTAL powered up in sleep
|
||||
pub xtal_fpu, set_xtal_fpu: 30;
|
||||
/// keep rtc regulator powered up in sleep
|
||||
pub rtc_regulator_fpu, set_rtc_regulator_fpu: 31;
|
||||
/// enable deep sleep reject
|
||||
pub deep_slp_reject, set_deep_slp_reject: 32;
|
||||
/// enable light sleep reject
|
||||
pub light_slp_reject, set_light_slp_reject: 33;
|
||||
}
|
||||
|
||||
impl Default for RtcSleepConfig {
|
||||
fn default() -> Self {
|
||||
let mut cfg = Self(Default::default());
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
const SYSCON_SRAM_POWER_UP: u16 = 0x7FF;
|
||||
const SYSCON_ROM_POWER_UP: u8 = 0x7;
|
||||
|
||||
fn rtc_sleep_pu(val: bool) {
|
||||
let rtc_cntl = unsafe { &*esp32s3::RTC_CNTL::ptr() };
|
||||
let syscon = unsafe { &*esp32s3::APB_CTRL::ptr() };
|
||||
let bb = unsafe { &*esp32s3::BB::ptr() };
|
||||
let nrx = unsafe { &*esp32s3::NRX::ptr() };
|
||||
let fe = unsafe { &*esp32s3::FE::ptr() };
|
||||
let fe2 = unsafe { &*esp32s3::FE2::ptr() };
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().bit(val));
|
||||
|
||||
rtc_cntl
|
||||
.pwc()
|
||||
.modify(|_, w| w.slowmem_force_lpu().bit(val).fastmem_force_lpu().bit(val));
|
||||
|
||||
syscon.front_end_mem_pd().modify(|_r, w| {
|
||||
w.dc_mem_force_pu()
|
||||
.bit(val)
|
||||
.pbus_mem_force_pu()
|
||||
.bit(val)
|
||||
.agc_mem_force_pu()
|
||||
.bit(val)
|
||||
});
|
||||
|
||||
bb.bbpd_ctrl()
|
||||
.modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val));
|
||||
|
||||
nrx.nrxpd_ctrl().modify(|_, w| {
|
||||
w.rx_rot_force_pu()
|
||||
.bit(val)
|
||||
.vit_force_pu()
|
||||
.bit(val)
|
||||
.demap_force_pu()
|
||||
.bit(val)
|
||||
});
|
||||
|
||||
fe.gen_ctrl().modify(|_, w| w.iq_est_force_pu().bit(val));
|
||||
|
||||
fe2.tx_interp_ctrl()
|
||||
.modify(|_, w| w.tx_inf_force_pu().bit(val));
|
||||
|
||||
syscon.mem_power_up().modify(|_r, w| unsafe {
|
||||
w.sram_power_up()
|
||||
.bits(if val { SYSCON_SRAM_POWER_UP } else { 0 })
|
||||
.rom_power_up()
|
||||
.bits(if val { SYSCON_ROM_POWER_UP } else { 0 })
|
||||
});
|
||||
}
|
||||
|
||||
impl RtcSleepConfig {
|
||||
/// Configures the RTC for deep sleep mode.
|
||||
pub fn deep() -> Self {
|
||||
// Set up for ultra-low power sleep. Wakeup sources may modify these settings.
|
||||
let mut cfg = Self::default();
|
||||
|
||||
cfg.set_lslp_mem_inf_fpu(false);
|
||||
cfg.set_rtc_mem_inf_follow_cpu(true); // ?
|
||||
cfg.set_rtc_fastmem_pd_en(true);
|
||||
cfg.set_rtc_slowmem_pd_en(true);
|
||||
cfg.set_rtc_peri_pd_en(true);
|
||||
cfg.set_modem_pd_en(true);
|
||||
cfg.set_cpu_pd_en(true);
|
||||
cfg.set_int_8m_pd_en(true);
|
||||
|
||||
cfg.set_dig_peri_pd_en(true);
|
||||
cfg.set_dig_dbias_slp(0); // because of dig_peri_pd_en
|
||||
|
||||
cfg.set_deep_slp(true);
|
||||
cfg.set_wdt_flashboot_mod_en(false);
|
||||
cfg.set_vddsdio_pd_en(true);
|
||||
cfg.set_xtal_fpu(false);
|
||||
cfg.set_deep_slp_reject(true);
|
||||
cfg.set_light_slp_reject(true);
|
||||
cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
|
||||
|
||||
// because of dig_peri_pd_en
|
||||
cfg.set_rtc_regulator_fpu(false);
|
||||
cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_ULTRA_LOW);
|
||||
|
||||
// because of xtal_fpu
|
||||
cfg.set_bias_sleep_monitor(true);
|
||||
cfg.set_pd_cur_monitor(true);
|
||||
cfg.set_bias_sleep_slp(true);
|
||||
cfg.set_pd_cur_slp(true);
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
|
||||
// settings derived from esp_clk_init -> rtc_init
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32s3::RTC_CNTL::ptr();
|
||||
let syscon = &*esp32s3::APB_CTRL::ptr();
|
||||
let extmem = &*esp32s3::EXTMEM::ptr();
|
||||
let system = &*esp32s3::SYSTEM::ptr();
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pd().clear_bit());
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| w.pvtmon_pu().clear_bit());
|
||||
|
||||
rtc_cntl.timer1().modify(|_, w| {
|
||||
w.pll_buf_wait()
|
||||
.bits(RTC_CNTL_PLL_BUF_WAIT_DEFAULT)
|
||||
.ck8m_wait()
|
||||
.bits(RTC_CNTL_CK8M_WAIT_DEFAULT)
|
||||
});
|
||||
|
||||
// Moved from rtc sleep to rtc init to save sleep function running time
|
||||
// set shortest possible sleep time limit
|
||||
|
||||
rtc_cntl
|
||||
.timer5()
|
||||
.modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN));
|
||||
|
||||
rtc_cntl.timer3().modify(|_, w| {
|
||||
w
|
||||
// set wifi timer
|
||||
.wifi_powerup_timer()
|
||||
.bits(WIFI_POWERUP_CYCLES)
|
||||
.wifi_wait_timer()
|
||||
.bits(WIFI_WAIT_CYCLES)
|
||||
// set bt timer
|
||||
.bt_powerup_timer()
|
||||
.bits(BT_POWERUP_CYCLES)
|
||||
.bt_wait_timer()
|
||||
.bits(BT_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl.timer6().modify(|_, w| {
|
||||
w.cpu_top_powerup_timer()
|
||||
.bits(CPU_TOP_POWERUP_CYCLES)
|
||||
.cpu_top_wait_timer()
|
||||
.bits(CPU_TOP_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl.timer4().modify(|_, w| {
|
||||
w
|
||||
// set rtc peri timer
|
||||
.powerup_timer()
|
||||
.bits(RTC_POWERUP_CYCLES)
|
||||
.wait_timer()
|
||||
.bits(RTC_WAIT_CYCLES)
|
||||
// set digital wrap timer
|
||||
.dg_wrap_powerup_timer()
|
||||
.bits(DG_WRAP_POWERUP_CYCLES)
|
||||
.dg_wrap_wait_timer()
|
||||
.bits(DG_WRAP_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
rtc_cntl.timer6().modify(|_, w| {
|
||||
w.dg_peri_powerup_timer()
|
||||
.bits(DG_PERI_POWERUP_CYCLES)
|
||||
.dg_peri_wait_timer()
|
||||
.bits(DG_PERI_WAIT_CYCLES)
|
||||
});
|
||||
|
||||
// Reset RTC bias to default value (needed if waking up from deep sleep)
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
RTC_CNTL_DBIAS_1V10
|
||||
);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, RTC_CNTL_DBIAS_1V10);
|
||||
|
||||
// Set the wait time to the default value.
|
||||
|
||||
rtc_cntl.timer2().modify(|_, w| {
|
||||
w.ulpcp_touch_start_wait()
|
||||
.bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT)
|
||||
});
|
||||
|
||||
// LDO dbias initialization
|
||||
// TODO: this modifies g_rtc_dbias_pvt_non_240m and g_dig_dbias_pvt_non_240m.
|
||||
// We're using a high enough default but we should read from the efuse.
|
||||
// rtc_set_stored_dbias();
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, RTC_CNTL_DBIAS_1V25);
|
||||
|
||||
// clear CMMU clock force on
|
||||
|
||||
extmem
|
||||
.cache_mmu_power_ctrl()
|
||||
.modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
|
||||
|
||||
// clear clkgate force on
|
||||
syscon.clkgate_force_on().write(|w| w.bits(0));
|
||||
|
||||
// clear tag clock force on
|
||||
|
||||
extmem
|
||||
.dcache_tag_power_ctrl()
|
||||
.modify(|_, w| w.dcache_tag_mem_force_on().clear_bit());
|
||||
|
||||
extmem
|
||||
.icache_tag_power_ctrl()
|
||||
.modify(|_, w| w.icache_tag_mem_force_on().clear_bit());
|
||||
|
||||
// clear register clock force on
|
||||
(*esp32s3::SPI0::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
(*esp32s3::SPI1::ptr())
|
||||
.clock_gate()
|
||||
.modify(|_, w| w.clk_en().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w
|
||||
// open sar_i2c protect function to avoid sar_i2c reset when rtc_ldo is low.
|
||||
// clear i2c_reset_protect pd force, need tested in low temperature.
|
||||
// NOTE: this bit is written again in esp-idf, but it's not clear why.
|
||||
.i2c_reset_por_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// cancel bbpll force pu if setting no force power up
|
||||
|
||||
rtc_cntl.options0().modify(|_, w| {
|
||||
w.bbpll_force_pu()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pu()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// cancel RTC REG force PU
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| w.force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.rtc().modify(|_, w| {
|
||||
w.regulator_force_pu()
|
||||
.clear_bit()
|
||||
.dboost_force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.slowmem_force_noiso()
|
||||
.clear_bit()
|
||||
.fastmem_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.rtc().modify(|_, w| w.dboost_force_pd().set_bit());
|
||||
|
||||
// If this mask is enabled, all soc memories cannot enter power down mode
|
||||
// We should control soc memory power down mode from RTC, so we will not touch
|
||||
// this register any more
|
||||
|
||||
system
|
||||
.mem_pd_mask()
|
||||
.modify(|_, w| w.lslp_mem_pd_mask().clear_bit());
|
||||
|
||||
// If this pd_cfg is set to 1, all memory won't enter low power mode during
|
||||
// light sleep If this pd_cfg is set to 0, all memory will enter low
|
||||
// power mode during light sleep
|
||||
rtc_sleep_pu(false);
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_wrap_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_wrap_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.bt_force_noiso().clear_bit().bt_force_iso().clear_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.bt_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.cpu_top_force_noiso()
|
||||
.clear_bit()
|
||||
.cpu_top_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.cpu_top_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_peri_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_peri_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_peri_force_pu().clear_bit());
|
||||
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.force_noiso()
|
||||
.clear_bit()
|
||||
.force_iso()
|
||||
.clear_bit()
|
||||
.force_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0,
|
||||
// the cpu clk will be closed when cpu enter WAITI mode
|
||||
|
||||
system
|
||||
.cpu_per_conf()
|
||||
.modify(|_, w| w.cpu_wait_mode_force_on().clear_bit());
|
||||
|
||||
// cancel digital PADS force no iso
|
||||
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_pad_force_unhold()
|
||||
.clear_bit()
|
||||
.dg_pad_force_noiso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// force power down modem(wifi and ble) power domain
|
||||
|
||||
rtc_cntl
|
||||
.dig_iso()
|
||||
.modify(|_, w| w.wifi_force_iso().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pd().set_bit());
|
||||
|
||||
rtc_cntl.int_ena().write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply(&self) {
|
||||
// like esp-idf rtc_sleep_init()
|
||||
let rtc_cntl = unsafe { &*esp32s3::RTC_CNTL::ptr() };
|
||||
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
|
||||
if self.modem_pd_en() {
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.wifi_force_noiso()
|
||||
.clear_bit()
|
||||
.wifi_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.wifi_force_pu().clear_bit().wifi_pd_en().set_bit());
|
||||
} else {
|
||||
rtc_cntl.dig_pwc().modify(|_, w| w.wifi_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
if self.cpu_pd_en() {
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.cpu_top_force_noiso()
|
||||
.clear_bit()
|
||||
.cpu_top_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.cpu_top_force_pu().clear_bit().cpu_top_pd_en().set_bit());
|
||||
} else {
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.cpu_top_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
if self.dig_peri_pd_en() {
|
||||
rtc_cntl.dig_iso().modify(|_, w| {
|
||||
w.dg_peri_force_noiso()
|
||||
.clear_bit()
|
||||
.dg_peri_force_iso()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_peri_force_pu().clear_bit().dg_peri_pd_en().set_bit());
|
||||
} else {
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_peri_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
if self.rtc_peri_pd_en() {
|
||||
rtc_cntl.pwc().modify(|_, w| {
|
||||
w.force_noiso()
|
||||
.clear_bit()
|
||||
.force_iso()
|
||||
.clear_bit()
|
||||
.force_pu()
|
||||
.clear_bit()
|
||||
.pd_en()
|
||||
.set_bit()
|
||||
});
|
||||
} else {
|
||||
rtc_cntl.pwc().modify(|_, w| w.pd_en().clear_bit());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_RTC_DREG_SLEEP,
|
||||
self.rtc_dbias_slp()
|
||||
);
|
||||
|
||||
regi2c_write_mask!(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_EXT_DIG_DREG_SLEEP,
|
||||
self.dig_dbias_slp()
|
||||
);
|
||||
|
||||
rtc_cntl.bias_conf().modify(|_, w| {
|
||||
w.dbg_atten_deep_slp()
|
||||
.bits(self.dbg_atten_slp())
|
||||
.bias_sleep_deep_slp()
|
||||
.bit(self.bias_sleep_slp())
|
||||
.pd_cur_deep_slp()
|
||||
.bit(self.pd_cur_slp())
|
||||
.dbg_atten_monitor()
|
||||
.bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT)
|
||||
.bias_sleep_monitor()
|
||||
.bit(self.bias_sleep_monitor())
|
||||
.pd_cur_monitor()
|
||||
.bit(self.pd_cur_monitor())
|
||||
});
|
||||
|
||||
if self.deep_slp() {
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().set_bit());
|
||||
|
||||
rtc_cntl.ana_conf().modify(|_, w| {
|
||||
w.ckgen_i2c_pu()
|
||||
.clear_bit()
|
||||
.pll_i2c_pu()
|
||||
.clear_bit()
|
||||
.rfrx_pbus_pu()
|
||||
.clear_bit()
|
||||
.txrf_i2c_pu()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.bb_i2c_force_pu().clear_bit());
|
||||
} else {
|
||||
rtc_cntl
|
||||
.regulator_drv_ctrl()
|
||||
.modify(|_, w| w.dg_vdd_drv_b_slp().bits(0xF));
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.dg_wrap_pd_en().clear_bit());
|
||||
}
|
||||
|
||||
// mem force pu
|
||||
|
||||
rtc_cntl
|
||||
.dig_pwc()
|
||||
.modify(|_, w| w.lslp_mem_force_pu().set_bit());
|
||||
|
||||
rtc_cntl
|
||||
.rtc()
|
||||
.modify(|_, w| w.regulator_force_pu().bit(self.rtc_regulator_fpu()));
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.ck8m_force_pu().bit(!self.int_8m_pd_en()));
|
||||
|
||||
// enable VDDSDIO control by state machine
|
||||
|
||||
rtc_cntl.sdio_conf().modify(|_, w| {
|
||||
w.sdio_force()
|
||||
.clear_bit()
|
||||
.sdio_reg_pd_en()
|
||||
.bit(self.vddsdio_pd_en())
|
||||
});
|
||||
|
||||
rtc_cntl.slp_reject_conf().modify(|_, w| {
|
||||
w.deep_slp_reject_en()
|
||||
.bit(self.deep_slp_reject())
|
||||
.light_slp_reject_en()
|
||||
.bit(self.light_slp_reject())
|
||||
});
|
||||
|
||||
// Set wait cycle for touch or COCPU after deep sleep and light
|
||||
// sleep.
|
||||
|
||||
rtc_cntl.timer2().modify(|_, w| {
|
||||
w.ulpcp_touch_start_wait()
|
||||
.bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_IN_SLEEP)
|
||||
});
|
||||
|
||||
rtc_cntl
|
||||
.options0()
|
||||
.modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu()));
|
||||
|
||||
rtc_cntl
|
||||
.clk_conf()
|
||||
.modify(|_, w| w.xtal_global_force_nogating().bit(self.xtal_fpu()));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32s3::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl
|
||||
.reset_state()
|
||||
.modify(|_, w| w.procpu_stat_vector_sel().set_bit());
|
||||
|
||||
// set bits for what can wake us up
|
||||
rtc_cntl
|
||||
.wakeup_state()
|
||||
.modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into()));
|
||||
|
||||
rtc_cntl
|
||||
.state0()
|
||||
.write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn finish_sleep(&self) {
|
||||
// In deep sleep mode, we never get here
|
||||
unsafe {
|
||||
let rtc_cntl = &*esp32s3::RTC_CNTL::ptr();
|
||||
|
||||
rtc_cntl.int_clr().write(|w| {
|
||||
w.slp_reject()
|
||||
.clear_bit_by_one()
|
||||
.slp_wakeup()
|
||||
.clear_bit_by_one()
|
||||
});
|
||||
|
||||
// restore config if it is a light sleep
|
||||
if self.lslp_mem_inf_fpu() {
|
||||
rtc_sleep_pu(true);
|
||||
}
|
||||
|
||||
// Recover default wait cycle for touch or COCPU after wakeup.
|
||||
|
||||
rtc_cntl.timer2().modify(|_, w| {
|
||||
w.ulpcp_touch_start_wait()
|
||||
.bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
//! # RTC Control Sleep Module
|
||||
//!
|
||||
//! ## Overview
|
||||
//! The `sleep` module allows configuring various wakeup sources and setting up
|
||||
//! the sleep behavior based on those sources. The supported wakeup sources
|
||||
//! include:
|
||||
//! * `GPIO` pins - light sleep only
|
||||
//! * timers
|
||||
//! * `SDIO (Secure Digital Input/Output) - light sleep only`
|
||||
//! * `MAC (Media Access Control)` wake - light sleep only
|
||||
//! * `UART0` - light sleep only
|
||||
//! * `UART1` - light sleep only
|
||||
//! * `touch`
|
||||
//! * `ULP (Ultra-Low Power)` wake
|
||||
//! * `BT (Bluetooth) wake` - light sleep only
|
||||
|
||||
use core::cell::RefCell;
|
||||
#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))]
|
||||
use core::time::Duration;
|
||||
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
use crate::gpio::RtcPin as RtcIoWakeupPinType;
|
||||
#[cfg(any(esp32c3, esp32c6, esp32c2))]
|
||||
use crate::gpio::RtcPinWithResistors as RtcIoWakeupPinType;
|
||||
use crate::rtc_cntl::Rtc;
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
use crate::{
|
||||
into_ref,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
};
|
||||
|
||||
#[cfg_attr(esp32, path = "esp32.rs")]
|
||||
#[cfg_attr(esp32s3, path = "esp32s3.rs")]
|
||||
#[cfg_attr(esp32c3, path = "esp32c3.rs")]
|
||||
#[cfg_attr(esp32c6, path = "esp32c6.rs")]
|
||||
#[cfg_attr(esp32c2, path = "esp32c2.rs")]
|
||||
mod sleep_impl;
|
||||
|
||||
pub use sleep_impl::*;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
/// Level at which a wake-up event is triggered
|
||||
pub enum WakeupLevel {
|
||||
/// The wake-up event is triggered when the pin is low.
|
||||
Low,
|
||||
#[default]
|
||||
/// The wake-up event is triggered when the pin is high.
|
||||
High,
|
||||
}
|
||||
|
||||
/// Represents a timer wake-up source, triggering an event after a specified
|
||||
/// duration.
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))]
|
||||
pub struct TimerWakeupSource {
|
||||
/// The duration after which the wake-up event is triggered.
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))]
|
||||
impl TimerWakeupSource {
|
||||
/// Creates a new timer wake-up source with the specified duration.
|
||||
pub fn new(duration: Duration) -> Self {
|
||||
Self { duration }
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when configuring RTC wake-up sources.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// The selected pin is not a valid RTC pin.
|
||||
NotRtcPin,
|
||||
/// The maximum number of wake-up sources has been exceeded.
|
||||
TooManyWakeupSources,
|
||||
}
|
||||
|
||||
/// External wake-up source (Ext0).
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
pub struct Ext0WakeupSource<'a, P: RtcIoWakeupPinType> {
|
||||
/// The pin used as the wake-up source.
|
||||
pin: RefCell<PeripheralRef<'a, P>>,
|
||||
/// The level at which the wake-up event is triggered.
|
||||
level: WakeupLevel,
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
impl<'a, P: RtcIoWakeupPinType> Ext0WakeupSource<'a, P> {
|
||||
/// Creates a new external wake-up source (Ext0``) with the specified pin
|
||||
/// and wake-up level.
|
||||
pub fn new(pin: impl Peripheral<P = P> + 'a, level: WakeupLevel) -> Self {
|
||||
into_ref!(pin);
|
||||
Self {
|
||||
pin: RefCell::new(pin),
|
||||
level,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// External wake-up source (Ext1).
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
pub struct Ext1WakeupSource<'a, 'b> {
|
||||
/// A collection of pins used as wake-up sources.
|
||||
pins: RefCell<&'a mut [&'b mut dyn RtcIoWakeupPinType]>,
|
||||
/// The level at which the wake-up event is triggered across all pins.
|
||||
level: WakeupLevel,
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
impl<'a, 'b> Ext1WakeupSource<'a, 'b> {
|
||||
/// Creates a new external wake-up source (Ext1) with the specified pins and
|
||||
/// wake-up level.
|
||||
pub fn new(pins: &'a mut [&'b mut dyn RtcIoWakeupPinType], level: WakeupLevel) -> Self {
|
||||
Self {
|
||||
pins: RefCell::new(pins),
|
||||
level,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// External wake-up source (Ext1).
|
||||
#[cfg(esp32c6)]
|
||||
pub struct Ext1WakeupSource<'a, 'b> {
|
||||
pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>,
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
impl<'a, 'b> Ext1WakeupSource<'a, 'b> {
|
||||
/// Creates a new external wake-up source (Ext1) with the specified pins and
|
||||
/// wake-up level.
|
||||
pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self {
|
||||
Self {
|
||||
pins: RefCell::new(pins),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[cfg(any(esp32c3, esp32s3, esp32c2))]
|
||||
pub struct RtcioWakeupSource<'a, 'b> {
|
||||
pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>,
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c3, esp32s3, esp32c2))]
|
||||
impl<'a, 'b> RtcioWakeupSource<'a, 'b> {
|
||||
/// Creates a new external wake-up source (Ext1).
|
||||
pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self {
|
||||
Self {
|
||||
pins: RefCell::new(pins),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LP Core wakeup source
|
||||
///
|
||||
/// Wake up from LP core. This wakeup source
|
||||
/// can be used to wake up from both light and deep sleep.
|
||||
#[cfg(esp32c6)]
|
||||
pub struct WakeFromLpCoreWakeupSource {}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
impl WakeFromLpCoreWakeupSource {
|
||||
/// Create a new instance of `WakeFromLpCoreWakeupSource`
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
impl Default for WakeFromLpCoreWakeupSource {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// GPIO wakeup source
|
||||
///
|
||||
/// Wake up from GPIO high or low level. Any pin can be used with this wake up
|
||||
/// source. Configure the pin for wake up via
|
||||
/// [crate::gpio::Input::wakeup_enable].
|
||||
///
|
||||
/// This wakeup source can be used to wake up from light sleep only.
|
||||
pub struct GpioWakeupSource {}
|
||||
|
||||
impl GpioWakeupSource {
|
||||
/// Create a new instance of [GpioWakeupSource]
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GpioWakeupSource {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeSource for GpioWakeupSource {
|
||||
fn apply(
|
||||
&self,
|
||||
_rtc: &Rtc<'_>,
|
||||
triggers: &mut WakeTriggers,
|
||||
_sleep_config: &mut RtcSleepConfig,
|
||||
) {
|
||||
triggers.set_gpio(true);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! uart_wakeup_impl {
|
||||
($num:literal) => {
|
||||
paste::paste! {
|
||||
#[doc = concat!("UART", $num, " wakeup source")]
|
||||
///
|
||||
/// The chip can be woken up by reverting RXD for multiple cycles until the
|
||||
/// number of rising edges is equal to or greater than the given value.
|
||||
///
|
||||
/// Note that the character which triggers wakeup (and any characters before
|
||||
/// it) will not be received by the UART after wakeup. This means that the
|
||||
/// external device typically needs to send an extra character to trigger
|
||||
/// wakeup before sending the data.
|
||||
///
|
||||
/// After waking-up from UART, you should send some extra data through the UART
|
||||
/// port in Active mode, so that the internal wakeup indication signal can be
|
||||
/// cleared. Otherwise, the next UART wake-up would trigger with two less
|
||||
/// rising edges than the configured threshold value.
|
||||
///
|
||||
/// Wakeup from light sleep takes some time, so not every character sent to the
|
||||
/// UART can be received by the application.
|
||||
///
|
||||
/// This wakeup source can be used to wake up from light sleep only.
|
||||
pub struct [< Uart $num WakeupSource >] {
|
||||
threshold: u16,
|
||||
}
|
||||
|
||||
impl [< Uart $num WakeupSource >] {
|
||||
#[doc = concat!("Create a new instance of UART", $num, " wakeup source>") ]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `threshold` is out of bounds.
|
||||
pub fn new(threshold: u16) -> Self {
|
||||
if threshold > 1023 {
|
||||
panic!("Invalid threshold");
|
||||
}
|
||||
Self { threshold }
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeSource for [< Uart $num WakeupSource >] {
|
||||
fn apply(&self, _rtc: &Rtc<'_>, triggers: &mut WakeTriggers, _sleep_config: &mut RtcSleepConfig) {
|
||||
triggers.[< set_uart $num >](true);
|
||||
let uart = unsafe { crate::peripherals::[< UART $num >]::steal() };
|
||||
|
||||
#[cfg(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3))]
|
||||
uart.sleep_conf()
|
||||
.modify(|_, w| unsafe { w.active_threshold().bits(self.threshold) });
|
||||
|
||||
#[cfg(not(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3)))]
|
||||
uart.sleep_conf2().modify(|_, w| unsafe {
|
||||
w.wk_mode_sel()
|
||||
.bits(0)
|
||||
.active_threshold()
|
||||
.bits(self.threshold)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
uart_wakeup_impl!(0);
|
||||
uart_wakeup_impl!(1);
|
||||
|
||||
#[cfg(not(pmu))]
|
||||
bitfield::bitfield! {
|
||||
/// Represents the wakeup triggers.
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct WakeTriggers(u16);
|
||||
impl Debug;
|
||||
/// EXT0 GPIO wakeup
|
||||
pub ext0, set_ext0: 0;
|
||||
/// EXT1 GPIO wakeup
|
||||
pub ext1, set_ext1: 1;
|
||||
/// GPIO wakeup (light sleep only)
|
||||
pub gpio, set_gpio: 2;
|
||||
/// Timer wakeup
|
||||
pub timer, set_timer: 3;
|
||||
/// SDIO wakeup (light sleep only)
|
||||
pub sdio, set_sdio: 4;
|
||||
/// MAC wakeup (light sleep only)
|
||||
pub mac, set_mac: 5;
|
||||
/// UART0 wakeup (light sleep only)
|
||||
pub uart0, set_uart0: 6;
|
||||
/// UART1 wakeup (light sleep only)
|
||||
pub uart1, set_uart1: 7;
|
||||
/// Touch wakeup
|
||||
pub touch, set_touch: 8;
|
||||
/// ULP wakeup
|
||||
pub ulp, set_ulp: 9;
|
||||
/// BT wakeup (light sleep only)
|
||||
pub bt, set_bt: 10;
|
||||
}
|
||||
|
||||
#[cfg(pmu)]
|
||||
bitfield::bitfield! {
|
||||
/// Represents the wakeup triggers.
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct WakeTriggers(u16);
|
||||
impl Debug;
|
||||
|
||||
/// EXT0 GPIO wakeup
|
||||
pub ext0, set_ext0: 0;
|
||||
/// EXT1 GPIO wakeup
|
||||
pub ext1, set_ext1: 1;
|
||||
/// GPIO wakeup
|
||||
pub gpio, set_gpio: 2;
|
||||
/// WiFi beacon wakeup
|
||||
pub wifi_beacon, set_wifi_beacon: 3;
|
||||
/// Timer wakeup
|
||||
pub timer, set_timer: 4;
|
||||
/// WiFi SoC wakeup
|
||||
pub wifi_soc, set_wifi_soc: 5;
|
||||
/// UART0 wakeup
|
||||
pub uart0, set_uart0: 6;
|
||||
/// UART1 wakeup
|
||||
pub uart1, set_uart1: 7;
|
||||
/// SDIO wakeup
|
||||
pub sdio, set_sdio: 8;
|
||||
/// BT wakeup
|
||||
pub bt, set_bt: 10;
|
||||
/// LP core wakeup
|
||||
pub lp_core, set_lp_core: 11;
|
||||
/// USB wakeup
|
||||
pub usb, set_usb: 14;
|
||||
}
|
||||
|
||||
/// Trait representing a wakeup source.
|
||||
pub trait WakeSource {
|
||||
/// Configures the RTC and applies the wakeup triggers.
|
||||
fn apply(&self, rtc: &Rtc<'_>, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig);
|
||||
}
|
||||
Reference in new issue
Block a user