pub mod prelude {
pub use crate::silabs_letimer_monotonic;
pub use silabs_metapac;
pub use crate::fugit::{self, ExtU64, ExtU64Ceil};
pub use crate::Monotonic;
}
use core::sync::atomic::Ordering;
use crate::{
rtic_time::timer_queue::TimerQueue, set_monotonic_prio, silabs::NVIC_PRIO_BITS,
TimerQueueBackend,
};
use cortex_m::peripheral::NVIC;
use portable_atomic::AtomicU32;
use rtic_time::half_period_counter::calculate_now;
use silabs_metapac::{Interrupt, LETIMER0};
#[cfg(feature = "silabs-efr32mg22")]
pub use silabs_metapac::{
cmu_v1::Cmu,
letimer_v0::{vals::Cntpresc, Letimer},
};
#[cfg(feature = "silabs-efr32mg24")]
pub use silabs_metapac::{
cmu_v3::Cmu,
letimer_v1::{vals::Cntpresc, Letimer},
};
#[cfg(feature = "silabs-efr32fg25")]
pub use silabs_metapac::{
cmu_v4::Cmu,
letimer_v1::{vals::Cntpresc, Letimer},
};
#[cfg(feature = "silabs-efr32mg26")]
pub use silabs_metapac::{
cmu_v7::Cmu,
letimer_v1::{vals::Cntpresc, Letimer},
};
pub struct TimerBackend;
const U24_MAX: u32 = 0xFF_FFFF;
const HALF_PERIOD_UP: u32 = 0x80_0000;
const HALF_PERIOD_DOWN: u32 = U24_MAX - HALF_PERIOD_UP;
impl TimerBackend {
pub fn _start(timer: Letimer, cmu: &Cmu, tick_rate_hz: u32) {
cmu.clken0().modify(|w| w.set_letimer0(true));
timer.ctrl().write(|w| match tick_rate_hz {
32_768 => w.set_cntpresc(Cntpresc::Div1),
16_384 => w.set_cntpresc(Cntpresc::Div2),
8_192 => w.set_cntpresc(Cntpresc::Div4),
4_096 => w.set_cntpresc(Cntpresc::Div8),
2_048 => w.set_cntpresc(Cntpresc::Div16),
1_024 => w.set_cntpresc(Cntpresc::Div32),
512 => w.set_cntpresc(Cntpresc::Div64),
256 => w.set_cntpresc(Cntpresc::Div128),
_ => ::core::panic!("Timer cannot run at desired tick rate!"),
});
timer.en().write(|w| w.set_en(true));
timer.comp1().write(|w| w.set_comp1(HALF_PERIOD_DOWN));
timer.ien().modify(|w| {
w.set_comp0(true);
w.set_comp1(true);
w.set_uf(true)
});
timer.cmd().write(|w| w.set_start(true));
TIMER_QUEUE.initialize(Self {});
unsafe {
set_monotonic_prio(NVIC_PRIO_BITS, Interrupt::LETIMER0);
NVIC::unmask(Interrupt::LETIMER0);
}
}
fn letimer() -> &'static Letimer {
&LETIMER0
}
}
static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
static LETIMER_HALF_PERIOD_COUNTER: AtomicU32 = AtomicU32::new(0);
impl TimerQueueBackend for TimerBackend {
type Ticks = u64;
fn now() -> Self::Ticks {
let timer = Self::letimer();
calculate_now(
|| LETIMER_HALF_PERIOD_COUNTER.load(Ordering::Relaxed),
|| {
let now = timer.cnt().read().cnt();
if now == 0 {
0 } else {
U24_MAX - now
}
},
)
}
fn set_compare(instant: Self::Ticks) {
let now = Self::now();
let compare_value = if instant.saturating_sub(now) <= U24_MAX.into() {
U24_MAX - (instant as u32 & U24_MAX)
} else {
U24_MAX
};
Self::letimer()
.comp0()
.write(|w| w.set_comp0(compare_value));
}
fn clear_compare_flag() {
Self::letimer().if_clr().write(|w| w.set_comp0(true));
Self::letimer().comp0().write(|w| w.set_comp0(0));
}
fn on_interrupt() {
let interrupt_flag = Self::letimer().if_().read();
if interrupt_flag.comp1() {
Self::letimer().if_clr().write(|w| w.set_comp1(true));
let prev = LETIMER_HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
}
if interrupt_flag.uf() {
Self::letimer().if_clr().write(|w| w.set_uf(true));
let prev = LETIMER_HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
}
}
fn pend_interrupt() {
NVIC::pend(Interrupt::LETIMER0);
}
fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE
}
}
#[macro_export]
macro_rules! silabs_letimer_monotonic {
($name:ident) => {
$crate::silabs_letimer_monotonic!($name, 32_768);
};
($name:ident, $tick_rate_hz:expr) => {
use $crate::{fugit, rtic_time};
pub struct $name;
impl $name {
pub fn start(
timer: $crate::silabs::letimer::Letimer,
cmu: &$crate::silabs::letimer::Cmu,
) {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn LETIMER0() {
use $crate::TimerQueueBackend;
$crate::silabs::letimer::TimerBackend::timer_queue().on_monotonic_interrupt();
}
$crate::silabs::letimer::TimerBackend::_start(timer, cmu, $tick_rate_hz);
}
}
impl $crate::TimerQueueBasedMonotonic for $name {
type Backend = $crate::silabs::letimer::TimerBackend;
type Instant = fugit::Instant<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
type Duration = fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
}
rtic_time::impl_embedded_hal_delay_fugit!($name);
rtic_time::impl_embedded_hal_async_delay_fugit!($name);
};
}