imxrt_hal/common/timer.rs
1//! General timer APIs.
2//!
3//! `timer` provides adapters for implementing (non-)blocking
4//! timers. It builds upon hardware drivers like [`gpt`](crate::gpt)
5//! and [`pit`](crate::pit), and implements various `embedded-hal`
6//! timer traits.
7//!
8//! Use [`Blocking`] to adapt a timer for simple, blocking delays. Blocking
9//! delays use durations, not raw clock ticks; this means that you need to
10//! specify the clock's frequency.
11//!
12//! Use [`CountDown`] to adapt a timer for periodic, non-blocking time
13//! tracking. This also uses time duration, not raw clock ticks, for
14//! timing, so it again needs the clock frequency. Use [`RawCountDown`]
15//! if you want to track time in clock ticks.
16//!
17//! # Raw count representations
18//!
19//! The raw count, or ticks, supported by the driver influences the API.
20//! The table below describes the raw bit width for each adapter.
21//!
22//! | Driver | Bit width |
23//! | ----------- | --------- |
24//! | PIT channel | 32 |
25//! | PIT chain | 64 |
26//! | GPT | 32 |
27//!
28//! # Computing the clock frequency
29//!
30//! [`ccm` APIs](crate::ccm) make it easy to understand, at compile time, the intended
31//! clock frequency for a given timer. Consider using these to simply compute
32//! the clock frequency. See the [`Blocking`] and [`CountDown`] examples for
33//! demonstrations.
34//!
35//! This package does not provide an implementation of `Cancel` from embedded-hal 0.2.
36//! See [`RawCountDown`] documentation for more information.
37//!
38//! # Limitations of `CountDown`
39//!
40//! This module cannot provide the embedded-hal 0.2 `CountDown` implementation for all
41//! possible drivers. You're encouraged to adapt [`RawCountDown`] to implement a
42//! `CountDown` implementation for your specific driver.
43
44use crate::{gpt, pit};
45
46/// An interface for hardware timers.
47///
48/// This is implemented for various GPT and PIT objects.
49/// It is not convered by the semver guarantees of this
50/// crate. Do not use.
51#[doc(hidden)]
52pub trait HardwareTimer {
53 /// Representation of ticks.
54 type Ticks;
55 /// Indicates if the timer has elapsed.
56 fn is_elapsed(&self) -> bool;
57 /// Clears the elapsed flag.
58 fn clear_elapsed(&mut self);
59 /// Set the number of ticks that the timer counts.
60 fn set_ticks(&mut self, ticks: Self::Ticks);
61 /// Enable / disable the timer.
62 fn set_enable(&mut self, enable: bool);
63}
64
65impl<const N: u8> HardwareTimer for pit::Pit<N> {
66 type Ticks = u32;
67 fn is_elapsed(&self) -> bool {
68 pit::Pit::<N>::is_elapsed(self)
69 }
70 fn clear_elapsed(&mut self) {
71 pit::Pit::<N>::clear_elapsed(self);
72 }
73 fn set_ticks(&mut self, ticks: Self::Ticks) {
74 self.set_load_timer_value(ticks);
75 }
76 fn set_enable(&mut self, enable: bool) {
77 if enable {
78 self.enable();
79 } else {
80 self.disable();
81 }
82 }
83}
84
85impl<const L: u8, const R: u8> HardwareTimer for pit::Chained<L, R> {
86 type Ticks = u64;
87 fn is_elapsed(&self) -> bool {
88 pit::Chained::<L, R>::is_elapsed(self)
89 }
90 fn clear_elapsed(&mut self) {
91 pit::Chained::<L, R>::clear_elapsed(self);
92 }
93 fn set_ticks(&mut self, ticks: Self::Ticks) {
94 self.set_load_timer_value(ticks);
95 }
96 fn set_enable(&mut self, enable: bool) {
97 if enable {
98 self.enable();
99 } else {
100 self.disable();
101 }
102 }
103}
104
105/// The GPT OCR used for timers implementations.
106const GPT_OCR: gpt::OutputCompareRegister = gpt::OutputCompareRegister::OCR1;
107
108impl<const N: u8> HardwareTimer for gpt::Gpt<N> {
109 type Ticks = u32;
110 fn is_elapsed(&self) -> bool {
111 self.is_elapsed(GPT_OCR)
112 }
113 fn clear_elapsed(&mut self) {
114 gpt::Gpt::<N>::clear_elapsed(self, GPT_OCR);
115 }
116 fn set_ticks(&mut self, ticks: Self::Ticks) {
117 self.set_output_compare_count(GPT_OCR, ticks);
118 }
119 fn set_enable(&mut self, enable: bool) {
120 if enable {
121 self.enable();
122 } else {
123 self.disable();
124 }
125 }
126}
127
128/// Extensions for `fugit::TimerDuration`.
129///
130/// `fugit` does not provide a generic way to work with u32- and u64-backed durations.
131/// This trait provides that generalization.
132///
133/// Do not use directly.
134#[doc(hidden)]
135pub trait TimerDurationExt {
136 /// The duration representation.
137 type Repr;
138 /// Create yourself from raw ticks.
139 fn from_ticks(ticks: Self::Repr) -> Self;
140 /// Returns the ticks expressed by the duration.
141 fn ticks(&self) -> Self::Repr;
142 /// Convert into a different timer duration.
143 fn convert<const OTHER_HZ: u32>(&self) -> fugit::TimerDuration<Self::Repr, OTHER_HZ>;
144}
145
146impl<const HZ: u32> TimerDurationExt for fugit::TimerDurationU32<HZ> {
147 type Repr = u32;
148 fn from_ticks(ticks: Self::Repr) -> Self {
149 fugit::TimerDuration::<Self::Repr, HZ>::from_ticks(ticks)
150 }
151 fn ticks(&self) -> Self::Repr {
152 fugit::TimerDuration::<Self::Repr, HZ>::ticks(self)
153 }
154 fn convert<const OTHER_HZ: u32>(&self) -> fugit::TimerDuration<Self::Repr, OTHER_HZ> {
155 fugit::TimerDuration::<Self::Repr, HZ>::convert::<1, OTHER_HZ>(*self)
156 }
157}
158
159impl<const HZ: u32> TimerDurationExt for fugit::TimerDurationU64<HZ> {
160 type Repr = u64;
161 fn from_ticks(ticks: Self::Repr) -> Self {
162 fugit::TimerDuration::<Self::Repr, HZ>::from_ticks(ticks)
163 }
164 fn ticks(&self) -> Self::Repr {
165 fugit::TimerDuration::<Self::Repr, HZ>::ticks(self)
166 }
167 fn convert<const OTHER_HZ: u32>(&self) -> fugit::TimerDuration<Self::Repr, OTHER_HZ> {
168 fugit::TimerDuration::<Self::Repr, HZ>::convert::<1, OTHER_HZ>(*self)
169 }
170}
171
172/// A blocking timer that runs at `HZ`.
173///
174/// Blocking occupies the CPU until the timer elapses.
175/// You're responsible for specifying the clock frequency
176/// `HZ`.
177///
178/// # Example
179///
180/// Create a blocking adapter over a single PIT channel. [`ccm` API](crate::ccm)
181/// provide a simple way to specify the PIT clock frequency.
182///
183/// ```no_run
184/// use imxrt_hal as hal;
185/// use imxrt_ral as ral;
186///
187/// use hal::{
188/// ccm::{self, clock_gate, perclk_clk},
189/// timer::BlockingPit,
190/// };
191///
192/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
193///
194/// // Before touching the PERCLK clock roots, turn off all downstream clock gates.
195/// clock_gate::PERCLK_CLOCK_GATES.iter().for_each(|loc| loc.set(&mut ccm, clock_gate::OFF));
196///
197/// // Configure PERCLK to match this frequency:
198/// const PERCLK_CLK_FREQUENCY_HZ: u32 = ccm::XTAL_OSCILLATOR_HZ / PERCLK_CLK_DIVIDER;
199/// const PERCLK_CLK_DIVIDER: u32 = 24;
200/// perclk_clk::set_selection(&mut ccm, perclk_clk::Selection::Oscillator);
201/// perclk_clk::set_divider(&mut ccm, PERCLK_CLK_DIVIDER);
202///
203/// // Turn on the PIT clock gate.
204/// clock_gate::pit().set(&mut ccm, clock_gate::ON);
205///
206/// // There's no other divider, so the PIT frequency is the root
207/// // clock frequency.
208/// const PIT_FREQUENCY_HZ: u32 = PERCLK_CLK_FREQUENCY_HZ;
209///
210/// let pit = unsafe { ral::pit::PIT::instance() };
211/// let (pit0, _, _, _) = hal::pit::new(pit);
212///
213/// let mut blocking = BlockingPit::<0, PIT_FREQUENCY_HZ>::from_pit(pit0);
214/// // Block for milliseconds:
215/// blocking.block_ms(1000);
216/// // Block for microseconds:
217/// blocking.block_us(5000);
218///
219/// // Use fugit to express other durations.
220/// use fugit::ExtU32;
221/// blocking.block(1000.millis());
222/// blocking.block(1000.micros());
223///
224/// // All blocking adapters play well with embedded-hal 0.2 interfaces.
225/// use eh02::blocking::delay::{DelayMs, DelayUs};
226/// blocking.delay_ms(1000u32);
227/// blocking.delay_us(1000u32);
228/// ```
229pub struct Blocking<T, const HZ: u32> {
230 timer: T,
231}
232
233impl<T, const HZ: u32> Blocking<T, HZ>
234where
235 T: HardwareTimer,
236{
237 /// Before calling this method, make sure that
238 ///
239 /// - the timer is disabled.
240 /// - the elapsed flag is cleared.
241 /// - interrupts are disabled.
242 ///
243 /// Also ensure that your timer can operate given the expected
244 /// `block` implementation. For example, change the GPT reset
245 /// on enable configuration so that the timer always starts at
246 /// its newly-loaded value.
247 fn new(timer: T) -> Self {
248 Self { timer }
249 }
250
251 /// Release the underlying timer.
252 ///
253 /// The released timer's state is unspecified.
254 pub fn release(self) -> T {
255 self.timer
256 }
257
258 /// Occupy the CPU, blocking execution, for a `duration` represented
259 /// by the target clock.
260 ///
261 /// See the [struct-level documentation](crate::timer::Blocking) for an example.
262 /// Prefer this API if you would like to catch overflow issues at compile time,
263 /// as demonstrated below.
264 ///
265 /// ```compile_fail
266 /// // See struct-level documentation for configuration...
267 /// # let pit0 = unsafe { imxrt_hal::pit::Pit::<0>::new(&imxrt_ral::pit::PIT::instance()) };
268 /// # let mut blocking = imxrt_hal::timer::BlockingPit::<0, PIT_FREQUENCY_HZ>::from_pit(pit0);
269 /// # const PIT_FREQUENCY_HZ: u32 = 75000000;
270 /// // 99 seconds, expressed in microseconds, cannot fit within a u32 counter
271 /// // that counts at PIT_FREQUENCY_HZ. This fails to compile:
272 /// const DELAY: fugit::TimerDurationU32<PIT_FREQUENCY_HZ>
273 /// = fugit::MicrosDurationU32::from_ticks(99_000_000).convert();
274 /// blocking.block(DELAY);
275 /// ```
276 ///
277 /// ```no_run
278 /// # let pit0 = unsafe { imxrt_hal::pit::Pit::<0>::new(&imxrt_ral::pit::PIT::instance()) };
279 /// # let mut blocking = imxrt_hal::timer::BlockingPit::<0, PIT_FREQUENCY_HZ>::from_pit(pit0);
280 /// # const PIT_FREQUENCY_HZ: u32 = 75000000;
281 /// // However, 99 milliseconds, expressed in microseconds, can fit within a u32
282 /// // counter that counts at PIT_FREQENCY_HZ.
283 /// const DELAY: fugit::TimerDurationU32<PIT_FREQUENCY_HZ>
284 /// = fugit::MicrosDurationU32::from_ticks(99_000).convert();
285 /// blocking.block(DELAY);
286 /// ```
287 pub fn block(&mut self, duration: fugit::TimerDuration<T::Ticks, HZ>)
288 where
289 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
290 {
291 self.timer.set_ticks(duration.ticks());
292 self.timer.set_enable(true);
293
294 while !self.timer.is_elapsed() {}
295
296 self.timer.clear_elapsed();
297 self.timer.set_enable(false);
298 }
299
300 /// Occupy the CPU, blocking execution, for `ms` milliseconds.
301 ///
302 /// Note that the type of `ms` depends on the tick representation
303 /// of your underlying timer. See the [module documentation](crate::timer)
304 /// for specifics.
305 ///
306 /// # Panics
307 ///
308 /// Panics if the tick representation in `ms` on the target clock
309 /// would overflow.
310 ///
311 /// # Example
312 ///
313 /// See the top-level example for a demonstration. Despite the complex
314 /// type signature, note that the duration is simply a u32 or u64.
315 pub fn block_ms(&mut self, ms: T::Ticks)
316 where
317 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
318 fugit::TimerDuration<T::Ticks, 1_000>: TimerDurationExt<Repr = T::Ticks>,
319 {
320 self.block(fugit::TimerDuration::<T::Ticks, 1_000>::from_ticks(ms).convert())
321 }
322
323 /// Occupy the CPU, blocking execution, for `us` milliseconds.
324 ///
325 /// Note that the type of `us` depends on the tick representation
326 /// of your underlying timer. See the [module documentation](crate::timer)
327 /// for specifics.
328 ///
329 /// # Panics
330 ///
331 /// Panics if the tick representation in `us` on the target clock
332 /// would overflow.
333 ///
334 /// # Example
335 ///
336 /// See the top-level example for a demonstration. Despite the complex
337 /// type signature, note that the duration is simply a u32 or u64.
338 pub fn block_us(&mut self, us: T::Ticks)
339 where
340 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
341 fugit::TimerDuration<T::Ticks, 1_000_000>: TimerDurationExt<Repr = T::Ticks>,
342 {
343 self.block(fugit::TimerDuration::<T::Ticks, 1_000_000>::from_ticks(us).convert())
344 }
345}
346
347/// Prepares a PIT channel to be adapted by blocking / count down
348/// adapters.
349fn prepare_pit<const N: u8>(pit: &mut pit::Pit<N>) {
350 pit.disable();
351 pit.clear_elapsed();
352 pit.set_chained(false);
353 pit.set_interrupt_enable(false);
354}
355
356/// Prepares a PIT chain to be adapted by blocking / count down
357/// adapters.
358fn prepare_pit_chained<const L: u8, const R: u8>(chain: &mut pit::Chained<L, R>) {
359 chain.disable();
360 chain.clear_elapsed();
361 chain.set_interrupt_enable(false);
362}
363
364/// Prepares a GPT to be adapted by blocking / count down adapters.
365fn prepare_gpt<const N: u8>(gpt: &mut gpt::Gpt<N>) {
366 gpt.disable();
367 gpt.clear_rollover();
368 gpt.set_rollover_interrupt_enable(false);
369
370 // We're using OCR1 so we can achieve the periodic
371 // implementation by the hardware behavior.
372 gpt.set_mode(gpt::Mode::Restart);
373 // Start counting from zero when the timer is enabled.
374 gpt.set_reset_on_enable(true);
375
376 use gpt::OutputCompareRegister::*;
377 for ocr in [OCR1, OCR2, OCR3] {
378 gpt::Gpt::<N>::clear_elapsed(gpt, ocr);
379 gpt.set_output_interrupt_on_compare(ocr, false);
380 }
381}
382
383/// A single PIT channel that acts as a blocking timer.
384pub type BlockingPit<const N: u8, const HZ: u32> = Blocking<pit::Pit<N>, HZ>;
385
386/// A single PIT channel that acts as a blocking timer.
387///
388/// Prefer [`BlockingPit`], which is easier to type. It is also more
389/// distinct than [`BlockingPitChain`], which varies from `BlockingPitChan`
390/// by only one letter.
391#[deprecated(since = "0.5.1", note = "Use BlockingPit")]
392pub type BlockingPitChan<const N: u8, const HZ: u32> = BlockingPit<N, HZ>;
393
394impl<const N: u8, const HZ: u32> BlockingPit<N, HZ> {
395 /// Create a blocking adapter from a PIT channel.
396 pub fn from_pit(mut pit: pit::Pit<N>) -> Self {
397 prepare_pit(&mut pit);
398 Self::new(pit)
399 }
400
401 /// Create a blocking adapter from a PIT channel.
402 ///
403 /// Prefer [`from_pit`](Self::from_pit), which is easier to type
404 /// and matches the name of the type we're converting.
405 #[deprecated(since = "0.5.1", note = "Use from_pit")]
406 pub fn from_pit_channel(pit: pit::Pit<N>) -> Self {
407 Self::from_pit(pit)
408 }
409}
410
411/// A chain of PIT channels that act as a blocking timer.
412pub type BlockingPitChain<const L: u8, const R: u8, const HZ: u32> =
413 Blocking<pit::Chained<L, R>, HZ>;
414
415impl<const L: u8, const R: u8, const HZ: u32> BlockingPitChain<L, R, HZ> {
416 /// Create a blocking adapter from chained PIT channels.
417 pub fn from_pit_chained(mut chain: pit::Chained<L, R>) -> Self {
418 prepare_pit_chained(&mut chain);
419 Self::new(chain)
420 }
421}
422
423/// A GPT that acts as a blocking timer.
424pub type BlockingGpt<const N: u8, const HZ: u32> = Blocking<gpt::Gpt<N>, HZ>;
425
426impl<const N: u8, const HZ: u32> BlockingGpt<N, HZ> {
427 /// Create a blocking adapter from a GPT.
428 pub fn from_gpt(mut gpt: gpt::Gpt<N>) -> Self {
429 prepare_gpt(&mut gpt);
430 Self::new(gpt)
431 }
432}
433
434impl<R, T, const HZ: u32> eh02::blocking::delay::DelayMs<R> for Blocking<T, HZ>
435where
436 R: Into<T::Ticks>,
437 T: HardwareTimer,
438 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
439 fugit::TimerDuration<T::Ticks, 1_000>: TimerDurationExt<Repr = T::Ticks>,
440{
441 fn delay_ms(&mut self, ms: R) {
442 self.block_ms(ms.into());
443 }
444}
445
446impl<R, T, const HZ: u32> eh02::blocking::delay::DelayUs<R> for Blocking<T, HZ>
447where
448 R: Into<T::Ticks>,
449 T: HardwareTimer,
450 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
451 fugit::TimerDuration<T::Ticks, 1_000_000>: TimerDurationExt<Repr = T::Ticks>,
452{
453 fn delay_us(&mut self, us: R) {
454 self.block_us(us.into());
455 }
456}
457
458/// A count down timer that uses ticks for the timeout.
459///
460/// This adapter does not require you to know about the
461/// clock frequency. However, not knowing this means that
462/// you're responsible for choosing meaningful count ticks.
463///
464/// The adapter implements `Periodic`. However, it does not
465/// implement `Cancel`, since it cannot decide the error type
466/// for all possible users. You're encouraged to build your
467/// own adapter atop this type if you need to provide a `Cancel`
468/// implementation.
469///
470/// See [`CountDown`] for an example of using this type with
471/// a GPT timer.
472pub struct RawCountDown<T> {
473 timer: T,
474}
475
476impl<T> RawCountDown<T>
477where
478 T: HardwareTimer,
479{
480 /// Before calling this method, make sure that the timer
481 ///
482 /// - is disabled.
483 /// - has interrupts disabled.
484 /// - is configured for periodic execution.
485 fn new(timer: T) -> Self {
486 Self { timer }
487 }
488
489 /// Release the adapter to acquire the raw count down timer.
490 ///
491 /// The released timer's state is unspecified.
492 pub fn release(self) -> T {
493 self.timer
494 }
495
496 /// Start the count down timer to periodically elapse every
497 /// number of `ticks` clock counts.
498 ///
499 /// If this is invoked when a timer is already counting,
500 /// this resets the timer to run at `ticks`.
501 ///
502 /// The type of `ticks` depends on the underlying timer.
503 /// See the [module documentation](crate::timer) for specifics.
504 pub fn start(&mut self, ticks: T::Ticks) {
505 self.timer.set_enable(false);
506 self.timer.clear_elapsed();
507 self.timer.set_ticks(ticks);
508 self.timer.set_enable(true);
509 }
510
511 /// Cancel a running timer.
512 ///
513 /// Does nothing if the timer is already canceled / disabled.
514 pub fn cancel(&mut self) {
515 self.timer.set_enable(false);
516 }
517
518 /// Indicates if the timer has elapsed.
519 pub fn is_elapsed(&self) -> bool {
520 self.timer.is_elapsed()
521 }
522
523 /// Clears the elapsed condition.
524 pub fn clear_elapsed(&mut self) {
525 self.timer.clear_elapsed()
526 }
527}
528
529/// A count down timer over a PIT channel.
530pub type RawCountDownPit<const N: u8> = RawCountDown<pit::Pit<N>>;
531
532/// A count down timer over a PIT channel.
533///
534/// Prefer [`RawCountDownPit`], which is easier to type. It is also more
535/// distinct than [`RawCountDownPitChain`], which varies from `RawCountDownPitChan`
536/// by only one letter.
537#[deprecated(since = "0.5.1", note = "Use RawCountDownPit")]
538pub type RawCountDownPitChan<const N: u8> = RawCountDownPit<N>;
539
540impl<const N: u8> RawCountDownPit<N> {
541 /// Create a count down timer from a PIT channel.
542 pub fn from_pit(mut pit: pit::Pit<N>) -> Self {
543 prepare_pit(&mut pit);
544 Self::new(pit)
545 }
546
547 /// Create a count down timer from a PIT channel.
548 ///
549 /// Prefer [`from_pit`](Self::from_pit), which is easier to type
550 /// and matches the name of the type we're converting.
551 #[deprecated(since = "0.5.1", note = "Use from_pit")]
552 pub fn from_pit_channel(pit: pit::Pit<N>) -> Self {
553 Self::from_pit(pit)
554 }
555}
556
557/// A count down timer over two chained PIT channels.
558pub type RawCountDownPitChain<const L: u8, const R: u8> = RawCountDown<pit::Chained<L, R>>;
559
560impl<const L: u8, const R: u8> RawCountDownPitChain<L, R> {
561 /// Create a count down timer from a PIT chain.
562 pub fn from_pit_chained(mut chain: pit::Chained<L, R>) -> Self {
563 prepare_pit_chained(&mut chain);
564 Self::new(chain)
565 }
566}
567
568/// A count down timer over a GPT.
569pub type RawCountDownGpt<const N: u8> = RawCountDown<gpt::Gpt<N>>;
570
571impl<const N: u8> RawCountDownGpt<N> {
572 /// Create a count down timer from a GPT.
573 pub fn from_gpt(mut gpt: gpt::Gpt<N>) -> Self {
574 prepare_gpt(&mut gpt);
575 Self::new(gpt)
576 }
577}
578
579impl<T> eh02::timer::CountDown for RawCountDown<T>
580where
581 T: HardwareTimer,
582{
583 type Time = T::Ticks;
584 fn start<C>(&mut self, count: C)
585 where
586 C: Into<Self::Time>,
587 {
588 RawCountDown::<T>::start(self, count.into());
589 }
590 fn wait(&mut self) -> nb::Result<(), void::Void> {
591 if self.is_elapsed() {
592 self.clear_elapsed();
593 Ok(())
594 } else {
595 Err(nb::Error::WouldBlock)
596 }
597 }
598}
599
600impl<T> eh02::timer::Periodic for RawCountDown<T> {}
601
602/// A count down timer adapter that uses `fugit` durations.
603///
604/// You're responsible for specifying the `HZ` that represents
605/// your underlying clock's frequency. However, it conveniently
606/// lets you express timeouts in units of time, not clock ticks.
607///
608/// To use this type, create a [`RawCountDown`], then simply wrap
609/// that object with this adapter. You may also adapt this object
610/// to satisfy your driver's need.
611///
612/// The `CountDown` embedded-hal implementation provided by this
613/// crate may not work for your specific use case. If that's the
614/// case, you may adapt the [`RawCountDown`] object to satisfy
615/// your needs.
616///
617/// # Example
618///
619/// Use the GPT as a countdown timer. [`ccm` APIs](crate::ccm) make
620/// it easy to configure the root clock. Additional constants ensure
621/// that the run-time and compile-time frequencies match.
622///
623/// ```no_run
624/// use imxrt_hal as hal;
625/// use imxrt_ral as ral;
626///
627/// use hal::ccm::{self, clock_gate, perclk_clk};
628///
629/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
630///
631/// // Before touching the PERCLK clock roots, turn off all downstream clock gates.
632/// clock_gate::PERCLK_CLOCK_GATES.iter().for_each(|loc| loc.set(&mut ccm, clock_gate::OFF));
633///
634/// // Configure PERCLK to match this frequency:
635/// const PERCLK_CLK_FREQUENCY_HZ: u32 = ccm::XTAL_OSCILLATOR_HZ / PERCLK_CLK_DIVIDER;
636/// const PERCLK_CLK_DIVIDER: u32 = 24;
637/// perclk_clk::set_selection(&mut ccm, perclk_clk::Selection::Oscillator);
638/// perclk_clk::set_divider(&mut ccm, PERCLK_CLK_DIVIDER);
639///
640/// // Enable the clock gate for our GPT.
641/// clock_gate::gpt_bus::<1>().set(&mut ccm, clock_gate::ON);
642/// clock_gate::gpt_serial::<1>().set(&mut ccm, clock_gate::ON);
643///
644/// // GPT1 counts with this frequency:
645/// const GPT1_FREQUENCY_HZ: u32 = PERCLK_CLK_FREQUENCY_HZ / GPT1_DIVIDER;
646/// const GPT1_DIVIDER: u32 = 100;
647/// const GPT1_CLOCK_SOURCE: hal::gpt::ClockSource = hal::gpt::ClockSource::HighFrequencyReferenceClock;
648///
649/// let gpt1 = unsafe { ral::gpt::GPT1::instance() };
650/// let mut gpt1 = hal::gpt::Gpt::new(gpt1);
651/// gpt1.set_divider(GPT1_DIVIDER);
652/// gpt1.set_clock_source(GPT1_CLOCK_SOURCE);
653///
654/// let mut count_down = hal::timer::CountDown::<_, GPT1_FREQUENCY_HZ>::new(
655/// hal::timer::RawCountDown::from_gpt(gpt1)
656/// );
657///
658/// use fugit::ExtU32;
659/// use eh02::timer::CountDown;
660/// CountDown::start(&mut count_down, 100.millis());
661/// ```
662pub struct CountDown<T, const HZ: u32> {
663 timer: RawCountDown<T>,
664}
665
666impl<T, const HZ: u32> CountDown<T, HZ>
667where
668 T: HardwareTimer,
669{
670 /// Create a new count down timer that works with timer units.
671 pub fn new(raw: RawCountDown<T>) -> Self {
672 Self { timer: raw }
673 }
674
675 /// Release the adapter to acquire the raw count down timer.
676 pub fn release(self) -> RawCountDown<T> {
677 self.timer
678 }
679
680 /// Start the timer to periodically elapse every `duration`.
681 ///
682 /// If this is invoked when a timer is already counting,
683 /// this resets the timer to run at `ticks`.
684 pub fn start(&mut self, duration: fugit::TimerDuration<T::Ticks, HZ>)
685 where
686 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
687 {
688 self.timer.start(duration.ticks());
689 }
690
691 /// Cancel a running timer.
692 ///
693 /// Does nothing if the timer is already canceled / disabled.
694 pub fn cancel(&mut self) {
695 self.timer.cancel();
696 }
697
698 /// Indicates if the timer has elapsed.
699 pub fn is_elapsed(&self) -> bool {
700 self.timer.is_elapsed()
701 }
702
703 /// Clears the elapsed condition.
704 pub fn clear_elapsed(&mut self) {
705 self.timer.clear_elapsed()
706 }
707}
708
709impl<T, const HZ: u32> eh02::timer::CountDown for CountDown<T, HZ>
710where
711 T: HardwareTimer,
712 fugit::TimerDuration<T::Ticks, HZ>: TimerDurationExt<Repr = T::Ticks>,
713{
714 type Time = fugit::TimerDuration<T::Ticks, HZ>;
715 fn start<C>(&mut self, count: C)
716 where
717 C: Into<Self::Time>,
718 {
719 let duration = count.into();
720 self.start(duration);
721 }
722
723 fn wait(&mut self) -> nb::Result<(), void::Void> {
724 self.timer.wait()
725 }
726}
727
728impl<T, const HZ: u32> eh02::timer::Periodic for CountDown<T, HZ> {}