1//! Working with timer counter hardware
2use core::convert::Infallible;
34use atsamd_hal_macros::hal_cfg;
5use fugit::NanosDurationU32;
67use 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;
1415use crate::clock;
16use crate::time::{Hertz, Nanoseconds};
17use crate::timer_traits::InterruptDrivenTimer;
1819#[cfg(feature = "async")]
20mod async_api;
2122#[cfg(feature = "async")]
23pub use async_api::*;
2425// 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
2829/// 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}
4445impl<TC: Count16> TimerCounter<TC> {}
4647/// 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 {
51fn count_16(&self) -> &Count16Reg;
52}
5354impl<TC> Periodic for TimerCounter<TC> {}
55impl<TC> CountDown for TimerCounter<TC>
56where
57TC: Count16,
58{
59type Time = Nanoseconds;
6061fn start<T>(&mut self, timeout: T)
62where
63T: Into<Self::Time>,
64 {
65 <Self as InterruptDrivenTimer>::start(self, timeout);
66 }
6768fn wait(&mut self) -> nb::Result<(), void::Void> {
69nb::block! {
70 <Self as InterruptDrivenTimer>::wait(self)
71 }
72 .unwrap(); // wait() is Infallible
73Ok(())
74 }
75}
7677impl<TC> InterruptDrivenTimer for TimerCounter<TC>
78where
79TC: 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.
85fn enable_interrupt(&mut self) {
86self.tc.count_16().intenset().write(|w| w.ovf().set_bit());
87 }
8889fn start<T: Into<NanosDurationU32>>(&mut self, timeout: T) {
90let params = TimerParams::new_ns(timeout.into(), self.freq);
91let divider = params.divider;
92let cycles = params.cycles;
9394let count = self.tc.count_16();
9596// Disable the timer while we reconfigure it
97count.ctrla().modify(|_, w| w.enable().clear_bit());
98while count.status().read().syncbusy().bit_is_set() {}
99100// Now that we have a clock routed to the peripheral, we
101 // can ask it to perform a reset.
102count.ctrla().write(|w| w.swrst().set_bit());
103while 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
106while count.ctrla().read().bits() & 1 != 0 {}
107108 count.ctrlbset().write(|w| {
109// Count up when the direction bit is zero
110w.dir().clear_bit();
111// Periodic
112w.oneshot().clear_bit()
113 });
114115// Set TOP value for mfrq mode
116count.cc(0).write(|w| unsafe { w.cc().bits(cycles as u16) });
117118 count.ctrla().modify(|_, w| {
119match divider {
1201 => w.prescaler().div1(),
1212 => w.prescaler().div2(),
1224 => w.prescaler().div4(),
1238 => w.prescaler().div8(),
12416 => w.prescaler().div16(),
12564 => w.prescaler().div64(),
126256 => w.prescaler().div256(),
1271024 => w.prescaler().div1024(),
128_ => unreachable!(),
129 };
130// Enable Match Frequency Waveform generation
131w.wavegen().mfrq();
132 w.enable().set_bit();
133 w.runstdby().set_bit()
134 });
135 }
136137fn wait(&mut self) -> nb::Result<(), Infallible> {
138let count = self.tc.count_16();
139if count.intflag().read().ovf().bit_is_set() {
140// Writing a 1 clears the flag
141count.intflag().modify(|_, w| w.ovf().set_bit());
142Ok(())
143 } else {
144Err(nb::Error::WouldBlock)
145 }
146 }
147148/// 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.
152fn disable_interrupt(&mut self) {
153self.tc.count_16().intenclr().write(|w| w.ovf().set_bit());
154 }
155}
156157macro_rules! tc {
158 ($($TYPE:ident: ($TC:ident, $pm:ident, $clock:ident),)+) => {
159 $(
160pub type $TYPE = TimerCounter<$TC>;
161162impl Count16 for $TC {
163fn count_16(&self) -> &Count16Reg {
164self.count16()
165 }
166}
167168impl 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.
176pub fn $pm(clock: &clock::$clock, tc: $TC, pm: &mut Pm) -> Self {
177// this is safe because we're constrained to just the tc3 bit
178pm.apbcmask().modify(|_, w| w.$pm().set_bit());
179 {
180let count = tc.count_16();
181182// Disable the timer while we reconfigure it
183count.ctrla().modify(|_, w| w.enable().clear_bit());
184while count.status().read().syncbusy().bit_is_set() {}
185 }
186Self {
187 freq: clock.freq(),
188 tc,
189 }
190 }
191}
192 )+
193 }
194}
195196// 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}