use core::sync::atomic::{AtomicU8, Ordering};
use fugit::{MicrosDurationU32, MicrosDurationU64, TimerInstantU64};
use crate::{
atomic_register_access::{write_bitmask_clear, write_bitmask_set},
clocks::ClocksManager,
pac,
resets::SubsystemReset,
typelevel::Sealed,
};
pub type Instant = TimerInstantU64<1_000_000>;
static ALARMS_TIMER0: AtomicU8 = AtomicU8::new(0x00);
static ALARMS_TIMER1: AtomicU8 = AtomicU8::new(0x00);
fn take_alarm(mask: u8, alarms: &'static AtomicU8) -> bool {
let current_alarms = alarms.fetch_or(mask, Ordering::Relaxed);
(current_alarms & mask) == 0
}
fn release_alarm(mask: u8, alarms: &'static AtomicU8) {
alarms.fetch_and(!mask, Ordering::Relaxed);
}
#[derive(Clone, Copy)]
pub struct CopyableTimer0 {
_inner: (),
}
#[derive(Clone, Copy)]
pub struct CopyableTimer1 {
_inner: (),
}
pub trait TimerDevice: Sealed + Clone + Copy + 'static {
const ID: usize;
fn get_perif() -> &'static pac::timer0::RegisterBlock {
if Self::ID == 0 {
unsafe { &*pac::TIMER0::ptr() }
} else {
unsafe { &*pac::TIMER1::ptr() }
}
}
fn get_alarms_tracker() -> &'static AtomicU8 {
if Self::ID == 0 {
&ALARMS_TIMER0
} else {
&ALARMS_TIMER1
}
}
}
impl TimerDevice for CopyableTimer0 {
const ID: usize = 0;
}
impl Sealed for CopyableTimer0 {}
impl TimerDevice for CopyableTimer1 {
const ID: usize = 1;
}
impl Sealed for CopyableTimer1 {}
#[derive(Copy, Clone)]
pub struct Timer<D: TimerDevice> {
_device: core::marker::PhantomData<D>,
}
impl Timer<CopyableTimer0> {
pub fn new_timer0(
timer: pac::TIMER0,
resets: &mut pac::RESETS,
_clocks: &ClocksManager,
) -> Self {
timer.reset_bring_down(resets);
timer.reset_bring_up(resets);
Self {
_device: core::marker::PhantomData,
}
}
}
impl Timer<CopyableTimer1> {
pub fn new_timer1(
timer: pac::TIMER1,
resets: &mut pac::RESETS,
_clocks: &ClocksManager,
) -> Self {
timer.reset_bring_down(resets);
timer.reset_bring_up(resets);
Self {
_device: core::marker::PhantomData,
}
}
}
impl<D> Timer<D>
where
D: TimerDevice,
{
pub fn get_counter(&self) -> Instant {
let timer = D::get_perif();
let mut hi0 = timer.timerawh().read().bits();
let timestamp = loop {
let low = timer.timerawl().read().bits();
let hi1 = timer.timerawh().read().bits();
if hi0 == hi1 {
break (u64::from(hi0) << 32) | u64::from(low);
}
hi0 = hi1;
};
TimerInstantU64::from_ticks(timestamp)
}
pub fn get_counter_low(&self) -> u32 {
let timer = D::get_perif();
timer.timerawl().read().bits()
}
pub fn count_down(&self) -> CountDown<'_, D> {
CountDown {
timer: self,
period: MicrosDurationU64::nanos(0),
next_end: None,
}
}
pub fn alarm_0(&mut self) -> Option<Alarm0<D>> {
take_alarm(1 << 0, <D>::get_alarms_tracker()).then_some(Alarm0(*self))
}
pub fn alarm_1(&mut self) -> Option<Alarm1<D>> {
take_alarm(1 << 1, <D>::get_alarms_tracker()).then_some(Alarm1(*self))
}
pub fn alarm_2(&mut self) -> Option<Alarm2<D>> {
take_alarm(1 << 2, <D>::get_alarms_tracker()).then_some(Alarm2(*self))
}
pub fn alarm_3(&mut self) -> Option<Alarm3<D>> {
take_alarm(1 << 3, <D>::get_alarms_tracker()).then_some(Alarm3(*self))
}
fn delay_us_internal(&self, mut us: u32) {
let mut start = self.get_counter_low();
loop {
let now = self.get_counter_low();
let waited = now.wrapping_sub(start);
if waited >= us {
break;
}
start = now;
us -= waited;
}
}
}
macro_rules! impl_delay_traits {
($($t:ty),+) => {
$(
impl<D> embedded_hal_0_2::blocking::delay::DelayUs<$t> for Timer<D> where D: TimerDevice {
fn delay_us(&mut self, us: $t) {
#![allow(unused_comparisons)]
assert!(us >= 0); self.delay_us_internal(us as u32)
}
}
impl<D> embedded_hal_0_2::blocking::delay::DelayMs<$t> for Timer<D> where D: TimerDevice {
fn delay_ms(&mut self, ms: $t) {
#![allow(unused_comparisons)]
assert!(ms >= 0); for _ in 0..ms {
self.delay_us_internal(1000);
}
}
}
)*
}
}
impl_delay_traits!(u8, u16, u32, i32);
impl<D> embedded_hal::delay::DelayNs for Timer<D>
where
D: TimerDevice,
{
fn delay_ns(&mut self, ns: u32) {
let us = ns.div_ceil(1000);
self.delay_us_internal(us)
}
fn delay_us(&mut self, us: u32) {
self.delay_us_internal(us)
}
fn delay_ms(&mut self, ms: u32) {
for _ in 0..ms {
self.delay_us_internal(1000);
}
}
}
pub struct CountDown<'timer, D>
where
D: TimerDevice,
{
timer: &'timer Timer<D>,
period: MicrosDurationU64,
next_end: Option<u64>,
}
impl<D> embedded_hal_0_2::timer::CountDown for CountDown<'_, D>
where
D: TimerDevice,
{
type Time = MicrosDurationU64;
fn start<T>(&mut self, count: T)
where
T: Into<Self::Time>,
{
self.period = count.into();
self.next_end = Some(
self.timer
.get_counter()
.ticks()
.wrapping_add(self.period.to_micros()),
);
}
fn wait(&mut self) -> nb::Result<(), void::Void> {
if let Some(end) = self.next_end {
let ts = self.timer.get_counter().ticks();
if ts >= end {
self.next_end = Some(end.wrapping_add(self.period.to_micros()));
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
} else {
panic!("CountDown is not running!");
}
}
}
impl<D> embedded_hal_0_2::timer::Periodic for CountDown<'_, D> where D: TimerDevice {}
impl<D> embedded_hal_0_2::timer::Cancel for CountDown<'_, D>
where
D: TimerDevice,
{
type Error = &'static str;
fn cancel(&mut self) -> Result<(), Self::Error> {
if self.next_end.is_none() {
Err("CountDown is not running.")
} else {
self.next_end = None;
Ok(())
}
}
}
pub trait Alarm: Sealed {
fn clear_interrupt(&mut self);
fn enable_interrupt(&mut self);
fn disable_interrupt(&mut self);
fn schedule(&mut self, countdown: MicrosDurationU32) -> Result<(), ScheduleAlarmError>;
fn schedule_at(&mut self, timestamp: Instant) -> Result<(), ScheduleAlarmError>;
fn finished(&self) -> bool;
fn cancel(&mut self) -> Result<(), ScheduleAlarmError>;
}
macro_rules! impl_alarm {
($name:ident { rb: $timer_alarm:ident, int: $int_alarm:ident, int_name: $int_name:tt, armed_bit_mask: $armed_bit_mask: expr }) => {
pub struct $name<D>(Timer<D>)
where
D: TimerDevice;
impl<D> $name<D>
where
D: TimerDevice,
{
fn schedule_internal(&mut self, timestamp: Instant) -> Result<(), ScheduleAlarmError> {
let timestamp_low = (timestamp.ticks() & 0xFFFF_FFFF) as u32;
let timer = D::get_perif();
crate::arch::interrupt_free(|| {
let alarm = timer.$timer_alarm();
alarm.write(|w| unsafe { w.bits(timestamp_low) });
let now = self.0.get_counter();
if now > timestamp && (timer.armed().read().bits() & $armed_bit_mask) != 0 {
unsafe {
timer.armed().write_with_zero(|w| w.bits($armed_bit_mask));
crate::atomic_register_access::write_bitmask_set(
timer.intf().as_ptr(),
$armed_bit_mask,
);
}
}
Ok(())
})
}
}
impl<D> Alarm for $name<D>
where
D: TimerDevice,
{
#[doc = $int_name]
fn clear_interrupt(&mut self) {
unsafe {
let timer = D::get_perif();
crate::atomic_register_access::write_bitmask_clear(
timer.intf().as_ptr(),
$armed_bit_mask,
);
timer
.intr()
.write_with_zero(|w| w.$int_alarm().clear_bit_by_one());
}
}
#[doc = $int_name]
fn enable_interrupt(&mut self) {
unsafe {
let timer = D::get_perif();
let reg = timer.inte().as_ptr();
write_bitmask_set(reg, $armed_bit_mask);
}
}
fn disable_interrupt(&mut self) {
unsafe {
let timer = D::get_perif();
let reg = timer.inte().as_ptr();
write_bitmask_clear(reg, $armed_bit_mask);
}
}
#[doc = $int_name]
fn schedule(&mut self, countdown: MicrosDurationU32) -> Result<(), ScheduleAlarmError> {
let timestamp = self.0.get_counter() + countdown;
self.schedule_internal(timestamp)
}
#[doc = $int_name]
fn schedule_at(&mut self, timestamp: Instant) -> Result<(), ScheduleAlarmError> {
let now = self.0.get_counter();
let duration = timestamp.ticks().saturating_sub(now.ticks());
if duration > u32::MAX.into() {
return Err(ScheduleAlarmError::AlarmTooLate);
}
self.schedule_internal(timestamp)
}
fn finished(&self) -> bool {
let timer = D::get_perif();
let bits: u32 = timer.armed().read().bits();
(bits & $armed_bit_mask) == 0
}
fn cancel(&mut self) -> Result<(), ScheduleAlarmError> {
unsafe {
let timer = D::get_perif();
timer.armed().write_with_zero(|w| w.bits($armed_bit_mask));
crate::atomic_register_access::write_bitmask_clear(
timer.intf().as_ptr(),
$armed_bit_mask,
);
}
Ok(())
}
}
impl<D> Sealed for $name<D> where D: TimerDevice {}
impl<D> Drop for $name<D>
where
D: TimerDevice,
{
fn drop(&mut self) {
self.disable_interrupt();
release_alarm($armed_bit_mask, D::get_alarms_tracker());
}
}
};
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ScheduleAlarmError {
AlarmTooLate,
}
impl_alarm!(Alarm0 {
rb: alarm0,
int: alarm_0,
int_name: "TIMER_IRQ_0",
armed_bit_mask: 0b0001
});
impl_alarm!(Alarm1 {
rb: alarm1,
int: alarm_1,
int_name: "TIMER_IRQ_1",
armed_bit_mask: 0b0010
});
impl_alarm!(Alarm2 {
rb: alarm2,
int: alarm_2,
int_name: "TIMER_IRQ_2",
armed_bit_mask: 0b0100
});
impl_alarm!(Alarm3 {
rb: alarm3,
int: alarm_3,
int_name: "TIMER_IRQ_3",
armed_bit_mask: 0b1000
});
#[cfg(feature = "rtic-monotonic")]
pub mod monotonic {
use super::{Alarm, Instant, Timer, TimerDevice};
use fugit::ExtU32;
pub struct Monotonic<D: TimerDevice, A>(pub Timer<D>, A);
impl<D: TimerDevice, A: Alarm> Monotonic<D, A> {
pub const fn new(timer: Timer<D>, alarm: A) -> Self {
Self(timer, alarm)
}
}
impl<D: TimerDevice, A: Alarm> rtic_monotonic::Monotonic for Monotonic<D, A> {
type Instant = Instant;
type Duration = fugit::MicrosDurationU64;
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
fn now(&mut self) -> Instant {
self.0.get_counter()
}
fn set_compare(&mut self, instant: Instant) {
let max_instant = self.0.get_counter() + 0xFFFF_FFFE.micros();
let wake_at = core::cmp::min(instant, max_instant);
let _ = self.1.schedule_at(wake_at);
self.1.enable_interrupt();
}
fn clear_compare_flag(&mut self) {
self.1.clear_interrupt();
}
fn zero() -> Self::Instant {
Instant::from_ticks(0)
}
unsafe fn reset(&mut self) {}
}
}