Skip to main content

imxrt_hal/chip/drivers/
pit.rs

1//! Periodic interrupt timers.
2//!
3//! Each PIT has four channels, all running at the same frequency.
4//! PIT channels count down to zero from a starting load value.
5//! When a channel elapses, it automatically restarts from the load value.
6//! All four channels share an interrupt.
7//!
8//! You can chain channels together using [`Pit::enable_chaining`].
9//! This increases the width of the timer by having a channel count down
10//! each time the previous channel expires. You can chain more than two
11//! channels together. Keep in mind that you'll need to handle cases of
12//! timer overflow in software.
13//!
14//! When channel 0 and 1 are chained together, the lifetime register is
15//! enabled. Reads from the lifetime registers will automatically handle
16//! overflow without a software loop.
17//!
18//! # Example
19//!
20//! Note that these examples do not demonstrate how to configure the PIT
21//! clock gates, or PERCLK. For more information, see [the CCM peripheral clock
22//! module](crate::ccm::perclk_clk).
23//!
24//! Acquire the PIT driver:
25//!
26//! ```no_run
27//! use imxrt_hal::pit::{Pit, Channel};
28//! use imxrt_ral::pit::PIT;
29//!
30//! let mut pit = Pit::new(unsafe { PIT::instance() });
31//! ```
32//!
33//! Use channel 0 to implement a blocking delay:
34//!
35//! ```no_run
36//! # use imxrt_hal::pit::{Pit, Channel};
37//! # use imxrt_ral::pit::PIT;
38//! # let mut pit = Pit::new(unsafe { PIT::instance() });
39//! # const DELAY_MS: u32 = 1;
40//! pit.set_load_timer_value(Channel::Chan0, DELAY_MS);
41//! pit.enable(Channel::Chan0);
42//!
43//! loop {
44//!     while !pit.is_elapsed(Channel::Chan0) {}
45//!     pit.clear_elapsed(Channel::Chan0);
46//!     // Do work...
47//! }
48//! ```
49//!
50//! Chain channels 0 and 1 together for a 64-bit timer:
51//!
52//! ```no_run
53//! # use imxrt_hal::pit::{Pit, Channel};
54//! # use imxrt_ral::pit::PIT;
55//! # let mut pit = Pit::new(unsafe { PIT::instance() });
56//! // Channel 1 will decrement when Channel 0 expires.
57//! pit.enable_chaining(Channel::Chan1).unwrap();
58//! ```
59
60use crate::ral;
61
62/// Any PIT instance.
63type AnyPitInstance = crate::AnyInstance<ral::pit::RegisterBlock>;
64
65/// A PIT channel.
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68#[repr(usize)]
69pub enum Channel {
70    /// Channel 0.
71    Chan0 = 0,
72    /// Channel 1.
73    Chan1 = 1,
74    /// Channel 2.
75    Chan2 = 2,
76    /// Channel 3.
77    Chan3 = 3,
78}
79
80/// Error returned when a channel cannot be chained.
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub struct CannotChainError(());
84
85/// A periodic interrupt timer (PIT) driver.
86///
87/// The PIT has four independent timer channels that share a single
88/// interrupt. Use the [`Channel`] enum to select which channel to operate on.
89pub struct Pit {
90    pit: AnyPitInstance,
91}
92
93impl Pit {
94    /// Create a new PIT driver from the RAL's PIT instance.
95    ///
96    /// When `new` returns, all channels are disabled and reset.
97    /// The `FRZ` bit in `MCR` is not modified.
98    pub fn new<const N: u8>(pit: ral::pit::Instance<N>) -> Self {
99        new(crate::into_any(pit))
100    }
101
102    fn timer(&self, channel: Channel) -> &ral::pit::timer::RegisterBlock {
103        &self.pit.TIMER[channel as usize]
104    }
105
106    /// Enable (true) or disable (false) interrupt generation for a channel.
107    pub fn set_interrupt_enable(&mut self, channel: Channel, enable: bool) {
108        ral::modify_reg!(ral::pit::timer, self.timer(channel), TCTRL, TIE: enable as u32);
109    }
110
111    /// Indicates if timeouts will (true) or will not (false) generate interrupts.
112    pub fn is_interrupt_enabled(&self, channel: Channel) -> bool {
113        ral::read_reg!(ral::pit::timer, self.timer(channel), TCTRL, TIE == 1)
114    }
115
116    /// Reads the current timer value, in clock ticks.
117    ///
118    /// Returns `0` if the channel is disabled.
119    pub fn current_timer_value(&self, channel: Channel) -> u32 {
120        if self.is_enabled(channel) {
121            // Note in CVAL register docs: don't read CVAL if the timer
122            // is disabled "because the value is unreliable."
123            ral::read_reg!(ral::pit::timer, self.timer(channel), CVAL)
124        } else {
125            0
126        }
127    }
128
129    /// Loads the timer value for the next timer run.
130    ///
131    /// `ticks` is in clock ticks.
132    pub fn set_load_timer_value(&self, channel: Channel, ticks: u32) {
133        ral::write_reg!(
134            ral::pit::timer,
135            self.timer(channel),
136            LDVAL,
137            ticks.saturating_sub(1)
138        );
139    }
140
141    /// Returns the load timer value for the next timer run, in clock ticks.
142    pub fn load_timer_value(&self, channel: Channel) -> u32 {
143        ral::read_reg!(ral::pit::timer, self.timer(channel), LDVAL).saturating_add(1)
144    }
145
146    /// Enable a timer channel.
147    pub fn enable(&mut self, channel: Channel) {
148        ral::modify_reg!(ral::pit::timer, self.timer(channel), TCTRL, TEN: 1);
149    }
150
151    /// Disable a timer channel.
152    pub fn disable(&mut self, channel: Channel) {
153        ral::modify_reg!(ral::pit::timer, self.timer(channel), TCTRL, TEN: 0);
154    }
155
156    /// Returns `true` if the channel is enabled.
157    pub fn is_enabled(&self, channel: Channel) -> bool {
158        ral::read_reg!(ral::pit::timer, self.timer(channel), TCTRL, TEN == 1)
159    }
160
161    /// Returns `true` if the timer has elapsed.
162    pub fn is_elapsed(&self, channel: Channel) -> bool {
163        ral::read_reg!(ral::pit::timer, self.timer(channel), TFLG, TIF == 1)
164    }
165
166    /// Clear the elapsed flag.
167    pub fn clear_elapsed(&self, channel: Channel) {
168        ral::write_reg!(ral::pit::timer, self.timer(channel), TFLG, TIF: 1);
169    }
170
171    /// Chain adjacent channels together, forming a larger timer.
172    ///
173    /// A chained timer will decrement by one each time the previous channel
174    /// expires. The previous channel is the channel with a lower number.
175    ///
176    /// For example, to chain `Chan2` to `Chan1`, supply `Chan2` as an argument.
177    /// Then, every time `Chan1` expires, `Chan2` decrements by one.
178    ///
179    /// You may chain multiple channels together, forming 96-bit and 128-bit
180    /// timers. When reading the timer values, take care to handle overflows.
181    ///
182    /// If you're generating interrupts from a chained timer, the channel with
183    /// the larger number should generate that interrupt.
184    ///
185    /// When querying for the time tracked by chained timers, make sure you
186    /// account for rollover in software. Keep in mind that the lifetime timer
187    /// will handle rollover automatically; see [`lifetime_value`](Self::lifetime_value)
188    /// for more information.
189    ///
190    /// # Errors
191    ///
192    /// Returns [`CannotChainError`] if `channel` is `Chan0`, since there is
193    /// no previous channel to chain to.
194    pub fn enable_chaining(&mut self, channel: Channel) -> Result<(), CannotChainError> {
195        if channel == Channel::Chan0 {
196            return Err(CannotChainError(()));
197        }
198        ral::modify_reg!(ral::pit::timer, self.timer(channel), TCTRL, CHN: 1);
199        Ok(())
200    }
201
202    /// Disable chaining for a channel.
203    ///
204    /// See [`enable_chaining`](Self::enable_chaining) for more information.
205    ///
206    /// # Errors
207    ///
208    /// Returns [`CannotChainError`] if `channel` is `Chan0`, since `Chan0`
209    /// cannot be chained.
210    pub fn disable_chaining(&mut self, channel: Channel) -> Result<(), CannotChainError> {
211        if channel == Channel::Chan0 {
212            return Err(CannotChainError(()));
213        }
214        ral::modify_reg!(ral::pit::timer, self.timer(channel), TCTRL, CHN: 0);
215        Ok(())
216    }
217
218    /// Returns `true` if a channel is chained to the previous channel.
219    ///
220    /// See [`enable_chaining`](Self::enable_chaining) for more information.
221    ///
222    /// Always returns `false` for `Chan0`.
223    pub fn is_chained(&self, channel: Channel) -> bool {
224        if channel == Channel::Chan0 {
225            return false;
226        }
227        ral::read_reg!(ral::pit::timer, self.timer(channel), TCTRL, CHN == 1)
228    }
229
230    /// Read the lifetime register value.
231    ///
232    /// The lifetime register is a 64-bit register that combines channels 0 and 1.
233    /// It is only valid when channel 1 is chained to channel 0.
234    ///
235    /// The method assumes that channel 1 is chained to channel 0. See
236    /// [`enable_chaining`](Self::enable_chaining) for more information. Additionally,
237    /// the method assumes both channels 0 and 1 are enabled.
238    ///
239    /// Given the hardware support, this call does not require a loop to account for
240    /// rollover.
241    ///
242    /// This method implements the recommended fix for errata ERR050130.
243    pub fn lifetime_value(&self) -> u64 {
244        let mut high = self.pit.LTMR64H.read();
245        let mut low = self.pit.LTMR64L.read();
246
247        let ldval0 = self.pit.TIMER[0].LDVAL.read();
248        if low == ldval0 {
249            high = self.pit.LTMR64H.read();
250            low = self.pit.LTMR64L.read();
251        }
252
253        (u64::from(high) << 32) + u64::from(low)
254    }
255}
256
257fn new(pit: AnyPitInstance) -> Pit {
258    ral::modify_reg!(ral::pit, pit, MCR, MDIS: MDIS_0);
259    // Reset all PIT channels
260    //
261    // PIT channels may be used by a system's boot ROM, or another
262    // user. Set them to a known, good state.
263    ral::write_reg!(ral::pit::timer, &pit.TIMER[0], TCTRL, 0);
264    ral::write_reg!(ral::pit::timer, &pit.TIMER[1], TCTRL, 0);
265    ral::write_reg!(ral::pit::timer, &pit.TIMER[2], TCTRL, 0);
266    ral::write_reg!(ral::pit::timer, &pit.TIMER[3], TCTRL, 0);
267
268    Pit { pit }
269}
270
271/// ```compile_fail
272/// use imxrt_hal::pit::Pit;
273/// fn not_sync<T: Sync>() {}
274///
275/// not_sync::<Pit>();
276/// ```
277#[cfg(doctest)]
278struct PitNotSync;
279
280/// ```send
281/// use imxrt_hal::pit::Pit;
282/// fn is_send<T: Send>() {}
283///
284/// is_send::<Pit>();
285/// ```
286#[cfg(doctest)]
287struct PitSend;