use core::cell::Cell;
use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
use core::{mem, ptr};
use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::{AlarmHandle, Driver};
pub const ALARM_COUNT: usize = 1;
struct AlarmState {
timestamp: Cell<u64>,
callback: Cell<*const ()>,
ctx: Cell<*mut ()>,
}
unsafe impl Send for AlarmState {}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
callback: Cell::new(ptr::null()),
ctx: Cell::new(ptr::null_mut()),
}
}
}
pub struct SystickDriver {
alarm_count: AtomicU8,
alarms: Mutex<[AlarmState; ALARM_COUNT]>,
tick_low: AtomicU32,
tick_high: AtomicU32,
}
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
embassy_time_driver::time_driver_impl!(static DRIVER: SystickDriver = SystickDriver {
alarm_count: AtomicU8::new(0),
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
tick_low: AtomicU32::new(1),
tick_high: AtomicU32::new(0),
});
impl SystickDriver {
fn init(&'static self) {
self.tick_low.store(1, Ordering::Relaxed);
self.tick_high.store(0, Ordering::Relaxed);
}
#[inline(always)]
fn on_interrupt(&self) {
if self.tick_low.load(Ordering::Relaxed) == u32::MAX {
self.tick_low.store(0, Ordering::Relaxed);
self.tick_high.store(
self.tick_high.load(Ordering::Relaxed) + 1,
Ordering::Relaxed);
}
self.tick_low.store(
self.tick_low.load(Ordering::Relaxed) + 1,
Ordering::Relaxed);
critical_section::with(|cs| {
let timestamp = self.alarms.borrow(cs)[0].timestamp.get();
if timestamp <= self.now() + 1 {
self.trigger_alarm(cs);
}
});
}
fn trigger_alarm(&self, cs: CriticalSection) {
let alarm = &self.alarms.borrow(cs)[0];
alarm.timestamp.set(u64::MAX);
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
f(alarm.ctx.get());
}
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
}
}
impl Driver for SystickDriver {
fn now(&self) -> u64 {
let low = self.tick_low.load(Ordering::Relaxed);
let high = self.tick_high.load(Ordering::Relaxed);
((high as u64) << 32) | (low as u64)
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
let old_count = self.alarm_count.load(Ordering::Acquire);
let id = if old_count < ALARM_COUNT as u8 {
self.alarm_count.store(old_count + 1, Ordering::Release);
Some(old_count)
} else {
None
};
match id {
Some(id) => Some(AlarmHandle::new(id)),
None => None,
}
}
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
critical_section::with(|cs| {
let alarm = self.get_alarm(cs, alarm);
alarm.callback.set(callback as *const ());
alarm.ctx.set(ctx);
})
}
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
if timestamp <= self.now() {
alarm.timestamp.set(u64::MAX);
return false;
}
true
})
}
}
#[inline]
pub(crate) fn init() {
DRIVER.init();
}
#[inline]
pub(crate) fn on_interrupt() {
DRIVER.on_interrupt();
}