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}