use core::{intrinsics::transmute, marker::PhantomData}; use fugit::MillisDurationU32; use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::{ generic::Reg, systimer::{ target0_conf::TARGET0_CONF_SPEC, target0_hi::TARGET0_HI_SPEC, target0_lo::TARGET0_LO_SPEC, }, SYSTIMER, }, }; // TODO this only handles unit0 of the systimer pub struct SystemTimer<'d> { _inner: PeripheralRef<'d, SYSTIMER>, pub alarm0: Alarm, pub alarm1: Alarm, pub alarm2: Alarm, } impl<'d> SystemTimer<'d> { #[cfg(esp32s2)] pub const BIT_MASK: u64 = u64::MAX; #[cfg(not(esp32s2))] pub const BIT_MASK: u64 = 0xFFFFFFFFFFFFF; #[cfg(esp32s2)] pub const TICKS_PER_SECOND: u64 = 80_000_000; // TODO this can change when we have support for changing APB frequency #[cfg(not(esp32s2))] pub const TICKS_PER_SECOND: u64 = 16_000_000; pub fn new(p: impl Peripheral

+ 'd) -> Self { crate::into_ref!(p); Self { _inner: p, alarm0: Alarm::new(), alarm1: Alarm::new(), alarm2: Alarm::new(), } } // TODO use fugit types pub fn now() -> u64 { // This should be safe to access from multiple contexts // worst case scenario the second accesor ends up reading // an older time stamp let systimer = unsafe { &*SYSTIMER::ptr() }; systimer .unit0_op .modify(|_, w| w.timer_unit0_update().set_bit()); while !systimer .unit0_op .read() .timer_unit0_value_valid() .bit_is_set() {} let value_lo = systimer.unit0_value_lo.read().bits(); let value_hi = systimer.unit0_value_hi.read().bits(); ((value_hi as u64) << 32) | value_lo as u64 } } #[derive(Debug)] pub struct Target; #[derive(Debug)] pub struct Periodic; // TODO, also impl e-h timer traits #[derive(Debug)] pub struct Alarm { _pd: PhantomData, } impl Alarm { // private constructor fn new() -> Self { Self { _pd: PhantomData } } pub fn interrupt_enable(&self, val: bool) { let systimer = unsafe { &*SYSTIMER::ptr() }; match CHANNEL { 0 => systimer.int_ena.modify(|_, w| w.target0_int_ena().bit(val)), 1 => systimer.int_ena.modify(|_, w| w.target1_int_ena().bit(val)), 2 => systimer.int_ena.modify(|_, w| w.target2_int_ena().bit(val)), _ => unreachable!(), } } pub fn clear_interrupt(&self) { let systimer = unsafe { &*SYSTIMER::ptr() }; match CHANNEL { 0 => systimer.int_clr.write(|w| w.target0_int_clr().set_bit()), 1 => systimer.int_clr.write(|w| w.target1_int_clr().set_bit()), 2 => systimer.int_clr.write(|w| w.target2_int_clr().set_bit()), _ => unreachable!(), } } fn configure( &self, conf: impl FnOnce(&Reg, &Reg, &Reg), ) { unsafe { let systimer = &*SYSTIMER::ptr(); let (tconf, hi, lo): ( &Reg, &Reg, &Reg, ) = match CHANNEL { 0 => ( &systimer.target0_conf, &systimer.target0_hi, &systimer.target0_lo, ), 1 => ( transmute(&systimer.target1_conf), transmute(&systimer.target1_hi), transmute(&systimer.target1_lo), ), 2 => ( transmute(&systimer.target2_conf), transmute(&systimer.target2_hi), transmute(&systimer.target2_lo), ), _ => unreachable!(), }; #[cfg(esp32s2)] systimer.step.write(|w| w.timer_xtal_step().bits(0x1)); // run at XTAL freq, not 80 * XTAL freq #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] { tconf.write(|w| w.target0_timer_unit_sel().clear_bit()); // default, use unit 0 systimer .conf .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit()); } conf(tconf, hi, lo); #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] { match CHANNEL { 0 => { systimer .comp0_load .write(|w| w.timer_comp0_load().set_bit()); } 1 => systimer .comp1_load .write(|w| w.timer_comp1_load().set_bit()), 2 => systimer .comp2_load .write(|w| w.timer_comp2_load().set_bit()), _ => unreachable!(), } systimer.conf.modify(|_r, w| match CHANNEL { 0 => w.target0_work_en().set_bit(), 1 => w.target1_work_en().set_bit(), 2 => w.target2_work_en().set_bit(), _ => unreachable!(), }); } #[cfg(esp32s2)] tconf.modify(|_r, w| match CHANNEL { 0 => w.target0_work_en().set_bit(), 1 => w.target0_work_en().set_bit(), 2 => w.target0_work_en().set_bit(), _ => unreachable!(), }); } } } impl Alarm { pub fn set_target(&self, timestamp: u64) { self.configure(|tconf, hi, lo| unsafe { tconf.write(|w| w.target0_period_mode().clear_bit()); // target mode hi.write(|w| w.timer_target0_hi().bits((timestamp >> 32) as u32)); lo.write(|w| w.timer_target0_lo().bits((timestamp & 0xFFFF_FFFF) as u32)); }) } pub fn into_periodic(self) -> Alarm { Alarm { _pd: PhantomData } } } impl Alarm { pub fn set_period(&self, period: fugit::HertzU32) { let time_period: MillisDurationU32 = period.into_duration(); let cycles = time_period.ticks(); self.configure(|tconf, hi, lo| unsafe { tconf.write(|w| { w.target0_period_mode() .set_bit() .target0_period() .bits(cycles * (SystemTimer::TICKS_PER_SECOND as u32 / 1000)) }); hi.write(|w| w.timer_target0_hi().bits(0)); lo.write(|w| w.timer_target0_lo().bits(0)); }) } pub fn into_target(self) -> Alarm { Alarm { _pd: PhantomData } } } impl Alarm { pub const unsafe fn conjure() -> Self { Self { _pd: PhantomData } } } impl Alarm { pub const unsafe fn conjure() -> Self { Self { _pd: PhantomData } } } impl Alarm { pub const unsafe fn conjure() -> Self { Self { _pd: PhantomData } } }