imxrt_hal/common/pit.rs
1//! Periodic interrupt timers.
2//!
3//! PITs are countdown timers that run on the PERCLK clock root.
4//! When the timer elapses, it automatically restarts from the load value.
5//! There are four PIT channels for independent time tracking. The four
6//! channels share an interrupt.
7//!
8//! You can chain channels together by using [`Chained`](Chained).
9//! This doubles the width of the timer.
10//!
11//! # Example
12//!
13//! Note that these examples do not demonstrate how to configure the PIT
14//! clock gates, or PERCLK. For more information, see [the CCM peripheral clock
15//! module](crate::ccm::perclk_clk).
16//!
17//! Acquire the four PIT timer channels:
18//!
19//! ```no_run
20//! use imxrt_hal::pit;
21//! use imxrt_ral::pit::PIT;
22//!
23//! let (mut pit0, mut pit1, _, _) = pit::new(unsafe { PIT::instance() });
24//! ```
25//!
26//! Use `pit0` to implement a blocking delay:
27//!
28//! ```no_run
29//! # use imxrt_hal::pit;
30//! # use imxrt_ral::pit::PIT;
31//! # let (mut pit0, pit1, _, _) = pit::new(unsafe { PIT::instance() });
32//! # const DELAY_MS: u32 = 1;
33//! pit0.set_load_timer_value(DELAY_MS);
34//! pit0.enable();
35//!
36//! loop {
37//! while !pit0.is_elapsed() {}
38//! pit0.clear_elapsed();
39//! // Do work...
40//! }
41//! ```
42//!
43//! Chain the channels together to form a larger timer:
44//!
45//! ```no_run
46//! # use imxrt_hal::pit;
47//! # use imxrt_ral::pit::PIT;
48//! # let (pit0, pit1, _, _) = pit::new(unsafe { PIT::instance() });
49//!
50//! let chained = pit::Chained01::new(pit0, pit1);
51//! ```
52
53/// Channel 0.
54pub type Pit0 = Pit<0>;
55/// Channel 1.
56pub type Pit1 = Pit<1>;
57/// Channel 2.
58pub type Pit2 = Pit<2>;
59/// Channel 3.
60pub type Pit3 = Pit<3>;
61
62/// All four channels.
63pub type Channels = (Pit0, Pit1, Pit2, Pit3);
64
65/// A periodic interrupt timer (PIT) channel.
66pub struct Pit<const CHAN: u8> {
67 instance: &'static crate::ral::pit::RegisterBlock,
68}
69
70/// Convert the PIT peripheral instances into four timer channels.
71///
72/// `new` will reset all timer control registers before returning
73/// the channels. It is is guaranteed to not touch the `FRZ` bit
74/// in `MCR`.
75pub fn new<const N: u8>(pit: crate::ral::pit::Instance<N>) -> Channels {
76 crate::ral::modify_reg!(crate::ral::pit, pit, MCR, MDIS: MDIS_0);
77 // Reset all PIT channels
78 //
79 // PIT channels may be used by a systems boot ROM, or another
80 // user. Set them to a known, good state.
81 crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[0], TCTRL, 0);
82 crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[1], TCTRL, 0);
83 crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[2], TCTRL, 0);
84 crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[3], TCTRL, 0);
85
86 // Safety: we own the larger PIT peripheral instance. The caller
87 // (or the user who fabricated the larger PIT instance) ensures
88 // that we're not aliasing that peripheral.
89 unsafe {
90 (
91 Pit::new(&pit),
92 Pit::new(&pit),
93 Pit::new(&pit),
94 Pit::new(&pit),
95 )
96 }
97}
98
99mod private {
100 #[allow(dead_code)]
101 pub trait Sealed {}
102}
103/// Describes a valid PIT channel.
104pub trait Valid {}
105/// Type-level constant for PIT channels.
106///
107/// When combined with [`Valid`], this can constrain an API to accept
108/// only valid PIT channel constants. See [`Pit::new()`](crate::pit::Pit::new)
109/// for an example.
110pub enum Const<const N: u8> {}
111impl private::Sealed for Const<0> {}
112impl private::Sealed for Const<1> {}
113impl private::Sealed for Const<2> {}
114impl private::Sealed for Const<3> {}
115impl Valid for Const<0> {}
116impl Valid for Const<1> {}
117impl Valid for Const<2> {}
118impl Valid for Const<3> {}
119
120impl<const CHAN: u8> Pit<CHAN> {
121 /// Fabricate a PIT channel instance.
122 ///
123 /// # Safety
124 ///
125 /// This allows you to produce multiple PIT channels that mutably
126 /// reference the same memory. You must ensure that writes to the
127 /// peripheral memory are synchronized across instances.
128 ///
129 /// Use the free function [`new()`](crate::pit::new) to safely
130 /// acquire the four PIT channels.
131 pub unsafe fn new<const N: u8>(instance: &crate::ral::pit::Instance<N>) -> Self
132 where
133 Const<CHAN>: Valid,
134 {
135 let register_block: &'_ crate::ral::pit::RegisterBlock = instance;
136 // Safety: extending lifetime when we know that the register block has
137 // static lifetime.
138 let register_block: &'static _ = unsafe { core::mem::transmute(register_block) };
139 Self {
140 instance: register_block,
141 }
142 }
143
144 /// Enable (true) or disable (false) interrupt generation.
145 pub fn set_interrupt_enable(&mut self, enable: bool) {
146 crate::ral::modify_reg!(
147 crate::ral::pit::timer,
148 &self.instance.TIMER[CHAN as usize],
149 TCTRL,
150 TIE: enable as u32
151 )
152 }
153
154 /// Indicates if timeouts will (true) or will not (false) generate interrupts.
155 pub fn is_interrupt_enabled(&self) -> bool {
156 crate::ral::read_reg!(
157 crate::ral::pit::timer,
158 &self.instance.TIMER[CHAN as usize],
159 TCTRL,
160 TIE == 1
161 )
162 }
163
164 /// Reads the current time value, in clock ticks.
165 ///
166 /// Returns `0` if the timer is disabled.
167 pub fn current_timer_value(&self) -> u32 {
168 if self.is_enabled() {
169 // Note in CVAL register docs: don't read CVAL if the timer
170 // is disable "because the value is unreliable."
171 crate::ral::read_reg!(
172 crate::ral::pit::timer,
173 &self.instance.TIMER[CHAN as usize],
174 CVAL
175 )
176 } else {
177 0
178 }
179 }
180
181 /// Loads the timer value for the next timer run.
182 ///
183 /// `ticks` is in clock ticks.
184 pub fn set_load_timer_value(&self, ticks: u32) {
185 crate::ral::write_reg!(
186 crate::ral::pit::timer,
187 &self.instance.TIMER[CHAN as usize],
188 LDVAL,
189 ticks.saturating_sub(1)
190 );
191 }
192
193 /// Returns the load timer value for the next timer run, in clock ticks.
194 pub fn load_timer_value(&self) -> u32 {
195 crate::ral::read_reg!(
196 crate::ral::pit::timer,
197 &self.instance.TIMER[CHAN as usize],
198 LDVAL
199 )
200 .saturating_add(1)
201 }
202
203 /// Enable the timer.
204 pub fn enable(&mut self) {
205 crate::ral::modify_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TCTRL, TEN: 1);
206 }
207
208 /// Disable the timer.
209 pub fn disable(&mut self) {
210 crate::ral::modify_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TCTRL, TEN: 0);
211 }
212
213 /// Returns `true` if the PIT channel is enabled.
214 pub fn is_enabled(&self) -> bool {
215 crate::ral::read_reg!(
216 crate::ral::pit::timer,
217 &self.instance.TIMER[CHAN as usize],
218 TCTRL,
219 TEN == 1
220 )
221 }
222
223 /// Returns `true` if the timer has elapsed.
224 pub fn is_elapsed(&self) -> bool {
225 crate::ral::read_reg!(
226 crate::ral::pit::timer,
227 &self.instance.TIMER[CHAN as usize],
228 TFLG,
229 TIF == 1
230 )
231 }
232
233 /// Clear the elapsed flag.
234 pub fn clear_elapsed(&self) {
235 crate::ral::write_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TFLG, TIF: 1)
236 }
237
238 /// Specify that this channel is chained to the previous channel.
239 ///
240 /// This affects how the timer counts. If you're looking to chain timers
241 /// easily, see [`Chained`](crate::pit::Chained).
242 pub fn set_chained(&mut self, chained: bool) {
243 crate::ral::modify_reg!(
244 crate::ral::pit::timer,
245 &self.instance.TIMER[CHAN as usize],
246 TCTRL,
247 CHN: chained as u32
248 );
249 }
250
251 /// Returns true if this channel is chained to the previous channel.
252 pub fn is_chained(&self) -> bool {
253 crate::ral::read_reg!(
254 crate::ral::pit::timer,
255 &self.instance.TIMER[CHAN as usize],
256 TCTRL,
257 CHN == 1
258 )
259 }
260}
261
262// Safety: reference to static MMIO can be moved across execution contexts.
263// Study of this module reveals that the safe API splits the larger PIT
264// peripheral instance into separate, non-aliasing channels.
265unsafe impl<const CHAN: u8> Send for Pit<CHAN> {}
266
267/// Two chained PIT timer channels.
268///
269/// When the low timer counts down to zero, the high timer is decremented
270/// by one. This doubles the width of the timer.
271///
272/// Timers must be chained in sequence. For example, timer 2 (high) can be
273/// chained to timer 1 (low). But, timer 3 cannot be chained to timer 1.
274///
275/// Chaining channel 1 and 0 enables the lifetime register. The lifetime
276/// register allows us to read two 32-bit registers without rollover.
277/// Otherwise, the implementation handles rollovers in software with a small
278/// loop and comparison.
279pub struct Chained<const L: u8, const H: u8> {
280 low: Pit<L>,
281 high: Pit<H>,
282}
283
284/// Chained channels 0 and 1.
285pub type Chained01 = Chained<0, 1>;
286/// Chained channels 1 and 2.
287pub type Chained12 = Chained<1, 2>;
288/// Chained channels 2 and 3.
289pub type Chained23 = Chained<2, 3>;
290
291impl Chained<0, 1> {
292 /// Chain together channels 0 and 1.
293 ///
294 /// This creates the lifetime timer.
295 pub fn new(low: Pit<0>, high: Pit<1>) -> Self {
296 chain(low, high)
297 }
298
299 /// Read the lifetime register value.
300 ///
301 /// This is only supported when chaining channels 0 and
302 /// channel 1. Returns `0` if the timer is disabled. The
303 /// lifetime registers account for rollover possibility
304 /// in hardware.
305 ///
306 /// This method implements the recommended fix for
307 /// errata ERR050130.
308 pub fn lifetime_value(&self) -> u64 {
309 if !self.is_enabled() {
310 return 0;
311 }
312
313 // Safety: there can only be one (safe) instance of this chained timer.
314 // We effectively own these registers. These addresses are valid MMIO
315 // registers.
316 let mut high = self.low.instance.LTMR64H.read();
317 let mut low = self.low.instance.LTMR64L.read();
318
319 let ldval0 = self.low.load_timer_value();
320 if low == ldval0 {
321 high = self.low.instance.LTMR64H.read();
322 low = self.low.instance.LTMR64L.read();
323 }
324
325 (u64::from(high) << 32) + u64::from(low)
326 }
327}
328
329impl Chained<1, 2> {
330 /// Chain together channels 1 and 2.
331 pub fn new(low: Pit<1>, high: Pit<2>) -> Self {
332 chain(low, high)
333 }
334}
335
336impl Chained<2, 3> {
337 /// Chain together channels 2 and 3.
338 pub fn new(low: Pit<2>, high: Pit<3>) -> Self {
339 chain(low, high)
340 }
341}
342
343/// Chain the low and high timers together.
344///
345/// This has no type safety to ensure valid channel chaining.
346fn chain<const L: u8, const H: u8>(mut low: Pit<L>, mut high: Pit<H>) -> Chained<L, H> {
347 low.disable();
348 high.disable();
349
350 low.clear_elapsed();
351 high.clear_elapsed();
352
353 low.set_chained(false);
354 low.set_interrupt_enable(false);
355 high.set_chained(true);
356 Chained { low, high }
357}
358
359impl<const L: u8, const H: u8> Chained<L, H> {
360 /// Release the chained timers.
361 ///
362 /// When `release` returns, the timer chain is disabled, and the
363 /// timer channels are disabled.
364 pub fn release(mut self) -> (Pit<L>, Pit<H>) {
365 self.low.disable();
366 self.high.disable();
367
368 self.high.set_chained(false);
369 self.high.set_interrupt_enable(false);
370 (self.low, self.high)
371 }
372
373 /// Enable (true) or disable (false) interrupt generation when
374 /// the chained timer expires.
375 pub fn set_interrupt_enable(&mut self, enable: bool) {
376 self.high.set_interrupt_enable(enable);
377 }
378
379 /// Indicates if timeouts will (true) or will not (false) generate interrupts.
380 pub fn is_interrupt_enabled(&self) -> bool {
381 self.high.is_interrupt_enabled()
382 }
383
384 /// Loads the timer value for the next timer run.
385 ///
386 /// `ticks` is in clock ticks.
387 pub fn set_load_timer_value(&mut self, ticks: u64) {
388 self.low.set_load_timer_value((ticks & 0xFFFF_FFFF) as u32);
389 self.high.set_load_timer_value((ticks >> 32) as u32);
390 }
391
392 /// Returns the load timer value for the next timer run, in ticks.
393 pub fn load_timer_value(&self) -> u64 {
394 let low = self.low.load_timer_value();
395 let high = self.high.load_timer_value();
396 (u64::from(high) << 32) + u64::from(low)
397 }
398
399 /// Reads the current timer value, in clock ticks.
400 ///
401 /// Returns `0` if the timer is disabled.
402 pub fn current_timer_value(&self) -> u64 {
403 if !self.is_enabled() {
404 return 0;
405 }
406
407 loop {
408 let tmp = self.high.current_timer_value();
409 let low = self.low.current_timer_value();
410 let high = self.high.current_timer_value();
411 if high == tmp {
412 // No rollover.
413 return (u64::from(high) << 32) + u64::from(low);
414 }
415 }
416 }
417
418 /// Enable the chained timer.
419 pub fn enable(&mut self) {
420 self.high.enable();
421 self.low.enable();
422 }
423
424 /// Disable the chained timer.
425 pub fn disable(&mut self) {
426 self.low.disable();
427 self.high.disable();
428 }
429
430 /// Returns `true` if the chained timer is enabled.
431 pub fn is_enabled(&self) -> bool {
432 self.high.is_enabled()
433 }
434
435 /// Returns `true` if the chained timer has elapsed.
436 pub fn is_elapsed(&self) -> bool {
437 self.high.is_elapsed()
438 }
439
440 /// Clear the elapsed flag.
441 pub fn clear_elapsed(&mut self) {
442 self.high.clear_elapsed();
443 self.low.clear_elapsed(); // Is this necessary?
444 }
445}
446
447/// ```compile_fail
448/// use imxrt_ral as ral;
449/// use imxrt_hal as hal;
450/// use hal::pit::Pit;
451///
452/// let p: Pit<4> = unsafe { Pit::new(&ral::pit::PIT::instance()) };
453/// ```
454#[cfg(doctest)]
455struct InvalidChannel;