#![allow(non_snake_case)]
#[cfg(feature = "low-power")]
use core::cell::Cell;
use core::cell::RefCell;
use core::sync::atomic::{AtomicU32, Ordering, compiler_fence};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time_driver::{Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use stm32_metapac::timer::{TimGp16, regs};
use super::AlarmState;
use crate::interrupt::typelevel::Interrupt;
use crate::pac::timer::vals;
use crate::peripherals;
use crate::rcc::{self, SealedRccPeripheral};
#[cfg(feature = "low-power")]
use crate::rtc::Rtc;
use crate::timer::{CoreInstance, GeneralInstance1Channel};
#[cfg(time_driver_tim1)]
type T = peripherals::TIM1;
#[cfg(time_driver_tim2)]
type T = peripherals::TIM2;
#[cfg(time_driver_tim3)]
type T = peripherals::TIM3;
#[cfg(time_driver_tim4)]
type T = peripherals::TIM4;
#[cfg(time_driver_tim5)]
type T = peripherals::TIM5;
#[cfg(time_driver_tim8)]
type T = peripherals::TIM8;
#[cfg(time_driver_tim9)]
type T = peripherals::TIM9;
#[cfg(time_driver_tim12)]
type T = peripherals::TIM12;
#[cfg(time_driver_tim15)]
type T = peripherals::TIM15;
#[cfg(time_driver_tim20)]
type T = peripherals::TIM20;
#[cfg(time_driver_tim21)]
type T = peripherals::TIM21;
#[cfg(time_driver_tim22)]
type T = peripherals::TIM22;
#[cfg(time_driver_tim23)]
type T = peripherals::TIM23;
#[cfg(time_driver_tim24)]
type T = peripherals::TIM24;
fn regs_gp16() -> TimGp16 {
unsafe { TimGp16::from_ptr(T::regs()) }
}
fn calc_now(period: u32, counter: u16) -> u64 {
((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
}
#[cfg(feature = "low-power")]
fn calc_period_counter(ticks: u64) -> (u32, u16) {
(2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16)
}
pub(crate) struct RtcDriver {
period: AtomicU32,
alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
#[cfg(feature = "low-power")]
pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,
#[cfg(feature = "low-power")]
min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>,
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
period: AtomicU32::new(0),
alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
#[cfg(feature = "low-power")]
rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)),
#[cfg(feature = "low-power")]
min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))),
queue: Mutex::new(RefCell::new(Queue::new()))
});
impl RtcDriver {
pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
let r = regs_gp16();
rcc::enable_and_reset_with_cs::<T>(cs);
let timer_freq = T::frequency();
r.cr1().modify(|w| w.set_cen(false));
r.cnt().write(|w| w.set_cnt(0));
let psc = timer_freq.0 / TICK_HZ as u32 - 1;
let psc: u16 = match psc.try_into() {
Err(_) => panic!("psc division overflow: {}", psc),
Ok(n) => n,
};
r.psc().write_value(psc);
r.arr().write(|w| w.set_arr(u16::MAX));
r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTER_ONLY));
r.egr().write(|w| w.set_ug(true));
r.cr1().modify(|w| w.set_urs(vals::Urs::ANY_EVENT));
r.ccr(0).write(|w| w.set_ccr(0x8000));
r.dier().write(|w| {
w.set_uie(true);
w.set_ccie(0, true);
});
<T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
<T as CoreInstance>::UpdateInterrupt::unpend();
unsafe {
<T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable();
<T as CoreInstance>::UpdateInterrupt::enable();
#[cfg(feature = "low-power")]
crate::rcc::reset_stop_refcount(cs);
}
}
fn init(&'static self, cs: CriticalSection) {
self.init_timer(cs);
regs_gp16().cr1().modify(|w| w.set_cen(true));
}
pub(crate) fn on_interrupt(&self) {
let r = regs_gp16();
critical_section::with(|cs| {
let sr = r.sr().read();
let dier = r.dier().read();
r.sr().write_value(regs::SrGp16(!sr.0));
if sr.uif() {
self.next_period();
}
if sr.ccif(0) {
self.next_period();
}
let n = 0;
if sr.ccif(n + 1) && dier.ccie(n + 1) {
self.trigger_alarm(cs);
}
})
}
fn next_period(&self) {
let r = regs_gp16();
let period = self.period.load(Ordering::Relaxed) + 1;
self.period.store(period, Ordering::Relaxed);
let t = (period as u64) << 15;
critical_section::with(move |cs| {
r.dier().modify(move |w| {
let n = 0;
let alarm = self.alarm.borrow(cs);
let at = alarm.timestamp.get();
if at < t + 0xc000 {
w.set_ccie(n + 1, true);
}
})
})
}
fn trigger_alarm(&self, cs: CriticalSection) {
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
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 {
let r = regs_gp16();
let n = 0;
self.alarm.borrow(cs).timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
r.dier().modify(|w| w.set_ccie(n + 1, false));
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
let diff = timestamp - t;
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
let t = self.now();
if timestamp <= t {
r.dier().modify(|w| w.set_ccie(n + 1, false));
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
true
}
#[cfg(feature = "low-power")]
fn set_time(&self, instant: u64, cs: CriticalSection) {
let (period, counter) = calc_period_counter(core::cmp::max(self.now(), instant));
self.period.store(period, Ordering::SeqCst);
regs_gp16().cnt().write(|w| w.set_cnt(counter));
let alarm = self.alarm.borrow(cs);
if !self.set_alarm(cs, alarm.timestamp.get()) {
self.trigger_alarm(cs);
}
}
}
#[cfg(feature = "low-power")]
impl super::LPTimeDriver for RtcDriver {
fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration {
let now = self.now() + 32;
embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now))
}
fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) {
self.min_stop_pause.borrow(cs).replace(min_stop_pause);
}
fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) {
rtc.stop_wakeup_alarm();
assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none());
}
fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> {
assert!(regs_gp16().cr1().read().cen());
let time_until_next_alarm = self.time_until_next_alarm(cs);
if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {
trace!(
"time_until_next_alarm < self.min_stop_pause ({})",
time_until_next_alarm
);
Err(())
} else {
self.rtc
.borrow(cs)
.borrow_mut()
.as_mut()
.unwrap()
.start_wakeup_alarm(time_until_next_alarm);
regs_gp16().cr1().modify(|w| w.set_cen(false));
Ok(())
}
}
fn resume_time(&self, cs: CriticalSection) {
assert!(!regs_gp16().cr1().read().cen());
self.set_time(
self.rtc
.borrow(cs)
.borrow_mut()
.as_mut()
.unwrap()
.stop_wakeup_alarm()
.as_ticks(),
cs,
);
regs_gp16().cr1().modify(|w| w.set_cen(true));
}
fn is_stopped(&self) -> bool {
!regs_gp16().cr1().read().cen()
}
}
impl Driver for RtcDriver {
fn now(&self) -> u64 {
let r = regs_gp16();
let period = self.period.load(Ordering::Relaxed);
compiler_fence(Ordering::Acquire);
let counter = r.cnt().read().cnt();
calc_now(period, counter)
}
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 mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}
}
})
}
}
pub(crate) const fn get_driver() -> &'static RtcDriver {
&DRIVER
}
pub(crate) fn init(cs: CriticalSection) {
DRIVER.init(cs)
}