atsamd_hal/peripherals/timer/
async_api.rs

1//! Async APIs for timers.
2//!
3//! Use [`TimerCounter::into_future`] to convert a regular [`TimerCounter`] into
4//! an asynchronous [`TimerFuture`].
5
6use crate::{
7    async_hal::interrupts::{Binding, Handler, Interrupt},
8    pac,
9    timer_traits::InterruptDrivenTimer,
10    typelevel::Sealed,
11};
12use atsamd_hal_macros::hal_cfg;
13use core::{
14    future::poll_fn,
15    marker::PhantomData,
16    sync::atomic::Ordering,
17    task::{Poll, Waker},
18};
19use embassy_sync::waitqueue::AtomicWaker;
20use fugit::NanosDurationU32;
21use portable_atomic::AtomicBool;
22
23use crate::peripherals::timer;
24
25#[hal_cfg("tc1")]
26#[allow(unused_imports)]
27use crate::pac::Tc1;
28
29#[hal_cfg("tc2")]
30#[allow(unused_imports)]
31use crate::pac::Tc2;
32
33#[hal_cfg("tc3")]
34#[allow(unused_imports)]
35use crate::pac::Tc3;
36
37#[hal_cfg("tc4")]
38#[allow(unused_imports)]
39use crate::pac::Tc4;
40
41#[hal_cfg("tc5")]
42#[allow(unused_imports)]
43use crate::pac::Tc5;
44
45use timer::{Count16, TimerCounter};
46
47#[hal_cfg("tc1-d11")]
48type RegBlock = pac::tc1::RegisterBlock;
49
50#[hal_cfg("tc3-d21")]
51type RegBlock = pac::tc3::RegisterBlock;
52
53#[hal_cfg("tc1-d5x")]
54type RegBlock = pac::tc0::RegisterBlock;
55
56/// Trait enabling the use of a Timer/Counter in async mode. Specifically, this
57/// trait enables us to register a `TC*` interrupt as a waker for timer futures.
58///
59/// **⚠️ Warning** This trait should not be implemented outside of this crate!
60pub trait AsyncCount16: Count16 + Sealed {
61    /// Index of this TC in the `STATE` tracker
62    const STATE_ID: usize;
63
64    /// Get a reference to the timer's register block
65    fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock;
66
67    /// Interrupt type for this timer
68    type Interrupt: Interrupt;
69}
70
71/// Interrupt handler for async timer operarions
72pub struct InterruptHandler<T: AsyncCount16> {
73    _private: (),
74    _tc: PhantomData<T>,
75}
76
77impl<T: AsyncCount16> crate::typelevel::Sealed for InterruptHandler<T> {}
78
79impl<A: AsyncCount16> Handler<A::Interrupt> for InterruptHandler<A> {
80    /// Callback function when the corresponding TC interrupt is fired
81    ///
82    /// # Safety
83    ///
84    /// This method may [`steal`](crate::pac::Peripherals::steal) the `TC`
85    /// peripheral instance to check the interrupt flags. The only
86    /// modifications it is allowed to apply to the peripheral is to clear
87    /// the interrupt flag (to prevent re-firing). This method should ONLY be
88    /// able to be called while a [`TimerFuture`] holds an unique reference
89    /// to the underlying `TC` peripheral.
90    unsafe fn on_interrupt() {
91        let periph = unsafe { crate::pac::Peripherals::steal() };
92        let tc = A::reg_block(&periph);
93        let intflag = &tc.count16().intflag();
94
95        if intflag.read().ovf().bit_is_set() {
96            // Clear the flag
97            intflag.modify(|_, w| w.ovf().set_bit());
98            STATE[A::STATE_ID].wake();
99        }
100    }
101}
102
103macro_rules! impl_async_count16 {
104    ($TC: ident, $id: expr) => {
105        paste::paste! {
106            impl AsyncCount16 for $TC {
107                const STATE_ID: usize = $id;
108
109                type Interrupt = crate::async_hal::interrupts::[< $TC:upper >];
110
111                fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock {
112                    &*peripherals.[< $TC:lower >]
113                }
114            }
115
116            impl Sealed for $TC {}
117        }
118    };
119}
120
121#[hal_cfg("tc1-d11")]
122impl_async_count16!(Tc1, 0);
123
124#[hal_cfg("tc3-d21")]
125impl_async_count16!(Tc3, 0);
126
127#[hal_cfg("tc4-d21")]
128impl_async_count16!(Tc4, 1);
129
130#[hal_cfg("tc5-d21")]
131impl_async_count16!(Tc5, 2);
132
133#[hal_cfg("tc2-d5x")]
134impl_async_count16!(Tc2, 0);
135
136#[hal_cfg("tc3-d5x")]
137impl_async_count16!(Tc3, 1);
138
139#[hal_cfg("tc4-d5x")]
140impl_async_count16!(Tc4, 2);
141
142#[hal_cfg("tc5-d5x")]
143impl_async_count16!(Tc5, 3);
144
145// Reserve space for the max number of timer peripherals based on chip type,
146// even though some wakers may not be used on some chips if they actually don't
147// exist on variant's hardware
148#[hal_cfg("tc1-d11")]
149const NUM_TIMERS: usize = 1;
150
151#[hal_cfg("tc3-d21")]
152const NUM_TIMERS: usize = 3;
153
154#[hal_cfg("tc3-d5x")]
155const NUM_TIMERS: usize = 4;
156
157impl<T> TimerCounter<T>
158where
159    T: AsyncCount16,
160{
161    /// Transform a [`TimerCounter`] into an [`TimerFuture`]
162    #[inline]
163    pub fn into_future<I>(mut self, _irq: I) -> TimerFuture<T>
164    where
165        I: Binding<T::Interrupt, InterruptHandler<T>>,
166    {
167        T::Interrupt::unpend();
168        unsafe { T::Interrupt::enable() };
169        self.enable_interrupt();
170
171        TimerFuture { timer: self }
172    }
173}
174
175/// Wrapper around a [`TimerCounter`] with an `async` interface
176pub struct TimerFuture<T>
177where
178    T: AsyncCount16,
179{
180    timer: TimerCounter<T>,
181}
182
183impl<T> TimerFuture<T>
184where
185    T: AsyncCount16,
186{
187    /// Delay asynchronously
188    #[inline]
189    pub async fn delay(&mut self, count: NanosDurationU32) {
190        self.timer.start(count);
191        self.timer.enable_interrupt();
192
193        poll_fn(|cx| {
194            STATE[T::STATE_ID].register(cx.waker());
195            if STATE[T::STATE_ID].ready() {
196                return Poll::Ready(());
197            }
198
199            Poll::Pending
200        })
201        .await;
202    }
203}
204
205impl<T> Drop for TimerFuture<T>
206where
207    T: AsyncCount16,
208{
209    #[inline]
210    fn drop(&mut self) {
211        T::Interrupt::disable();
212    }
213}
214
215impl<T> embedded_hal_async::delay::DelayNs for TimerFuture<T>
216where
217    T: AsyncCount16,
218{
219    async fn delay_ns(&mut self, ns: u32) {
220        self.delay(NanosDurationU32::from_ticks(ns).convert()).await;
221    }
222}
223
224// TODO instead of tracking the state manually, we could use ONESHOT
225// mode and check the STATUS.STOP bit
226struct State {
227    waker: AtomicWaker,
228    ready: AtomicBool,
229}
230
231impl State {
232    #[inline]
233    const fn new() -> Self {
234        Self {
235            waker: AtomicWaker::new(),
236            ready: AtomicBool::new(false),
237        }
238    }
239
240    #[inline]
241    fn register(&self, waker: &Waker) {
242        self.waker.register(waker)
243    }
244
245    #[inline]
246    fn wake(&self) {
247        self.ready.store(true, Ordering::SeqCst);
248        self.waker.wake()
249    }
250
251    #[inline]
252    fn ready(&self) -> bool {
253        self.ready.swap(false, Ordering::SeqCst)
254    }
255}
256
257#[allow(clippy::declare_interior_mutable_const)]
258const STATE_NEW: State = State::new();
259static STATE: [State; NUM_TIMERS] = [STATE_NEW; NUM_TIMERS];