//! System Timer Test // esp32 disabled as it does not have a systimer //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] use core::cell::RefCell; use critical_section::Mutex; use embedded_hal::delay::DelayNs; use esp_hal::{ delay::Delay, prelude::*, timer::systimer::{ Alarm, FrozenUnit, Periodic, SpecificComparator, SpecificUnit, SystemTimer, Target, }, Blocking, }; use fugit::ExtU32; use hil_test as _; use portable_atomic::{AtomicUsize, Ordering}; use static_cell::StaticCell; type TestAlarm = Alarm<'static, M, Blocking, SpecificComparator<'static, C>, SpecificUnit<'static, 0>>; static ALARM_TARGET: Mutex>>> = Mutex::new(RefCell::new(None)); static ALARM_PERIODIC: Mutex>>> = Mutex::new(RefCell::new(None)); struct Context { unit: FrozenUnit<'static, SpecificUnit<'static, 0>>, comparator0: SpecificComparator<'static, 0>, comparator1: SpecificComparator<'static, 1>, } #[handler(priority = esp_hal::interrupt::Priority::min())] fn pass_test_if_called() { critical_section::with(|cs| { ALARM_TARGET .borrow_ref_mut(cs) .as_mut() .unwrap() .clear_interrupt() }); embedded_test::export::check_outcome(()); } #[handler(priority = esp_hal::interrupt::Priority::min())] fn handle_periodic_interrupt() { critical_section::with(|cs| { ALARM_PERIODIC .borrow_ref_mut(cs) .as_mut() .unwrap() .clear_interrupt() }); } static COUNTER: AtomicUsize = AtomicUsize::new(0); #[handler(priority = esp_hal::interrupt::Priority::min())] fn pass_test_if_called_twice() { critical_section::with(|cs| { ALARM_PERIODIC .borrow_ref_mut(cs) .as_mut() .unwrap() .clear_interrupt() }); COUNTER.fetch_add(1, Ordering::Relaxed); if COUNTER.load(Ordering::Relaxed) == 2 { embedded_test::export::check_outcome(()); } } #[handler(priority = esp_hal::interrupt::Priority::min())] fn target_fail_test_if_called_twice() { critical_section::with(|cs| { ALARM_TARGET .borrow_ref_mut(cs) .as_mut() .unwrap() .clear_interrupt() }); COUNTER.fetch_add(1, Ordering::Relaxed); assert!(COUNTER.load(Ordering::Relaxed) != 2); } #[cfg(test)] #[embedded_test::tests] mod tests { use super::*; #[init] fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); let systimer = SystemTimer::new(peripherals.SYSTIMER); static UNIT0: StaticCell> = StaticCell::new(); let unit0 = UNIT0.init(systimer.unit0); let frozen_unit = FrozenUnit::new(unit0); Context { unit: frozen_unit, comparator0: systimer.comparator0, comparator1: systimer.comparator1, } } #[test] #[timeout(3)] fn target_interrupt_is_handled(ctx: Context) { let alarm0 = Alarm::new(ctx.comparator0, &ctx.unit); critical_section::with(|cs| { alarm0.set_interrupt_handler(pass_test_if_called); alarm0.set_target(SystemTimer::now() + SystemTimer::ticks_per_second() / 10); alarm0.enable_interrupt(true); ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); }); // We'll end the test in the interrupt handler. loop {} } #[test] #[timeout(3)] fn target_interrupt_is_handled_once(ctx: Context) { let alarm0 = Alarm::new(ctx.comparator0, &ctx.unit); let alarm1 = Alarm::new(ctx.comparator1, &ctx.unit); COUNTER.store(0, Ordering::Relaxed); critical_section::with(|cs| { alarm0.set_interrupt_handler(target_fail_test_if_called_twice); alarm0.set_target(SystemTimer::now() + SystemTimer::ticks_per_second() / 10); alarm0.enable_interrupt(true); let alarm1 = alarm1.into_periodic(); alarm1.set_interrupt_handler(handle_periodic_interrupt); alarm1.set_period(100u32.millis()); alarm1.enable_interrupt(true); ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); }); let mut delay = Delay::new(); delay.delay_ms(300); } #[test] #[timeout(3)] fn periodic_interrupt_is_handled(ctx: Context) { let alarm1 = Alarm::new(ctx.comparator1, &ctx.unit); COUNTER.store(0, Ordering::Relaxed); critical_section::with(|cs| { let alarm1 = alarm1.into_periodic(); alarm1.set_interrupt_handler(pass_test_if_called_twice); alarm1.set_period(100u32.millis()); alarm1.enable_interrupt(true); ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); }); // We'll end the test in the interrupt handler. loop {} } }