pub mod prelude {
pub use crate::silabs_rtcc_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, silabs::NVIC_PRIO_BITS, TimerQueueBackend};
use cortex_m::peripheral::NVIC;
use portable_atomic::AtomicU32;
use rtic_time::half_period_counter::calculate_now;
pub use silabs_metapac::{cmu_v1::Cmu, rtcc_v1::Rtcc};
use silabs_metapac::{
rtcc_v1::vals::{Cc0CtrlMode, Cc1CtrlMode},
Interrupt, RTCC,
};
pub struct TimerBackend;
impl TimerBackend {
pub fn _start(timer: Rtcc, cmu: &Cmu) {
cmu.clken0().modify(|w| w.set_rtcc(true));
timer.en().write(|w| w.set_en(true));
timer.cmd().write(|w| w.set_start(true));
timer.ien().modify(|w| {
w.set_cc0(true);
w.set_cc1(true);
w.set_of(true)
});
timer
.cc1_ctrl()
.write(|w| w.set_mode(Cc1CtrlMode::Outputcompare));
timer.cc1_ocvalue().write(|w| w.set_oc(0x8000_0000));
TIMER_QUEUE.initialize(Self {});
unsafe {
crate::set_monotonic_prio(NVIC_PRIO_BITS, Interrupt::RTCC);
NVIC::unmask(Interrupt::RTCC);
}
}
fn rtcc() -> &'static Rtcc {
&RTCC
}
}
static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
static RTCC_HALF_PERIOD_COUNTER: AtomicU32 = AtomicU32::new(0);
impl TimerQueueBackend for TimerBackend {
type Ticks = u64;
fn now() -> Self::Ticks {
let timer = Self::rtcc();
calculate_now(
|| RTCC_HALF_PERIOD_COUNTER.load(Ordering::Relaxed),
|| timer.cnt().read().cnt(),
)
}
fn set_compare(instant: Self::Ticks) {
const RTCC_MAX: u64 = 0xFFFF_FFFF;
Self::rtcc()
.cc0_ctrl()
.write(|w| w.set_mode(Cc0CtrlMode::Outputcompare));
let now = Self::now();
let diff = instant.wrapping_sub(now);
let compare_value = if diff <= RTCC_MAX {
(instant & RTCC_MAX) as u32
} else {
0
};
Self::rtcc()
.cc0_ocvalue()
.write(|w| w.set_oc(compare_value));
}
fn clear_compare_flag() {
Self::rtcc().if_clr().write(|w| w.set_cc0(true));
Self::rtcc()
.cc0_ctrl()
.write(|w| w.set_mode(Cc0CtrlMode::Off));
}
fn on_interrupt() {
let interrupt_flag = Self::rtcc().if_().read();
if interrupt_flag.cc1() {
Self::rtcc().if_clr().write(|w| w.set_cc1(true));
let prev = RTCC_HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed);
::core::assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
}
if interrupt_flag.of() {
Self::rtcc().if_clr().write(|w| w.set_of(true));
let prev = RTCC_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::RTCC);
}
fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE
}
}
#[macro_export]
macro_rules! silabs_rtcc_monotonic {
($name:ident) => {
use $crate::{fugit, rtic_time};
pub struct $name;
impl $name {
pub fn start(timer: $crate::silabs::rtcc::Rtcc, cmu: &$crate::silabs::rtcc::Cmu) {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn RTCC() {
use $crate::TimerQueueBackend;
$crate::silabs::rtcc::TimerBackend::timer_queue().on_monotonic_interrupt();
}
$crate::silabs::rtcc::TimerBackend::_start(timer, cmu);
}
}
impl $crate::TimerQueueBasedMonotonic for $name {
type Backend = $crate::silabs::rtcc::TimerBackend;
type Instant =
fugit::Instant<<Self::Backend as $crate::TimerQueueBackend>::Ticks, 1, 32768>;
type Duration =
fugit::Duration<<Self::Backend as $crate::TimerQueueBackend>::Ticks, 1, 32768>;
}
rtic_time::impl_embedded_hal_delay_fugit!($name);
rtic_time::impl_embedded_hal_async_delay_fugit!($name);
};
}