use crate::ll_api::ll_cmd::*;
use embedded_hal::delay::DelayNs;
use fugit::{Duration, TimerDurationU32};
use portable_atomic::{AtomicU32, Ordering};
pub const TICK_FREQ_HZ: u32 = crate::tick_freq_hz::TICK_FREQ_HZ;
#[cfg(not(feature = "tick-size-64bit"))]
pub type TickType = u32;
#[cfg(feature = "tick-size-64bit")]
pub type TickType = u64;
static SYS_TICK_0: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "tick-size-64bit")]
static SYS_TICK_1: AtomicU32 = AtomicU32::new(0);
pub trait HalTickHandler {
unsafe fn on_sys_tick_interrupt();
}
impl HalTickHandler for Tick {
#[inline]
unsafe fn on_sys_tick_interrupt() {
#[cfg(any(feature = "tick-size-64bit", feature = "embassy"))]
let sys_tick = SYS_TICK_0.fetch_add(1, Ordering::Relaxed);
#[cfg(not(any(feature = "tick-size-64bit", feature = "embassy")))]
SYS_TICK_0.add(1, Ordering::Relaxed);
#[cfg(feature = "tick-size-64bit")]
let sys_tick = if sys_tick == u32::MAX {
let tick_1 = SYS_TICK_1.fetch_add(1, Ordering::Release);
((tick_1 as u64) << 32) | (sys_tick as u64)
} else {
let tick_1 = SYS_TICK_1.load(Ordering::Relaxed);
((tick_1 as u64) << 32) | (sys_tick as u64)
};
#[cfg(feature = "embassy")]
tick_time_driver::check_alarm(sys_tick);
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn sys_tick_inc() {
unsafe { Tick::on_sys_tick_interrupt() };
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct Tick(TickType);
impl Tick {
pub fn now() -> Self {
#[cfg(feature = "tick-size-64bit")]
loop {
let t0 = SYS_TICK_1.load(Ordering::SeqCst);
let t = SYS_TICK_0.load(Ordering::SeqCst);
let t1 = SYS_TICK_1.load(Ordering::SeqCst);
if t0 == t1 {
break Tick(((t0 as u64) << 32) | (t as u64));
}
}
#[cfg(not(feature = "tick-size-64bit"))]
Tick(SYS_TICK_0.load(Ordering::Relaxed))
}
pub fn elapsed(self) -> TickType {
if let Some(tick) = Self::now().0.checked_sub(self.0) {
tick
} else {
TickType::MAX
}
}
#[inline]
pub const fn with_value(value: TickType) -> Self {
Tick(value)
}
#[inline]
pub fn tick() -> TickType {
Self::now().0
}
pub fn elapsed_time(self) -> Duration<TickType, 1, TICK_FREQ_HZ> {
let tick = if let Some(res) = Self::now().0.checked_sub(self.0) {
res
} else {
TickType::MAX
};
Duration::<TickType, 1, TICK_FREQ_HZ>::from_ticks(tick)
}
pub fn every(&mut self, duration: TickType) -> bool {
let timeout = if let Some(elapsed) = Self::now().0.checked_sub(self.0) {
if elapsed >= duration { true } else { false }
} else {
true
};
if timeout {
self.0 = Self::now().0;
}
return timeout;
}
pub fn reset(&mut self) {
self.0 = Self::now().0;
}
}
impl core::ops::Add for Tick {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Tick(self.0 + rhs.0)
}
}
impl core::ops::Sub for Tick {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Tick(self.0 - rhs.0)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Delay;
impl Delay {
pub const fn new() -> Self {
Delay
}
}
impl DelayNs for Delay {
#[inline]
fn delay_ns(&mut self, ns: u32) {
ll_invoke_inner!(INVOKE_ID_DELAY_NANO, ns);
}
#[cfg(feature = "tick-based-msdelay")]
#[inline]
fn delay_ms(&mut self, ms: u32) {
let ms_tick = TimerDurationU32::<TICK_FREQ_HZ>::millis(ms).ticks();
let start = Tick::now();
loop {
unsafe {
core::arch::asm!("wfi");
}
if (start.elapsed() as u32) >= ms_tick {
break;
}
}
}
}
#[cfg(feature = "embassy")]
mod tick_time_driver {
use core::cell::{Cell, RefCell};
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
struct AlarmState {
timestamp: Cell<super::TickType>,
}
unsafe impl Send for AlarmState {}
struct TimerDriver {
alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
timestamp: Cell::new(0),
}),
queue: Mutex::new(RefCell::new(Queue::new()))
});
#[inline(always)]
pub fn check_alarm(curr_tick: super::TickType) {
DRIVER.check_alarm(curr_tick);
}
impl TimerDriver {
fn check_alarm(&self, curr_tick: super::TickType) {
critical_section::with(|cs| {
let alarm = &self.alarms.borrow(cs);
let mut timestamp = alarm.timestamp.get();
while timestamp <= curr_tick {
let mut queue = self.queue.borrow(cs).borrow_mut();
timestamp = queue.next_expiration(curr_tick as u64) as super::TickType; alarm.timestamp.set(timestamp); }
});
}
}
impl Driver for TimerDriver {
fn now(&self) -> u64 {
super::Tick::now().0 as u64
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let alarm = &self.alarms.borrow(cs);
let now = self.now();
loop {
let timestamp = queue.next_expiration(now) as super::TickType; alarm.timestamp.set(timestamp); if timestamp > now as super::TickType {
break;
}
}
}
})
}
}
}