#[cfg(multi_core)]
use esp_hal::{peripherals::CPU_CTRL, system::Cpu, system::CpuControl};
use esp_hal::{
peripherals::LPWR,
rtc_cntl::{
WakeLock,
sleep::{LowPower, RtcSleepConfig},
},
time::{Duration, Instant},
};
use crate::{SCHEDULER, task::IdleFn};
#[cfg(multi_core)]
use crate::{run_queue::RunSchedulerOn, task};
const LIGHT_SLEEP_MIN_US: u64 =
esp_config::esp_config_int!(u32, "ESP_RTOS_CONFIG_LIGHT_SLEEP_MIN_US") as u64;
pub struct Sleep {
#[cfg(sleep_deep_sleep)]
pub deep_sleep: DeepSleep,
pub light_sleep_hook: IdleFn,
}
#[cfg(sleep_deep_sleep)]
pub struct DeepSleep {
lpwr: LPWR<'static>,
}
#[cfg(sleep_deep_sleep)]
impl DeepSleep {
pub fn deep_sleep(&mut self) -> ! {
let mut lpwr = LowPower::new(self.lpwr.reborrow());
lpwr.sleep_deep(RtcSleepConfig::deep())
}
}
pub fn configure(lpwr: LPWR<'static>) -> Sleep {
Sleep {
#[cfg(sleep_deep_sleep)]
deep_sleep: DeepSleep { lpwr },
light_sleep_hook: auto_light_sleep_hook,
}
}
extern "C" fn auto_light_sleep_hook() -> ! {
loop {
#[cfg(all(multi_core, esp32p4))]
if Cpu::current() == Cpu::AppCpu {
task::trigger_scheduler(RunSchedulerOn::RunOnCore(Cpu::ProCpu));
esp_hal::interrupt::wait_for_interrupt();
continue;
}
SCHEDULER.with(|scheduler| {
if WakeLock::is_active() {
return;
}
#[cfg(multi_core)]
{
if scheduler.run_queue.has_ready_tasks() {
return;
}
for cpu in Cpu::all() {
if !scheduler.cpu_idle(cpu) {
return;
}
}
}
let Some(time_driver) = scheduler.time_driver.as_mut() else {
return;
};
let next_wakeup = time_driver.next_wakeup();
let mut lpwr = LowPower::new(unsafe { LPWR::steal() });
if next_wakeup == u64::MAX {
lpwr.clear_wakeup_deadline();
} else {
lpwr.set_wakeup_deadline(Instant::EPOCH + Duration::from_micros(next_wakeup));
if next_wakeup.saturating_sub(crate::now()) < LIGHT_SLEEP_MIN_US {
return;
}
}
cfg_select! {
multi_core => {
let mut cpu_control = CpuControl::new(unsafe { CPU_CTRL::steal() });
for cpu in Cpu::other() {
if scheduler.active_cores.contains(cpu) {
unsafe { cpu_control.park_core(cpu) };
}
}
}
_ => {}
}
lpwr.sleep_light(RtcSleepConfig::default());
time_driver.rearm(crate::now());
#[cfg(multi_core)]
for cpu in Cpu::other() {
if scheduler.active_cores.contains(cpu) {
cpu_control.unpark_core(cpu);
task::trigger_scheduler(RunSchedulerOn::RunOnCore(cpu));
}
}
});
esp_hal::interrupt::wait_for_interrupt();
}
}