#![doc = crate::before_snippet!()]
use chrono::{DateTime, NaiveDateTime};
#[cfg(not(any(esp32c6, esp32h2)))]
use fugit::HertzU32;
use fugit::MicrosDurationU64;
pub use self::rtc::SocResetReason;
#[cfg(not(any(esp32c6, esp32h2)))]
use crate::clock::XtalClock;
#[cfg(not(esp32))]
use crate::efuse::Efuse;
#[cfg(not(any(esp32c6, esp32h2)))]
use crate::peripherals::{LPWR, TIMG0};
#[cfg(any(esp32c6, esp32h2))]
use crate::peripherals::{LP_AON, LP_TIMER, LP_WDT};
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers};
use crate::{
clock::Clock,
interrupt::{self, InterruptHandler},
peripheral::{Peripheral, PeripheralRef},
peripherals::Interrupt,
reset::{SleepSource, WakeupReason},
Cpu,
InterruptConfigurable,
};
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
pub mod sleep;
#[cfg_attr(esp32, path = "rtc/esp32.rs")]
#[cfg_attr(esp32c2, path = "rtc/esp32c2.rs")]
#[cfg_attr(esp32c3, path = "rtc/esp32c3.rs")]
#[cfg_attr(esp32c6, path = "rtc/esp32c6.rs")]
#[cfg_attr(esp32h2, path = "rtc/esp32h2.rs")]
#[cfg_attr(esp32s2, path = "rtc/esp32s2.rs")]
#[cfg_attr(esp32s3, path = "rtc/esp32s3.rs")]
pub(crate) mod rtc;
#[cfg(any(esp32c6, esp32h2))]
unsafe fn lp_aon<'a>() -> &'a <LP_AON as core::ops::Deref>::Target {
&*LP_AON::PTR
}
#[cfg(not(any(esp32c6, esp32h2)))]
unsafe fn lp_aon<'a>() -> &'a <LPWR as core::ops::Deref>::Target {
&*LPWR::PTR
}
#[cfg(any(esp32c6, esp32h2))]
unsafe fn lp_timer<'a>() -> &'a <LP_TIMER as core::ops::Deref>::Target {
&*LP_TIMER::PTR
}
#[cfg(not(any(esp32c6, esp32h2)))]
unsafe fn lp_timer<'a>() -> &'a <LPWR as core::ops::Deref>::Target {
&*LPWR::PTR
}
#[cfg(any(esp32c6, esp32h2))]
unsafe fn lp_wdt<'a>() -> &'a <LP_WDT as core::ops::Deref>::Target {
&*LP_WDT::PTR
}
#[cfg(not(any(esp32c6, esp32h2)))]
unsafe fn lp_wdt<'a>() -> &'a <LPWR as core::ops::Deref>::Target {
&*LPWR::PTR
}
#[cfg(not(any(esp32c6, esp32h2)))]
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum RtcFastClock {
RtcFastClockXtalD4 = 0,
RtcFastClock8m = 1,
}
#[cfg(not(any(esp32c6, esp32h2)))]
impl Clock for RtcFastClock {
fn frequency(&self) -> HertzU32 {
match self {
RtcFastClock::RtcFastClockXtalD4 => HertzU32::Hz(40_000_000 / 4),
#[cfg(any(esp32, esp32s2))]
RtcFastClock::RtcFastClock8m => HertzU32::Hz(8_500_000),
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
RtcFastClock::RtcFastClock8m => HertzU32::Hz(17_500_000),
}
}
}
#[cfg(not(any(esp32c6, esp32h2)))]
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum RtcSlowClock {
RtcSlowClockRtc = 0,
RtcSlowClock32kXtal = 1,
RtcSlowClock8mD256 = 2,
}
#[cfg(not(any(esp32c6, esp32h2)))]
impl Clock for RtcSlowClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(esp32)]
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(150_000),
#[cfg(esp32s2)]
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(90_000),
#[cfg(any(esp32c2, esp32c3, esp32s3))]
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(136_000),
RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768),
#[cfg(any(esp32, esp32s2))]
RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(8_500_000 / 256),
#[cfg(any(esp32c2, esp32c3, esp32s3))]
RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(17_500_000 / 256),
}
}
}
#[allow(unused)]
#[cfg(not(any(esp32c6, esp32h2)))]
#[derive(Debug, Clone, Copy)]
pub(crate) enum RtcCalSel {
RtcCalRtcMux = 0,
RtcCal8mD256 = 1,
RtcCal32kXtal = 2,
#[cfg(not(esp32))]
RtcCalInternalOsc = 3,
}
pub struct Rtc<'d> {
_inner: PeripheralRef<'d, crate::peripherals::LPWR>,
pub rwdt: Rwdt,
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
pub swd: Swd,
}
impl<'d> Rtc<'d> {
pub fn new(rtc_cntl: impl Peripheral<P = crate::peripherals::LPWR> + 'd) -> Self {
rtc::init();
rtc::configure_clock();
let this = Self {
_inner: rtc_cntl.into_ref(),
rwdt: Rwdt::new(),
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
swd: Swd::new(),
};
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
RtcSleepConfig::base_settings(&this);
this
}
pub fn estimate_xtal_frequency(&mut self) -> u32 {
RtcClock::estimate_xtal_frequency()
}
fn time_since_boot_raw(&self) -> u64 {
let rtc_cntl = unsafe { lp_timer() };
#[cfg(esp32)]
let (l, h) = {
rtc_cntl.time_update().write(|w| w.time_update().set_bit());
while rtc_cntl.time_update().read().time_valid().bit_is_clear() {
crate::rom::ets_delay_us(1);
}
let h = rtc_cntl.time1().read().time_hi().bits();
let l = rtc_cntl.time0().read().time_lo().bits();
(l, h)
};
#[cfg(any(esp32c2, esp32c3, esp32s3, esp32s2))]
let (l, h) = {
rtc_cntl.time_update().write(|w| w.time_update().set_bit());
let h = rtc_cntl.time_high0().read().timer_value0_high().bits();
let l = rtc_cntl.time_low0().read().timer_value0_low().bits();
(l, h)
};
#[cfg(any(esp32c6, esp32h2))]
let (l, h) = {
rtc_cntl.update().write(|w| w.main_timer_update().set_bit());
let h = rtc_cntl
.main_buf0_high()
.read()
.main_timer_buf0_high()
.bits();
let l = rtc_cntl.main_buf0_low().read().main_timer_buf0_low().bits();
(l, h)
};
((h as u64) << 32) | (l as u64)
}
pub fn time_since_boot(&self) -> MicrosDurationU64 {
MicrosDurationU64::micros(
self.time_since_boot_raw() * 1_000_000
/ RtcClock::slow_freq().frequency().to_Hz() as u64,
)
}
fn boot_time_us(&self) -> u64 {
let rtc_cntl = unsafe { lp_aon() };
let (l, h) = (rtc_cntl.store2(), rtc_cntl.store3());
let l = l.read().bits() as u64;
let h = h.read().bits() as u64;
l + (h << 32)
}
fn set_boot_time_us(&self, boot_time_us: u64) {
let rtc_cntl = unsafe { lp_aon() };
let (l, h) = (rtc_cntl.store2(), rtc_cntl.store3());
l.write(|w| unsafe { w.bits((boot_time_us & 0xffffffff) as u32) });
h.write(|w| unsafe { w.bits((boot_time_us >> 32) as u32) });
}
pub fn current_time(&self) -> NaiveDateTime {
let rtc_time_us = self.time_since_boot().to_micros();
let boot_time_us = self.boot_time_us();
let wrapped_boot_time_us = u64::MAX - boot_time_us;
let current_time_us = if rtc_time_us > wrapped_boot_time_us {
rtc_time_us - wrapped_boot_time_us
} else {
boot_time_us + rtc_time_us
};
DateTime::from_timestamp_micros(current_time_us as i64)
.unwrap()
.naive_utc()
}
pub fn set_current_time(&self, current_time: NaiveDateTime) {
let current_time_us: u64 = current_time
.and_utc()
.timestamp_micros()
.try_into()
.expect("current_time is negative");
let rtc_time_us = self.time_since_boot().to_micros();
if current_time_us < rtc_time_us {
self.set_boot_time_us(u64::MAX - rtc_time_us + current_time_us)
} else {
self.set_boot_time_us(current_time_us - rtc_time_us)
}
}
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
pub fn sleep_deep(&mut self, wake_sources: &[&dyn WakeSource]) -> ! {
let config = RtcSleepConfig::deep();
self.sleep(&config, wake_sources);
unreachable!();
}
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
pub fn sleep_light(&mut self, wake_sources: &[&dyn WakeSource]) {
let config = RtcSleepConfig::default();
self.sleep(&config, wake_sources);
}
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
pub fn sleep(&mut self, config: &RtcSleepConfig, wake_sources: &[&dyn WakeSource]) {
let mut config = *config;
let mut wakeup_triggers = WakeTriggers::default();
for wake_source in wake_sources {
wake_source.apply(self, &mut wakeup_triggers, &mut config)
}
config.apply();
config.start_sleep(wakeup_triggers);
config.finish_sleep();
}
const RTC_DISABLE_ROM_LOG: u32 = 1;
#[cfg(any(esp32s3, esp32h2))]
pub fn disable_rom_message_printing(&self) {
let rtc_cntl = unsafe { lp_aon() };
rtc_cntl
.store4()
.modify(|r, w| unsafe { w.bits(r.bits() | Self::RTC_DISABLE_ROM_LOG) });
}
}
impl crate::private::Sealed for Rtc<'_> {}
impl InterruptConfigurable for Rtc<'_> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
cfg_if::cfg_if! {
if #[cfg(any(esp32c6, esp32h2))] {
let interrupt = Interrupt::LP_WDT;
} else {
let interrupt = Interrupt::RTC_CORE;
}
}
for core in crate::Cpu::other() {
crate::interrupt::disable(core, interrupt);
}
unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) };
unwrap!(interrupt::enable(interrupt, handler.priority()));
}
}
pub struct RtcClock;
impl RtcClock {
const CAL_FRACT: u32 = 19;
#[cfg(not(any(esp32c6, esp32h2)))]
fn enable_8m(clk_8m_en: bool, d256_en: bool) {
let rtc_cntl = unsafe { &*LPWR::PTR };
if clk_8m_en {
rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().clear_bit());
unsafe {
rtc_cntl.timer1().modify(|_, w| w.ck8m_wait().bits(5));
}
crate::rom::ets_delay_us(50);
} else {
rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().set_bit());
rtc_cntl
.timer1()
.modify(|_, w| unsafe { w.ck8m_wait().bits(20) });
}
if d256_en {
rtc_cntl
.clk_conf()
.modify(|_, w| w.enb_ck8m_div().clear_bit());
} else {
rtc_cntl
.clk_conf()
.modify(|_, w| w.enb_ck8m_div().set_bit());
}
}
pub(crate) fn read_xtal_freq_mhz() -> Option<u32> {
let xtal_freq_reg = unsafe { lp_aon() }.store4().read().bits();
let xtal_freq = (xtal_freq_reg & !Rtc::RTC_DISABLE_ROM_LOG) as u16;
let xtal_freq_copy = (xtal_freq_reg >> 16) as u16;
if xtal_freq == xtal_freq_copy && xtal_freq != 0 && xtal_freq != u16::MAX {
Some(xtal_freq as u32)
} else {
None
}
}
#[cfg(not(any(esp32c6, esp32h2)))]
pub fn xtal_freq() -> XtalClock {
match Self::read_xtal_freq_mhz() {
None | Some(40) => XtalClock::RtcXtalFreq40M,
#[cfg(any(esp32c3, esp32s3))]
Some(32) => XtalClock::RtcXtalFreq32M,
#[cfg(any(esp32, esp32c2))]
Some(26) => XtalClock::RtcXtalFreq26M,
Some(other) => XtalClock::RtcXtalFreqOther(other),
}
}
#[cfg(not(any(esp32c6, esp32h2)))]
pub fn slow_freq() -> RtcSlowClock {
let rtc_cntl = unsafe { &*LPWR::PTR };
let slow_freq = rtc_cntl.clk_conf().read().ana_clk_rtc_sel().bits();
match slow_freq {
0 => RtcSlowClock::RtcSlowClockRtc,
1 => RtcSlowClock::RtcSlowClock32kXtal,
2 => RtcSlowClock::RtcSlowClock8mD256,
_ => unreachable!(),
}
}
#[cfg(not(any(esp32c6, esp32h2)))]
fn set_slow_freq(slow_freq: RtcSlowClock) {
unsafe {
let rtc_cntl = &*LPWR::PTR;
rtc_cntl.clk_conf().modify(|_, w| {
w.ana_clk_rtc_sel()
.bits(slow_freq as u8)
.dig_xtal32k_en()
.bit(matches!(slow_freq, RtcSlowClock::RtcSlowClock32kXtal))
.ck8m_force_pu()
.bit(matches!(slow_freq, RtcSlowClock::RtcSlowClock8mD256))
});
};
crate::rom::ets_delay_us(300u32);
}
#[cfg(not(any(esp32c6, esp32h2)))]
fn set_fast_freq(fast_freq: RtcFastClock) {
unsafe {
let rtc_cntl = &*LPWR::PTR;
rtc_cntl.clk_conf().modify(|_, w| {
w.fast_clk_rtc_sel().bit(match fast_freq {
RtcFastClock::RtcFastClock8m => true,
RtcFastClock::RtcFastClockXtalD4 => false,
})
});
};
crate::rom::ets_delay_us(3u32);
}
#[cfg(not(any(esp32c6, esp32h2)))]
fn calibrate_internal(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
#[cfg(not(esp32))]
let cal_clk = match cal_clk {
RtcCalSel::RtcCalRtcMux => match RtcClock::slow_freq() {
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256,
_ => cal_clk,
},
RtcCalSel::RtcCalInternalOsc => RtcCalSel::RtcCalRtcMux,
_ => cal_clk,
};
let rtc_cntl = unsafe { &*LPWR::PTR };
let timg0 = unsafe { &*TIMG0::PTR };
let dig_32k_xtal_enabled = rtc_cntl.clk_conf().read().dig_xtal32k_en().bit_is_set();
if matches!(cal_clk, RtcCalSel::RtcCal32kXtal) && !dig_32k_xtal_enabled {
rtc_cntl
.clk_conf()
.modify(|_, w| w.dig_xtal32k_en().set_bit());
}
if matches!(cal_clk, RtcCalSel::RtcCal8mD256) {
rtc_cntl
.clk_conf()
.modify(|_, w| w.dig_clk8m_d256_en().set_bit());
}
#[cfg(not(esp32))]
if timg0
.rtccalicfg()
.read()
.rtc_cali_start_cycling()
.bit_is_set()
{
timg0
.rtccalicfg2()
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) });
while timg0.rtccalicfg().read().rtc_cali_rdy().bit_is_clear()
&& timg0.rtccalicfg2().read().rtc_cali_timeout().bit_is_clear()
{}
}
timg0.rtccalicfg().modify(|_, w| unsafe {
w.rtc_cali_clk_sel()
.bits(cal_clk as u8)
.rtc_cali_start_cycling()
.clear_bit()
.rtc_cali_max()
.bits(slowclk_cycles as u16)
});
let expected_freq = match cal_clk {
RtcCalSel::RtcCal32kXtal => {
#[cfg(not(esp32))]
timg0.rtccalicfg2().modify(|_, w| unsafe {
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
});
RtcSlowClock::RtcSlowClock32kXtal
}
RtcCalSel::RtcCal8mD256 => {
#[cfg(not(esp32))]
timg0.rtccalicfg2().modify(|_, w| unsafe {
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
});
RtcSlowClock::RtcSlowClock8mD256
}
_ => {
#[cfg(not(esp32))]
timg0.rtccalicfg2().modify(|_, w| unsafe {
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 10)
});
RtcSlowClock::RtcSlowClockRtc
}
};
let us_time_estimate = HertzU32::MHz(slowclk_cycles) / expected_freq.frequency();
timg0
.rtccalicfg()
.modify(|_, w| w.rtc_cali_start().clear_bit().rtc_cali_start().set_bit());
crate::rom::ets_delay_us(us_time_estimate);
#[cfg(esp32)]
let mut timeout_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();
}
#[cfg(not(esp32))]
if timg0.rtccalicfg2().read().rtc_cali_timeout().bit_is_set() {
break 0;
}
#[cfg(esp32)]
if timeout_us > 0 {
timeout_us -= 1;
crate::rom::ets_delay_us(1);
} else {
break 0;
}
};
timg0
.rtccalicfg()
.modify(|_, w| w.rtc_cali_start().clear_bit());
rtc_cntl
.clk_conf()
.modify(|_, w| w.dig_xtal32k_en().bit(dig_32k_xtal_enabled));
if matches!(cal_clk, RtcCalSel::RtcCal8mD256) {
rtc_cntl
.clk_conf()
.modify(|_, w| w.dig_clk8m_d256_en().clear_bit());
}
cal_val
}
#[cfg(not(any(esp32c6, esp32h2)))]
fn calibration_ratio(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64;
let ratio = (xtal_cycles << RtcClock::CAL_FRACT) / slowclk_cycles as u64;
(ratio & (u32::MAX as u64)) as u32
}
#[cfg(not(any(esp32c6, esp32h2)))]
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
}
#[cfg(not(any(esp32c6, esp32h2)))]
fn cycles_to_1ms() -> u16 {
let period_13q19 = RtcClock::calibrate(
match RtcClock::slow_freq() {
RtcSlowClock::RtcSlowClockRtc => RtcCalSel::RtcCalRtcMux,
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
#[cfg(not(any(esp32c6, esp32h2)))]
RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256,
},
1024,
);
let period = (100_000_000 * period_13q19 as u64) / (1 << RtcClock::CAL_FRACT);
(100_000_000 * 1000 / period) as u16
}
#[cfg(not(any(esp32c6, esp32h2)))]
pub(crate) fn estimate_xtal_frequency() -> u32 {
const XTAL_FREQ_EST_CYCLES: u32 = 10;
let rtc_cntl = unsafe { &*LPWR::PTR };
let clk_8m_enabled = rtc_cntl.clk_conf().read().enb_ck8m().bit_is_clear();
let clk_8md256_enabled = rtc_cntl.clk_conf().read().enb_ck8m_div().bit_is_clear();
if !clk_8md256_enabled {
RtcClock::enable_8m(true, true);
}
let ratio = RtcClock::calibration_ratio(RtcCalSel::RtcCal8mD256, XTAL_FREQ_EST_CYCLES);
let freq_mhz =
((ratio as u64 * RtcFastClock::RtcFastClock8m.hz() as u64 / 1_000_000u64 / 256u64)
>> RtcClock::CAL_FRACT) as u32;
RtcClock::enable_8m(clk_8m_enabled, clk_8md256_enabled);
freq_mhz
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub enum RwdtStageAction {
Off = 0,
Interrupt = 1,
ResetCpu = 2,
ResetCore = 3,
ResetSystem = 4,
}
#[derive(Debug, Clone, Copy)]
pub enum RwdtStage {
Stage0,
Stage1,
Stage2,
Stage3,
}
pub struct Rwdt;
impl Default for Rwdt {
fn default() -> Self {
Self::new()
}
}
impl Rwdt {
pub fn new() -> Self {
Self
}
pub fn enable(&mut self) {
self.set_enabled(true);
}
pub fn disable(&mut self) {
self.set_enabled(false);
}
pub fn listen(&mut self) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::Interrupt as u8) });
rtc_cntl.int_ena().modify(|_, w| w.wdt().set_bit());
self.set_write_protection(true);
}
pub fn unlisten(&mut self) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::ResetSystem as u8) });
rtc_cntl.int_ena().modify(|_, w| w.wdt().clear_bit());
self.set_write_protection(true);
}
pub fn clear_interrupt(&mut self) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
rtc_cntl.int_clr().write(|w| w.wdt().clear_bit_by_one());
self.set_write_protection(true);
}
pub fn is_interrupt_set(&self) -> bool {
let rtc_cntl = unsafe { lp_wdt() };
rtc_cntl.int_st().read().wdt().bit_is_set()
}
pub fn feed(&mut self) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
rtc_cntl.wdtfeed().write(|w| w.wdt_feed().set_bit());
self.set_write_protection(true);
}
fn set_write_protection(&mut self, enable: bool) {
let rtc_cntl = unsafe { lp_wdt() };
let wkey = if enable { 0u32 } else { 0x50D8_3AA1 };
rtc_cntl.wdtwprotect().write(|w| unsafe { w.bits(wkey) });
}
fn set_enabled(&mut self, enable: bool) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
if !enable {
rtc_cntl.wdtconfig0().modify(|_, w| unsafe { w.bits(0) });
} else {
rtc_cntl
.wdtconfig0()
.write(|w| w.wdt_flashboot_mod_en().bit(false));
rtc_cntl
.wdtconfig0()
.modify(|_, w| w.wdt_en().bit(enable).wdt_pause_in_slp().bit(enable));
unsafe {
rtc_cntl.wdtconfig0().modify(|_, w| {
w.wdt_stg0()
.bits(RwdtStageAction::ResetSystem as u8)
.wdt_cpu_reset_length()
.bits(7)
.wdt_sys_reset_length()
.bits(7)
.wdt_stg1()
.bits(RwdtStageAction::Off as u8)
.wdt_stg2()
.bits(RwdtStageAction::Off as u8)
.wdt_stg3()
.bits(RwdtStageAction::Off as u8)
.wdt_en()
.set_bit()
});
}
}
self.set_write_protection(true);
}
pub fn set_timeout(&mut self, stage: RwdtStage, timeout: MicrosDurationU64) {
let rtc_cntl = unsafe { lp_wdt() };
let timeout_raw = (timeout.to_millis() * (RtcClock::cycles_to_1ms() as u64)) as u32;
self.set_write_protection(false);
unsafe {
#[cfg(esp32)]
match stage {
RwdtStage::Stage0 => rtc_cntl
.wdtconfig1()
.modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)),
RwdtStage::Stage1 => rtc_cntl
.wdtconfig2()
.modify(|_, w| w.wdt_stg1_hold().bits(timeout_raw)),
RwdtStage::Stage2 => rtc_cntl
.wdtconfig3()
.modify(|_, w| w.wdt_stg2_hold().bits(timeout_raw)),
RwdtStage::Stage3 => rtc_cntl
.wdtconfig4()
.modify(|_, w| w.wdt_stg3_hold().bits(timeout_raw)),
};
#[cfg(any(esp32c6, esp32h2))]
match stage {
RwdtStage::Stage0 => rtc_cntl.config1().modify(|_, w| {
w.wdt_stg0_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage1 => rtc_cntl.config2().modify(|_, w| {
w.wdt_stg1_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage2 => rtc_cntl.config3().modify(|_, w| {
w.wdt_stg2_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage3 => rtc_cntl.config4().modify(|_, w| {
w.wdt_stg3_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
};
#[cfg(not(any(esp32, esp32c6, esp32h2)))]
match stage {
RwdtStage::Stage0 => rtc_cntl.wdtconfig1().modify(|_, w| {
w.wdt_stg0_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage1 => rtc_cntl.wdtconfig2().modify(|_, w| {
w.wdt_stg1_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage2 => rtc_cntl.wdtconfig3().modify(|_, w| {
w.wdt_stg2_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
RwdtStage::Stage3 => rtc_cntl.wdtconfig4().modify(|_, w| {
w.wdt_stg3_hold()
.bits(timeout_raw >> (1 + Efuse::rwdt_multiplier()))
}),
};
}
self.set_write_protection(true);
}
pub fn set_stage_action(&mut self, stage: RwdtStage, action: RwdtStageAction) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
match stage {
RwdtStage::Stage0 => rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) }),
RwdtStage::Stage1 => rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) }),
RwdtStage::Stage2 => rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) }),
RwdtStage::Stage3 => rtc_cntl
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) }),
};
self.set_write_protection(true);
}
}
impl embedded_hal_02::watchdog::WatchdogDisable for Rwdt {
fn disable(&mut self) {
self.disable();
}
}
impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt {
type Time = MicrosDurationU64;
fn start<T>(&mut self, period: T)
where
T: Into<Self::Time>,
{
self.set_timeout(RwdtStage::Stage0, period.into());
}
}
impl embedded_hal_02::watchdog::Watchdog for Rwdt {
fn feed(&mut self) {
self.feed();
}
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
pub struct Swd;
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
impl Swd {
pub fn new() -> Self {
Self
}
pub fn enable(&mut self) {
self.set_enabled(true);
}
pub fn disable(&mut self) {
self.set_enabled(false);
}
fn set_write_protection(&mut self, enable: bool) {
let rtc_cntl = unsafe { lp_wdt() };
#[cfg(not(any(esp32c6, esp32h2)))]
let wkey = if enable { 0u32 } else { 0x8F1D_312A };
#[cfg(any(esp32c6, esp32h2))]
let wkey = if enable { 0u32 } else { 0x50D8_3AA1 };
rtc_cntl
.swd_wprotect()
.write(|w| unsafe { w.swd_wkey().bits(wkey) });
}
fn set_enabled(&mut self, enable: bool) {
let rtc_cntl = unsafe { lp_wdt() };
self.set_write_protection(false);
rtc_cntl
.swd_conf()
.write(|w| w.swd_auto_feed_en().bit(!enable));
self.set_write_protection(true);
}
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
impl Default for Swd {
fn default() -> Self {
Self::new()
}
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
impl embedded_hal_02::watchdog::WatchdogDisable for Swd {
fn disable(&mut self) {
self.disable();
}
}
pub fn reset_reason(cpu: Cpu) -> Option<SocResetReason> {
let reason = crate::rom::rtc_get_reset_reason(cpu as u32);
SocResetReason::from_repr(reason as usize)
}
pub fn wakeup_cause() -> SleepSource {
if reset_reason(Cpu::ProCpu) != Some(SocResetReason::CoreDeepSleep) {
return SleepSource::Undefined;
}
#[cfg(any(esp32c6, esp32h2))]
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
(*crate::peripherals::PMU::PTR)
.slp_wakeup_status0()
.read()
.wakeup_cause()
.bits()
});
#[cfg(not(any(esp32, esp32c6, esp32h2)))]
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
(*LPWR::PTR).slp_wakeup_cause().read().wakeup_cause().bits()
});
#[cfg(esp32)]
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
(*LPWR::PTR).wakeup_state().read().wakeup_cause().bits() as u32
});
if wakeup_cause.contains(WakeupReason::TimerTrigEn) {
return SleepSource::Timer;
}
if wakeup_cause.contains(WakeupReason::GpioTrigEn) {
return SleepSource::Gpio;
}
if wakeup_cause.intersects(WakeupReason::Uart0TrigEn | WakeupReason::Uart1TrigEn) {
return SleepSource::Uart;
}
#[cfg(pm_support_ext0_wakeup)]
if wakeup_cause.contains(WakeupReason::ExtEvent0Trig) {
return SleepSource::Ext0;
}
#[cfg(pm_support_ext1_wakeup)]
if wakeup_cause.contains(WakeupReason::ExtEvent1Trig) {
return SleepSource::Ext1;
}
#[cfg(pm_support_touch_sensor_wakeup)]
if wakeup_cause.contains(WakeupReason::TouchTrigEn) {
return SleepSource::TouchPad;
}
#[cfg(ulp_supported)]
if wakeup_cause.contains(WakeupReason::UlpTrigEn) {
return SleepSource::Ulp;
}
#[cfg(pm_support_wifi_wakeup)]
if wakeup_cause.contains(WakeupReason::WifiTrigEn) {
return SleepSource::Wifi;
}
#[cfg(pm_support_bt_wakeup)]
if wakeup_cause.contains(WakeupReason::BtTrigEn) {
return SleepSource::BT;
}
#[cfg(riscv_coproc_supported)]
if wakeup_cause.contains(WakeupReason::CocpuTrigEn) {
return SleepSource::Ulp;
} else if wakeup_cause.contains(WakeupReason::CocpuTrapTrigEn) {
return SleepSource::CocpuTrapTrig;
}
SleepSource::Undefined
}
#[no_mangle]
extern "C" fn rtc_clk_xtal_freq_get() -> i32 {
let xtal = RtcClock::xtal_freq();
xtal.mhz() as i32
}