Skip to content

Commit

Permalink
system timer simplfication
Browse files Browse the repository at this point in the history
* Removes _all_ type params on Alarm
* Systimer no longer implements peripheral ref, the peripheral ref
  pattern is instead intended to be used on the higher level timer
  drivers
* Removed `Unit` as a type, in favour of an enum
* Alarms are back in the main `SystemTimer` "driver"
* Made all `Unit` modification methods unsafe, it's not possible to
  modify the `Unit`'s safely whilst timers and or the `time::now` API is
  in use
  • Loading branch information
MabezDev committed Nov 21, 2024
1 parent 4fb386a commit 69a521f
Show file tree
Hide file tree
Showing 2 changed files with 318 additions and 0 deletions.
132 changes: 132 additions & 0 deletions examples/src/bin/systimer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! This shows how to use the SYSTIMER peripheral including interrupts.
//!
//! It's an additional timer besides the TIMG peripherals.

//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3

#![no_std]
#![no_main]

use core::cell::RefCell;

use critical_section::Mutex;
use esp_backtrace as _;
use esp_hal::{
delay::Delay,
prelude::*,
timer::systimer::{
Alarm,
FrozenUnit,
Periodic,
SpecificComparator,
SpecificUnit,
SystemTimer,
Target,
},
Blocking,
};
use esp_println::println;
use fugit::ExtU32;
use static_cell::StaticCell;

static ALARM0: Mutex<
RefCell<
Option<Alarm<Periodic, Blocking, SpecificComparator<'static, 0>, SpecificUnit<'static, 0>>>,
>,
> = Mutex::new(RefCell::new(None));
static ALARM1: Mutex<
RefCell<
Option<Alarm<Target, Blocking, SpecificComparator<'static, 1>, SpecificUnit<'static, 0>>>,
>,
> = Mutex::new(RefCell::new(None));
static ALARM2: Mutex<
RefCell<
Option<Alarm<Target, Blocking, SpecificComparator<'static, 2>, SpecificUnit<'static, 0>>>,
>,
> = Mutex::new(RefCell::new(None));

#[entry]
fn main() -> ! {
let peripherals = esp_hal::init(esp_hal::Config::default());

let systimer = SystemTimer::new(peripherals.SYSTIMER);
println!(
"SYSTIMER Current value = {}",
SystemTimer::now(Default::default())
);

static UNIT0: StaticCell<SpecificUnit<'static, 0>> = StaticCell::new();

let unit0 = UNIT0.init(systimer.unit0);

let frozen_unit = FrozenUnit::new(unit0);

let alarm0 = Alarm::new(systimer.comparator0, &frozen_unit);
let alarm1 = Alarm::new(systimer.comparator1, &frozen_unit);
let alarm2 = Alarm::new(systimer.comparator2, &frozen_unit);

critical_section::with(|cs| {
let alarm0 = alarm0.into_periodic();
alarm0.set_interrupt_handler(systimer_target0);
alarm0.set_period(1u32.secs());
alarm0.enable_interrupt(true);

alarm1.set_interrupt_handler(systimer_target1);
alarm1.set_target(
SystemTimer::now(Default::default()) + (SystemTimer::ticks_per_second() * 2),
);
alarm1.enable_interrupt(true);

alarm2.set_interrupt_handler(systimer_target2);
alarm2.set_target(
SystemTimer::now(Default::default()) + (SystemTimer::ticks_per_second() * 3),
);
alarm2.enable_interrupt(true);

ALARM0.borrow_ref_mut(cs).replace(alarm0);
ALARM1.borrow_ref_mut(cs).replace(alarm1);
ALARM2.borrow_ref_mut(cs).replace(alarm2);
});

let delay = Delay::new();

loop {
delay.delay_millis(500);
}
}

#[handler(priority = esp_hal::interrupt::Priority::min())]
fn systimer_target0() {
println!("Interrupt lvl1 (alarm0)");
critical_section::with(|cs| {
ALARM0
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.clear_interrupt()
});
}

#[handler(priority = esp_hal::interrupt::Priority::Priority1)]
fn systimer_target1() {
println!("Interrupt lvl2 (alarm1)");
critical_section::with(|cs| {
ALARM1
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.clear_interrupt()
});
}

#[handler(priority = esp_hal::interrupt::Priority::max())]
fn systimer_target2() {
println!("Interrupt lvl2 (alarm2)");
critical_section::with(|cs| {
ALARM2
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.clear_interrupt()
});
}
186 changes: 186 additions & 0 deletions hil-test/tests/systimer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//! 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<M, const C: u8> =
Alarm<'static, M, Blocking, SpecificComparator<'static, C>, SpecificUnit<'static, 0>>;

static ALARM_TARGET: Mutex<RefCell<Option<TestAlarm<Target, 0>>>> = Mutex::new(RefCell::new(None));
static ALARM_PERIODIC: Mutex<RefCell<Option<TestAlarm<Periodic, 1>>>> =
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<SpecificUnit<'static, 0>> = 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(Default::default()) + 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(Default::default()) + 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 {}
}
}

0 comments on commit 69a521f

Please sign in to comment.