atsamd_hal/peripherals/timer/
async_api.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! Async APIs for timers.
//!
//! Use [`TimerCounter::into_future`] to convert a regular [`TimerCounter`] into
//! an asynchronous [`TimerFuture`].

use crate::{
    async_hal::interrupts::{Binding, Handler, Interrupt},
    pac,
    timer_traits::InterruptDrivenTimer,
    typelevel::Sealed,
};
use atsamd_hal_macros::hal_cfg;
use core::{
    future::poll_fn,
    marker::PhantomData,
    sync::atomic::Ordering,
    task::{Poll, Waker},
};
use embassy_sync::waitqueue::AtomicWaker;
use fugit::NanosDurationU32;
use portable_atomic::AtomicBool;

use crate::peripherals::timer;

#[hal_cfg("tc1")]
#[allow(unused_imports)]
use crate::pac::Tc1;

#[hal_cfg("tc2")]
#[allow(unused_imports)]
use crate::pac::Tc2;

#[hal_cfg("tc3")]
#[allow(unused_imports)]
use crate::pac::Tc3;

#[hal_cfg("tc4")]
#[allow(unused_imports)]
use crate::pac::Tc4;

#[hal_cfg("tc5")]
#[allow(unused_imports)]
use crate::pac::Tc5;

use timer::{Count16, TimerCounter};

#[hal_cfg("tc1-d11")]
type RegBlock = pac::tc1::RegisterBlock;

#[hal_cfg("tc3-d21")]
type RegBlock = pac::tc3::RegisterBlock;

#[hal_cfg("tc1-d5x")]
type RegBlock = pac::tc0::RegisterBlock;

/// Trait enabling the use of a Timer/Counter in async mode. Specifically, this
/// trait enables us to register a `TC*` interrupt as a waker for timer futures.
///
/// **⚠️ Warning** This trait should not be implemented outside of this crate!
pub trait AsyncCount16: Count16 + Sealed {
    /// Index of this TC in the `STATE` tracker
    const STATE_ID: usize;

    /// Get a reference to the timer's register block
    fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock;

    /// Interrupt type for this timer
    type Interrupt: Interrupt;
}

/// Interrupt handler for async timer operarions
pub struct InterruptHandler<T: AsyncCount16> {
    _private: (),
    _tc: PhantomData<T>,
}

impl<T: AsyncCount16> crate::typelevel::Sealed for InterruptHandler<T> {}

impl<A: AsyncCount16> Handler<A::Interrupt> for InterruptHandler<A> {
    /// Callback function when the corresponding TC interrupt is fired
    ///
    /// # Safety
    ///
    /// This method may [`steal`](crate::pac::Peripherals::steal) the `TC`
    /// peripheral instance to check the interrupt flags. The only
    /// modifications it is allowed to apply to the peripheral is to clear
    /// the interrupt flag (to prevent re-firing). This method should ONLY be
    /// able to be called while a [`TimerFuture`] holds an unique reference
    /// to the underlying `TC` peripheral.
    unsafe fn on_interrupt() {
        let periph = unsafe { crate::pac::Peripherals::steal() };
        let tc = A::reg_block(&periph);
        let intflag = &tc.count16().intflag();

        if intflag.read().ovf().bit_is_set() {
            // Clear the flag
            intflag.modify(|_, w| w.ovf().set_bit());
            STATE[A::STATE_ID].wake();
        }
    }
}

macro_rules! impl_async_count16 {
    ($TC: ident, $id: expr) => {
        paste::paste! {
            impl AsyncCount16 for $TC {
                const STATE_ID: usize = $id;

                type Interrupt = crate::async_hal::interrupts::[< $TC:upper >];

                fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock {
                    &*peripherals.[< $TC:lower >]
                }
            }

            impl Sealed for $TC {}
        }
    };
}

#[hal_cfg("tc1-d11")]
impl_async_count16!(Tc1, 0);

#[hal_cfg("tc3-d21")]
impl_async_count16!(Tc3, 0);

#[hal_cfg("tc4-d21")]
impl_async_count16!(Tc4, 1);

#[hal_cfg("tc5-d21")]
impl_async_count16!(Tc5, 2);

#[hal_cfg("tc2-d5x")]
impl_async_count16!(Tc2, 0);

#[hal_cfg("tc3-d5x")]
impl_async_count16!(Tc3, 1);

#[hal_cfg("tc4-d5x")]
impl_async_count16!(Tc4, 2);

#[hal_cfg("tc5-d5x")]
impl_async_count16!(Tc5, 3);

// Reserve space for the max number of timer peripherals based on chip type,
// even though some wakers may not be used on some chips if they actually don't
// exist on variant's hardware
#[hal_cfg("tc1-d11")]
const NUM_TIMERS: usize = 1;

#[hal_cfg("tc3-d21")]
const NUM_TIMERS: usize = 3;

#[hal_cfg("tc3-d5x")]
const NUM_TIMERS: usize = 4;

impl<T> TimerCounter<T>
where
    T: AsyncCount16,
{
    /// Transform a [`TimerCounter`] into an [`TimerFuture`]
    #[inline]
    pub fn into_future<I>(mut self, _irq: I) -> TimerFuture<T>
    where
        I: Binding<T::Interrupt, InterruptHandler<T>>,
    {
        T::Interrupt::unpend();
        unsafe { T::Interrupt::enable() };
        self.enable_interrupt();

        TimerFuture { timer: self }
    }
}

/// Wrapper around a [`TimerCounter`] with an `async` interface
pub struct TimerFuture<T>
where
    T: AsyncCount16,
{
    timer: TimerCounter<T>,
}

impl<T> TimerFuture<T>
where
    T: AsyncCount16,
{
    /// Delay asynchronously
    #[inline]
    pub async fn delay(&mut self, count: NanosDurationU32) {
        self.timer.start(count);
        self.timer.enable_interrupt();

        poll_fn(|cx| {
            STATE[T::STATE_ID].register(cx.waker());
            if STATE[T::STATE_ID].ready() {
                return Poll::Ready(());
            }

            Poll::Pending
        })
        .await;
    }
}

impl<T> Drop for TimerFuture<T>
where
    T: AsyncCount16,
{
    #[inline]
    fn drop(&mut self) {
        T::Interrupt::disable();
    }
}

impl<T> embedded_hal_async::delay::DelayNs for TimerFuture<T>
where
    T: AsyncCount16,
{
    async fn delay_ns(&mut self, ns: u32) {
        self.delay(NanosDurationU32::from_ticks(ns).convert()).await;
    }
}

// TODO instead of tracking the state manually, we could use ONESHOT
// mode and check the STATUS.STOP bit
struct State {
    waker: AtomicWaker,
    ready: AtomicBool,
}

impl State {
    #[inline]
    const fn new() -> Self {
        Self {
            waker: AtomicWaker::new(),
            ready: AtomicBool::new(false),
        }
    }

    #[inline]
    fn register(&self, waker: &Waker) {
        self.waker.register(waker)
    }

    #[inline]
    fn wake(&self) {
        self.ready.store(true, Ordering::SeqCst);
        self.waker.wake()
    }

    #[inline]
    fn ready(&self) -> bool {
        self.ready.swap(false, Ordering::SeqCst)
    }
}

#[allow(clippy::declare_interior_mutable_const)]
const STATE_NEW: State = State::new();
static STATE: [State; NUM_TIMERS] = [STATE_NEW; NUM_TIMERS];