embassy_nrf/
timer.rs

1//! Timer driver.
2//!
3//! Important note! This driver is very low level. For most time-related use cases, like
4//! "sleep for X seconds", "do something every X seconds", or measuring time, you should
5//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead!
6
7#![macro_use]
8
9use core::marker::PhantomData;
10
11use embassy_hal_internal::{Peri, PeripheralType};
12
13use crate::pac;
14use crate::pac::timer::vals;
15use crate::ppi::{Event, Task};
16
17pub(crate) trait SealedInstance {
18    /// The number of CC registers this instance has.
19    const CCS: usize;
20    fn regs() -> pac::timer::Timer;
21}
22
23/// Basic Timer instance.
24#[allow(private_bounds)]
25pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
26    /// Interrupt for this peripheral.
27    type Interrupt: crate::interrupt::typelevel::Interrupt;
28}
29
30/// Extended timer instance.
31pub trait ExtendedInstance: Instance {}
32
33macro_rules! impl_timer {
34    ($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => {
35        impl crate::timer::SealedInstance for peripherals::$type {
36            const CCS: usize = $ccs;
37            fn regs() -> pac::timer::Timer {
38                unsafe { pac::timer::Timer::from_ptr(pac::$pac_type.as_ptr()) }
39            }
40        }
41        impl crate::timer::Instance for peripherals::$type {
42            type Interrupt = crate::interrupt::typelevel::$irq;
43        }
44    };
45    ($type:ident, $pac_type:ident, $irq:ident) => {
46        impl_timer!($type, $pac_type, $irq, 4);
47    };
48    ($type:ident, $pac_type:ident, $irq:ident, extended) => {
49        impl_timer!($type, $pac_type, $irq, 6);
50        impl crate::timer::ExtendedInstance for peripherals::$type {}
51    };
52}
53
54/// Timer frequency
55#[repr(u8)]
56pub enum Frequency {
57    /// 16MHz
58    F16MHz = 0,
59    /// 8MHz
60    F8MHz = 1,
61    /// 4MHz
62    F4MHz = 2,
63    /// 2MHz
64    F2MHz = 3,
65    /// 1MHz
66    F1MHz = 4,
67    /// 500kHz
68    F500kHz = 5,
69    /// 250kHz
70    F250kHz = 6,
71    /// 125kHz
72    F125kHz = 7,
73    /// 62500Hz
74    F62500Hz = 8,
75    /// 31250Hz
76    F31250Hz = 9,
77}
78
79/// nRF Timer driver.
80///
81/// The timer has an internal counter, which is incremented for every tick of the timer.
82/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32.
83///
84/// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter
85/// or trigger an event when the counter reaches a certain value.
86pub struct Timer<'d> {
87    r: pac::timer::Timer,
88    ccs: usize,
89    _p: PhantomData<&'d ()>,
90}
91
92impl<'d> Timer<'d> {
93    /// Create a new `Timer` driver.
94    ///
95    /// This can be useful for triggering tasks via PPI.
96    /// `Uarte` uses this internally.
97    pub fn new<T: Instance>(timer: Peri<'d, T>) -> Self {
98        Self::new_inner(timer, false)
99    }
100
101    /// Create a new `Timer` driver in counter mode.
102    ///
103    /// This can be useful for triggering tasks via PPI.
104    /// `Uarte` uses this internally.
105    pub fn new_counter<T: Instance>(timer: Peri<'d, T>) -> Self {
106        Self::new_inner(timer, true)
107    }
108
109    fn new_inner<T: Instance>(_timer: Peri<'d, T>, is_counter: bool) -> Self {
110        let regs = T::regs();
111
112        let this = Self {
113            r: regs,
114            ccs: T::CCS,
115            _p: PhantomData,
116        };
117
118        // Stop the timer before doing anything else,
119        // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
120        this.stop();
121
122        regs.mode().write(|w| {
123            w.set_mode(match is_counter {
124                #[cfg(not(feature = "_nrf51"))]
125                true => vals::Mode::LOW_POWER_COUNTER,
126                #[cfg(feature = "_nrf51")]
127                true => vals::Mode::COUNTER,
128                false => vals::Mode::TIMER,
129            })
130        });
131
132        // Make the counter's max value as high as possible.
133        // TODO: is there a reason someone would want to set this lower?
134        regs.bitmode().write(|w| w.set_bitmode(vals::Bitmode::_32BIT));
135
136        // Initialize the counter at 0.
137        this.clear();
138
139        // Default to the max frequency of the lower power clock
140        this.set_frequency(Frequency::F1MHz);
141
142        for n in 0..this.ccs {
143            let cc = this.cc(n);
144            // Initialize all the shorts as disabled.
145            cc.unshort_compare_clear();
146            cc.unshort_compare_stop();
147            // Initialize the CC registers as 0.
148            cc.write(0);
149        }
150
151        this
152    }
153
154    /// Direct access to the register block.
155    #[cfg(feature = "unstable-pac")]
156    #[inline]
157    pub fn regs(&mut self) -> pac::timer::Timer {
158        self.r
159    }
160
161    /// Starts the timer.
162    pub fn start(&self) {
163        self.r.tasks_start().write_value(1)
164    }
165
166    /// Stops the timer.
167    pub fn stop(&self) {
168        self.r.tasks_stop().write_value(1)
169    }
170
171    /// Reset the timer's counter to 0.
172    pub fn clear(&self) {
173        self.r.tasks_clear().write_value(1)
174    }
175
176    /// Returns the START task, for use with PPI.
177    ///
178    /// When triggered, this task starts the timer.
179    pub fn task_start(&self) -> Task<'d> {
180        Task::from_reg(self.r.tasks_start())
181    }
182
183    /// Returns the STOP task, for use with PPI.
184    ///
185    /// When triggered, this task stops the timer.
186    pub fn task_stop(&self) -> Task<'d> {
187        Task::from_reg(self.r.tasks_stop())
188    }
189
190    /// Returns the CLEAR task, for use with PPI.
191    ///
192    /// When triggered, this task resets the timer's counter to 0.
193    pub fn task_clear(&self) -> Task<'d> {
194        Task::from_reg(self.r.tasks_clear())
195    }
196
197    /// Returns the COUNT task, for use with PPI.
198    ///
199    /// When triggered, this task increments the timer's counter by 1.
200    /// Only works in counter mode.
201    pub fn task_count(&self) -> Task<'d> {
202        Task::from_reg(self.r.tasks_count())
203    }
204
205    /// Change the timer's frequency.
206    ///
207    /// This will stop the timer if it isn't already stopped,
208    /// because the timer may exhibit 'unpredictable behaviour' if it's frequency is changed while it's running.
209    pub fn set_frequency(&self, frequency: Frequency) {
210        self.stop();
211
212        self.r
213            .prescaler()
214            // SAFETY: `frequency` is a variant of `Frequency`,
215            // whose values are all in the range of 0-9 (the valid range of `prescaler`).
216            .write(|w| w.set_prescaler(frequency as u8))
217    }
218
219    /// Returns this timer's `n`th CC register.
220    ///
221    /// # Panics
222    /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer).
223    pub fn cc(&self, n: usize) -> Cc<'d> {
224        if n >= self.ccs {
225            panic!("Cannot get CC register {} of timer with {} CC registers.", n, self.ccs);
226        }
227        Cc {
228            n,
229            r: self.r,
230            _p: PhantomData,
231        }
232    }
233}
234
235impl Timer<'static> {
236    /// Persist the timer's configuration for the rest of the program's lifetime. This method
237    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents
238    /// accidental reuse of the underlying peripheral.
239    pub fn persist(self) {
240        core::mem::forget(self);
241    }
242}
243
244impl<'d> Drop for Timer<'d> {
245    fn drop(&mut self) {
246        self.stop();
247    }
248}
249
250/// A representation of a timer's Capture/Compare (CC) register.
251///
252/// A CC register holds a 32-bit value.
253/// This is used either to store a capture of the timer's current count, or to specify the value for the timer to compare against.
254///
255/// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register.
256/// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register
257pub struct Cc<'d> {
258    n: usize,
259    r: pac::timer::Timer,
260    _p: PhantomData<&'d ()>,
261}
262
263impl<'d> Cc<'d> {
264    /// Get the current value stored in the register.
265    pub fn read(&self) -> u32 {
266        self.r.cc(self.n).read()
267    }
268
269    /// Set the value stored in the register.
270    ///
271    /// `event_compare` will fire when the timer's counter reaches this value.
272    pub fn write(&self, value: u32) {
273        self.r.cc(self.n).write_value(value);
274    }
275
276    /// Capture the current value of the timer's counter in this register, and return it.
277    pub fn capture(&self) -> u32 {
278        self.r.tasks_capture(self.n).write_value(1);
279        self.read()
280    }
281
282    /// Returns this CC register's CAPTURE task, for use with PPI.
283    ///
284    /// When triggered, this task will capture the current value of the timer's counter in this register.
285    pub fn task_capture(&self) -> Task<'d> {
286        Task::from_reg(self.r.tasks_capture(self.n))
287    }
288
289    /// Returns this CC register's COMPARE event, for use with PPI.
290    ///
291    /// This event will fire when the timer's counter reaches the value in this CC register.
292    pub fn event_compare(&self) -> Event<'d> {
293        Event::from_reg(self.r.events_compare(self.n))
294    }
295
296    /// Clear the COMPARE event for this CC register.
297    #[inline]
298    pub fn clear_events(&self) {
299        self.r.events_compare(self.n).write_value(0);
300    }
301
302    /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
303    ///
304    /// This means that when the COMPARE event is fired, the CLEAR task will be triggered.
305    ///
306    /// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0.
307    pub fn short_compare_clear(&self) {
308        self.r.shorts().modify(|w| w.set_compare_clear(self.n, true))
309    }
310
311    /// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
312    pub fn unshort_compare_clear(&self) {
313        self.r.shorts().modify(|w| w.set_compare_clear(self.n, false))
314    }
315
316    /// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task.
317    ///
318    /// This means that when the COMPARE event is fired, the STOP task will be triggered.
319    ///
320    /// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up.
321    pub fn short_compare_stop(&self) {
322        self.r.shorts().modify(|w| w.set_compare_stop(self.n, true))
323    }
324
325    /// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task.
326    pub fn unshort_compare_stop(&self) {
327        self.r.shorts().modify(|w| w.set_compare_stop(self.n, false))
328    }
329}