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