atsamd_hal/peripherals/timer/
d11.rs

1//! Working with timer counter hardware
2use core::convert::Infallible;
3
4use atsamd_hal_macros::hal_cfg;
5use fugit::NanosDurationU32;
6
7use crate::ehal_02::timer::{CountDown, Periodic};
8use crate::pac::Pm;
9#[hal_cfg("tc1-d11")]
10use crate::pac::{tc1::Count16 as Count16Reg, Tc1};
11#[hal_cfg("tc3-d21")]
12use crate::pac::{tc3::Count16 as Count16Reg, Tc3, Tc4, Tc5};
13use crate::timer_params::TimerParams;
14
15use crate::clock;
16use crate::time::{Hertz, Nanoseconds};
17use crate::timer_traits::InterruptDrivenTimer;
18
19#[cfg(feature = "async")]
20mod async_api;
21
22#[cfg(feature = "async")]
23pub use async_api::*;
24
25// Note:
26// TC3 + TC4 can be paired to make a 32-bit counter
27// TC5 + TC6 can be paired to make a 32-bit counter
28
29/// A generic hardware timer counter.
30///
31/// The counters are exposed in 16-bit mode only.
32/// The hardware allows configuring the 8-bit mode
33/// and pairing up some instances to run in 32-bit
34/// mode, but that functionality is not currently
35/// exposed by this hal implementation.
36/// TimerCounter implements both the `Periodic` and
37/// the `CountDown` embedded_hal timer traits.
38/// Before a hardware timer can be used, it must first
39/// have a clock configured.
40pub struct TimerCounter<TC> {
41    freq: Hertz,
42    tc: TC,
43}
44
45impl<TC: Count16> TimerCounter<TC> {}
46
47/// This is a helper trait to make it easier to make most of the
48/// TimerCounter impl generic.  It doesn't make too much sense to
49/// to try to implement this trait outside of this module.
50pub trait Count16 {
51    fn count_16(&self) -> &Count16Reg;
52}
53
54impl<TC> Periodic for TimerCounter<TC> {}
55impl<TC> CountDown for TimerCounter<TC>
56where
57    TC: Count16,
58{
59    type Time = Nanoseconds;
60
61    fn start<T>(&mut self, timeout: T)
62    where
63        T: Into<Self::Time>,
64    {
65        <Self as InterruptDrivenTimer>::start(self, timeout);
66    }
67
68    fn wait(&mut self) -> nb::Result<(), void::Void> {
69        nb::block! {
70            <Self as InterruptDrivenTimer>::wait(self)
71        }
72        .unwrap(); // wait() is Infallible
73        Ok(())
74    }
75}
76
77impl<TC> InterruptDrivenTimer for TimerCounter<TC>
78where
79    TC: Count16,
80{
81    /// Enable the interrupt generation for this hardware timer.
82    /// This method only sets the clock configuration to trigger
83    /// the interrupt; it does not configure the interrupt controller
84    /// or define an interrupt handler.
85    fn enable_interrupt(&mut self) {
86        self.tc.count_16().intenset().write(|w| w.ovf().set_bit());
87    }
88
89    fn start<T: Into<NanosDurationU32>>(&mut self, timeout: T) {
90        let params = TimerParams::new_ns(timeout.into(), self.freq);
91        let divider = params.divider;
92        let cycles = params.cycles;
93
94        let count = self.tc.count_16();
95
96        // Disable the timer while we reconfigure it
97        count.ctrla().modify(|_, w| w.enable().clear_bit());
98        while count.status().read().syncbusy().bit_is_set() {}
99
100        // Now that we have a clock routed to the peripheral, we
101        // can ask it to perform a reset.
102        count.ctrla().write(|w| w.swrst().set_bit());
103        while count.status().read().syncbusy().bit_is_set() {}
104        // the SVD erroneously marks swrst as write-only, so we
105        // need to manually read the bit here
106        while count.ctrla().read().bits() & 1 != 0 {}
107
108        count.ctrlbset().write(|w| {
109            // Count up when the direction bit is zero
110            w.dir().clear_bit();
111            // Periodic
112            w.oneshot().clear_bit()
113        });
114
115        // Set TOP value for mfrq mode
116        count.cc(0).write(|w| unsafe { w.cc().bits(cycles as u16) });
117
118        count.ctrla().modify(|_, w| {
119            match divider {
120                1 => w.prescaler().div1(),
121                2 => w.prescaler().div2(),
122                4 => w.prescaler().div4(),
123                8 => w.prescaler().div8(),
124                16 => w.prescaler().div16(),
125                64 => w.prescaler().div64(),
126                256 => w.prescaler().div256(),
127                1024 => w.prescaler().div1024(),
128                _ => unreachable!(),
129            };
130            // Enable Match Frequency Waveform generation
131            w.wavegen().mfrq();
132            w.enable().set_bit();
133            w.runstdby().set_bit()
134        });
135    }
136
137    fn wait(&mut self) -> nb::Result<(), Infallible> {
138        let count = self.tc.count_16();
139        if count.intflag().read().ovf().bit_is_set() {
140            // Writing a 1 clears the flag
141            count.intflag().modify(|_, w| w.ovf().set_bit());
142            Ok(())
143        } else {
144            Err(nb::Error::WouldBlock)
145        }
146    }
147
148    /// Disables interrupt generation for this hardware timer.
149    /// This method only sets the clock configuration to prevent
150    /// triggering the interrupt; it does not configure the interrupt
151    /// controller.
152    fn disable_interrupt(&mut self) {
153        self.tc.count_16().intenclr().write(|w| w.ovf().set_bit());
154    }
155}
156
157macro_rules! tc {
158    ($($TYPE:ident: ($TC:ident, $pm:ident, $clock:ident),)+) => {
159        $(
160pub type $TYPE = TimerCounter<$TC>;
161
162impl Count16 for $TC {
163    fn count_16(&self) -> &Count16Reg {
164        self.count16()
165    }
166}
167
168impl TimerCounter<$TC>
169{
170    /// Configure this timer counter instance.
171    /// The clock is obtained from the `GenericClockController` instance
172    /// and its frequency impacts the resolution and maximum range of
173    /// the timeout values that can be passed to the `start` method.
174    /// Note that some hardware timer instances share the same clock
175    /// generator instance and thus will be clocked at the same rate.
176    pub fn $pm(clock: &clock::$clock, tc: $TC, pm: &mut Pm) -> Self {
177        // this is safe because we're constrained to just the tc3 bit
178        pm.apbcmask().modify(|_, w| w.$pm().set_bit());
179        {
180            let count = tc.count_16();
181
182            // Disable the timer while we reconfigure it
183            count.ctrla().modify(|_, w| w.enable().clear_bit());
184            while count.status().read().syncbusy().bit_is_set() {}
185        }
186        Self {
187            freq: clock.freq(),
188            tc,
189        }
190    }
191}
192        )+
193    }
194}
195
196// samd11
197#[hal_cfg("tc1-d11")]
198tc! {
199    TimerCounter1: (Tc1, tc1_, Tc1Tc2Clock),
200}
201// samd21
202#[hal_cfg("tc3-d21")]
203tc! {
204    TimerCounter3: (Tc3, tc3_, Tcc2Tc3Clock),
205    TimerCounter4: (Tc4, tc4_, Tc4Tc5Clock),
206    TimerCounter5: (Tc5, tc5_, Tc4Tc5Clock),
207}