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};
use crate::pac;
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]>,
period: AtomicU32,
}
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
embassy_time::time_driver_impl!(static DRIVER: SystickDriver = SystickDriver {
period: AtomicU32::new(1), alarm_count: AtomicU8::new(0),
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
});
impl SystickDriver {
fn init(&'static self) {
let rb = unsafe { &*pac::SYSTICK::PTR };
let hclk = crate::sysctl::clocks().hclk.to_Hz() as u64;
let cnt_per_second = hclk / 8;
let cnt_per_tick = cnt_per_second / embassy_time::TICK_HZ;
self.period.store(cnt_per_tick as u32, Ordering::Relaxed);
rb.cmp().write(|w| unsafe { w.bits(u64::MAX - 1) });
critical_section::with(|_| {
rb.sr().write(|w| w.cntif().bit(false)); rb.ctlr().modify(|_, w| {
w.init()
.set_bit()
.mode()
.upcount()
.stre()
.clear_bit()
.stclk()
.hclk_div8()
.ste()
.set_bit()
});
})
}
fn on_interrupt(&self) {
let rb = unsafe { &*pac::SYSTICK::PTR };
rb.ctlr().modify(|_, w| w.stie().clear_bit()); rb.sr().write(|w| w.cntif().bit(false));
critical_section::with(|cs| {
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 rb = unsafe { &*pac::SYSTICK::PTR };
rb.cnt().read().bits() / (self.period.load(Ordering::Relaxed) as u64)
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
if x < ALARM_COUNT as u8 {
Some(x + 1)
} else {
None
}
});
match id {
Ok(id) => Some(AlarmHandle::new(id)),
Err(_) => 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 _n = alarm.id();
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let rb = unsafe { &*pac::SYSTICK::PTR };
let t = self.now();
if timestamp <= t {
rb.ctlr().modify(|_, w| w.stie().clear_bit());
alarm.timestamp.set(u64::MAX);
return false;
}
let safe_timestamp = (timestamp + 1) * (self.period.load(Ordering::Relaxed) as u64);
rb.cmp().write(|w| unsafe { w.bits(safe_timestamp) });
rb.ctlr().modify(|_, w| w.stie().set_bit());
true
})
}
}
#[allow(non_snake_case)]
#[link_section = ".trap"]
#[no_mangle]
extern "C" fn SysTick() {
DRIVER.on_interrupt();
}
pub(crate) fn init() {
use qingke::interrupt::Priority;
use qingke_rt::CoreInterrupt;
DRIVER.init();
unsafe {
qingke::pfic::set_priority(CoreInterrupt::SysTick as u8, Priority::P15 as _);
qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8);
}
}