use core::cell::{Cell, RefCell};
use core::task::Waker;
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m::peripheral::SYST;
use cortex_m_rt::exception;
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_time_driver::{Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use portable_atomic::{AtomicU64, Ordering};
struct AlarmState {
timestamp: Cell<u64>,
}
unsafe impl Send for AlarmState {}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
pub(crate) struct SysTickDriver {
ticks: AtomicU64,
alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
#[allow(clippy::declare_interior_mutable_const)]
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
embassy_time_driver::time_driver_impl!(static DRIVER: SysTickDriver = SysTickDriver {
ticks: AtomicU64::new(0),
alarm: Mutex::const_new(CriticalSectionRawMutex::new(), ALARM_STATE_NEW),
queue: Mutex::new(RefCell::new(Queue::new()))
});
impl SysTickDriver {
fn init(&'static self, _cs: CriticalSection, mut systick: SYST) -> bool {
let core_clock = unsafe { crate::rcc::get_freqs() }
.hclk1
.to_hertz()
.unwrap()
.0;
let reload_value = match (core_clock as u64).checked_div(TICK_HZ) {
Some(div) if div > 0 && div <= 0x00FFFFFF => (div - 1) as u32,
_ => panic!("Invalid SysTick reload value"), };
systick.set_clock_source(SystClkSource::Core); systick.set_reload(reload_value);
systick.clear_current();
systick.enable_counter();
systick.enable_interrupt();
true
}
fn on_systick(&self) {
critical_section::with(|cs| {
let current_ticks = self.ticks.fetch_add(1, Ordering::Relaxed);
self.check_and_trigger_alarm(current_ticks, cs);
});
}
#[inline]
fn check_and_trigger_alarm(&self, current_time: u64, cs: CriticalSection) {
let alarm = &self.alarm.borrow(cs);
let alarm_timestamp = alarm.timestamp.get();
if alarm_timestamp != u64::MAX && current_time >= alarm_timestamp {
let mut next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(current_time);
while !self.set_alarm(cs, next) {
next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
}
}
}
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
if self.now() >= timestamp {
return false;
}
self.alarm.borrow(cs).timestamp.set(timestamp);
if self.now() >= timestamp {
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
true
}
}
impl Driver for SysTickDriver {
fn now(&self) -> u64 {
self.ticks.load(Ordering::Relaxed)
}
fn schedule_wake(&self, at: u64, waker: &Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}
}
})
}
}
pub(crate) fn init(cs: CriticalSection, systick: SYST) {
DRIVER.init(cs, systick);
}
#[exception]
fn SysTick() {
DRIVER.on_systick();
}