use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering, compiler_fence};
use core::task::Poll;
use embassy_hal_internal::Peri;
use embassy_hal_internal::interrupt::InterruptExt;
use embassy_sync::waitqueue::AtomicWaker;
use embassy_time::Duration;
pub use crate::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
use crate::{interrupt, pac};
const POWMAN_PASSWORD: u32 = 0x5AFE << 16;
static WAKER: AtomicWaker = AtomicWaker::new();
static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AlarmWakeMode {
WfiOnly,
DormantOnly,
Both,
Disabled,
}
#[derive(Clone, Copy)]
pub struct Config {
pub clock_source: ClockSource,
pub clock_freq_khz: u32,
pub alarm_wake_mode: AlarmWakeMode,
}
impl Default for Config {
fn default() -> Self {
Self {
clock_source: ClockSource::Xosc,
clock_freq_khz: 12000, alarm_wake_mode: AlarmWakeMode::WfiOnly,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ClockSource {
Xosc,
Lposc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
AlarmInPast,
DateTime(DateTimeError),
}
pub struct AonTimer<'d> {
_phantom: PhantomData<&'d ()>,
config: Config,
}
impl<'d> AonTimer<'d> {
pub fn new(
_inner: Peri<'d, crate::peripherals::POWMAN>,
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::POWMAN_IRQ_TIMER, InterruptHandler> + 'd,
config: Config,
) -> Self {
let powman = pac::POWMAN;
match config.clock_source {
ClockSource::Xosc => {
powman.xosc_freq_khz_int().write(|w| {
w.0 = (config.clock_freq_khz & 0xFFFF) | POWMAN_PASSWORD;
*w
});
powman.xosc_freq_khz_frac().write(|w| {
w.0 = POWMAN_PASSWORD;
*w
});
}
ClockSource::Lposc => {
powman.lposc_freq_khz_int().write(|w| {
w.0 = (config.clock_freq_khz & 0xFFFF) | POWMAN_PASSWORD;
*w
});
powman.lposc_freq_khz_frac().write(|w| {
w.0 = POWMAN_PASSWORD;
*w
});
}
}
interrupt::POWMAN_IRQ_TIMER.unpend();
unsafe { interrupt::POWMAN_IRQ_TIMER.enable() };
Self {
_phantom: PhantomData,
config,
}
}
pub fn start(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
match self.config.clock_source {
ClockSource::Lposc => w.set_use_lposc(true),
ClockSource::Xosc => w.set_use_xosc(true),
}
w.set_run(true);
*w
});
}
pub fn stop(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_run(false);
*w
});
}
pub fn is_running(&self) -> bool {
let powman = pac::POWMAN;
powman.timer().read().run()
}
pub fn now(&self) -> u64 {
let powman = pac::POWMAN;
loop {
let upper1 = powman.read_time_upper().read();
let lower = powman.read_time_lower().read();
let upper2 = powman.read_time_upper().read();
if upper1 == upper2 {
return ((upper1 as u64) << 32) | (lower as u64);
}
}
}
pub fn set_counter(&mut self, value_ms: u64) {
if self.is_running() {
panic!("timer must be stopped before setting counter");
}
let powman = pac::POWMAN;
powman.set_time_15to0().write(|w| {
w.0 = ((value_ms & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.set_time_31to16().write(|w| {
w.0 = (((value_ms >> 16) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.set_time_47to32().write(|w| {
w.0 = (((value_ms >> 32) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.set_time_63to48().write(|w| {
w.0 = (((value_ms >> 48) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
}
pub fn set_alarm(&mut self, alarm_ms: u64) -> Result<(), Error> {
let current_ms = self.now();
if alarm_ms <= current_ms {
return Err(Error::AlarmInPast);
}
self.disable_alarm();
self.set_alarm_value(alarm_ms);
self.clear_alarm();
match self.config.alarm_wake_mode {
AlarmWakeMode::WfiOnly => {
self.disable_dormant_wake();
self.enable_alarm_interrupt();
}
AlarmWakeMode::DormantOnly => {
self.enable_dormant_wake();
self.disable_alarm_interrupt();
}
AlarmWakeMode::Both => {
self.enable_dormant_wake();
self.enable_alarm_interrupt();
}
AlarmWakeMode::Disabled => {
self.disable_dormant_wake();
self.disable_alarm_interrupt();
}
}
self.enable_alarm();
Ok(())
}
fn enable_alarm_interrupt(&mut self) {
let powman = pac::POWMAN;
powman.inte().modify(|w| w.set_timer(true));
}
pub fn disable_alarm_interrupt(&mut self) {
let powman = pac::POWMAN;
powman.inte().modify(|w| w.set_timer(false));
}
#[inline(always)]
fn set_alarm_value(&mut self, value: u64) {
let powman = pac::POWMAN;
powman.alarm_time_15to0().write(|w| {
w.0 = ((value & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.alarm_time_31to16().write(|w| {
w.0 = (((value >> 16) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.alarm_time_47to32().write(|w| {
w.0 = (((value >> 32) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
powman.alarm_time_63to48().write(|w| {
w.0 = (((value >> 48) & 0xFFFF) as u32) | POWMAN_PASSWORD;
*w
});
}
pub fn alarm_fired(&self) -> bool {
let powman = pac::POWMAN;
powman.timer().read().alarm()
}
pub fn clear_alarm(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_alarm(true); *w
});
}
pub fn disable_alarm(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_alarm_enab(false);
*w
});
}
pub fn enable_alarm(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_alarm_enab(true);
*w
});
}
pub fn enable_dormant_wake(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_pwrup_on_alarm(true);
*w
});
}
pub fn disable_dormant_wake(&mut self) {
let powman = pac::POWMAN;
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_pwrup_on_alarm(false);
*w
});
}
pub fn set_wake_mode(&mut self, mode: AlarmWakeMode) {
match mode {
AlarmWakeMode::WfiOnly => {
self.enable_alarm_interrupt();
self.disable_dormant_wake();
}
AlarmWakeMode::DormantOnly => {
self.disable_alarm_interrupt();
self.enable_dormant_wake();
}
AlarmWakeMode::Both => {
self.enable_alarm_interrupt();
self.enable_dormant_wake();
}
AlarmWakeMode::Disabled => {
self.disable_alarm_interrupt();
self.disable_dormant_wake();
}
}
self.config.alarm_wake_mode = mode;
}
pub fn set_alarm_after(&mut self, duration: Duration) -> Result<(), Error> {
let current_ms = self.now();
let alarm_ms = current_ms + duration.as_millis();
self.set_alarm(alarm_ms)
}
pub fn elapsed(&self) -> Duration {
Duration::from_millis(self.now())
}
pub fn set_datetime(&mut self, dt: DateTime) -> Result<(), DateTimeError> {
#[cfg(feature = "chrono")]
let millis = crate::datetime::timestamp_millis(&dt)?;
#[cfg(not(feature = "chrono"))]
let millis = dt.timestamp_millis()?;
self.set_counter(millis);
Ok(())
}
pub fn now_as_datetime(&self) -> Result<DateTime, DateTimeError> {
let millis = self.now();
#[cfg(feature = "chrono")]
return crate::datetime::from_timestamp_millis(millis);
#[cfg(not(feature = "chrono"))]
return DateTime::from_timestamp_millis(millis);
}
pub fn set_alarm_at_datetime(&mut self, dt: DateTime) -> Result<(), Error> {
#[cfg(feature = "chrono")]
let alarm_ms = crate::datetime::timestamp_millis(&dt).map_err(Error::DateTime)?;
#[cfg(not(feature = "chrono"))]
let alarm_ms = dt.timestamp_millis().map_err(Error::DateTime)?;
self.set_alarm(alarm_ms)
}
pub async fn wait_for_alarm(&mut self) {
poll_fn(|cx| {
WAKER.register(cx.waker());
if ALARM_OCCURRED.swap(false, Ordering::SeqCst) {
self.clear_alarm();
compiler_fence(Ordering::SeqCst);
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
}
pub struct InterruptHandler {
_empty: (),
}
impl interrupt::typelevel::Handler<interrupt::typelevel::POWMAN_IRQ_TIMER> for InterruptHandler {
#[inline(always)]
unsafe fn on_interrupt() {
let powman = crate::pac::POWMAN;
powman.inte().modify(|w| w.set_timer(false));
powman.timer().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;
w.set_alarm_enab(false);
*w
});
powman.intr().modify(|w| w.set_timer(true));
ALARM_OCCURRED.store(true, Ordering::SeqCst);
WAKER.wake();
}
}