Skip to main content

imxrt_hal/common/
gpt.rs

1//! General purpose timer.
2//!
3//! # Features
4//!
5//! The GPTs are count-up, wrapping timers that run off of the IPG clock or the
6//! crystal oscillator (24MHz). Each GPT has three compare registers,
7//! called **output comparison registers (OCR)**. When the counter reaches a
8//! value in an OCR, the GPT signals the comparison through status flags.
9//! A comparison can generate an interrupt.
10//!
11//! # GPT modes
12//!
13//! The table below summarizes the effects of an output comparison operation
14//! when in restart mode, and when in free-running mode. See the subsequent
15//! sections for a discussion of the two modes.
16//!
17//! | GPT Mode     | OCR1                                      | OCR2                                      | OCR3         |
18//! | ------------ | ----------------------------------------- | ----------------------------------------- | ------------ |
19//! | Restart      | Resets the counter to zero                | No effect; counter continues incrementing | No effect... |
20//! | Free-running | No effect; counter continues incrementing | No effect ...                             | No effect... |
21//!
22//! In summary, **OCR1 is special in restart mode**, as it will reset the value in the GPT counter.
23//!
24//! Select a mode with [`set_mode()`](struct.GPT.html#method.set_mode).
25//!
26//! ## Restart mode
27//!
28//! The GPTs default to 'restart mode.' In restart mode, a compare on **channel
29//! 1** will reset the GPT counter to zero. Compare events on channels 2 and 3
30//! will not reset the GPT counter. If you would rather have the GPT counter continue
31//! no matter the comparison event, set the GPT to free-running mode.
32//!
33//! ## Free-running mode
34//!
35//! The GPTs may be in free-running mode. When a comparion event occurs in free-running
36//! mode, the counter continues to increment, eventually wrapping around. Free-running
37//! mode treats all channels as equal; that is, channel 1 is no different than channel
38//! 2 or 3.
39//!
40//! # Reset on enable
41//!
42//! Reset on enable is a complementary feature to the two modes. When reset on enable
43//! is active, the GPT counter will reset to zero each time the timer is enabled. By default,
44//! the counter will restart at whatever value is currently in the counter. The default
45//! behavior lets a user 'pause' the counter by disabling the GPT. On the other hand,
46//! reset on enable lets users reset the counter by disabling and re-enabling the GPT.
47//!
48//! The table below summarizes the 'reset on enable' behaviors. Use
49//! [`set_reset_on_enable()`](struct.GPT.html#method.set_reset_on_enable) to configure
50//! the reset on enable behavior.
51//!
52//! | State   | Behavior                                                 |
53//! | ------- | -------------------------------------------------------- |
54//! | `false` | When the GPT is disabled, it maintains its counter value |
55//! | `true`  | When the GPT is disabled, the counter resets to zero     |
56//!
57//! # GPTs and system WAIT / STOP
58//!
59//! By default, GPTs do not run when the process is in in wait mode. Use
60//! [`set_wait_mode_enable(true)`](struct.GPT.html#method.set_wait_mode_enable)
61//! to enable GPTs in wait mode.
62//!
63//! If the GPT stops counting in WAIT / STOP system states, the counter freezes its
64//! counter. When the processor transitions into RUNNING, the counter increments from
65//! its previously-frozen value (provided the GPT was enabled).
66//!
67//! # Example
68//!
69//! These examples do not demonstrate how to configure the GPT clock gates
70//! or clock root, PERCLK. To configure PERCLK, see [the CCM peripheral clock
71//! module](crate::ccm::perclk_clk).
72//!
73//! Create, configure, and wait for ticks to elapse:
74//!
75//! ```no_run
76//! use imxrt_ral::gpt::GPT1;
77//! use imxrt_hal::gpt::{Gpt, ClockSource, Mode, OutputCompareRegister};
78//!
79//! let mut gpt = Gpt::new(unsafe { GPT1::instance() });
80//! gpt.set_clock_source(ClockSource::HighFrequencyReferenceClock);
81//! gpt.set_divider(16);
82//! gpt.set_mode(Mode::FreeRunning);
83//! gpt.enable();
84//!
85//! // Later...
86//!
87//! const OCR: OutputCompareRegister = OutputCompareRegister::OCR1;
88//! gpt.clear_elapsed(OCR);
89//! let count = gpt.count();
90//! let ticks_to_wait = gpt.count().wrapping_add(40_000);
91//! gpt.set_output_compare_count(OCR, ticks_to_wait);
92//!
93//! while !gpt.is_elapsed(OCR) {}
94//! gpt.clear_elapsed(OCR);
95//! ```
96//!
97//! # TODO
98//!
99//! - Input capture. Each GPT can capture the value of the counter
100//!   when a pin state changes. When the pin state changes, the
101//!   GPT can generate an interrupt.
102//! - Output generation. When one of the three comparison registers
103//!   match the counter, the GPT can generate a signal on an output
104//!   pin.
105
106use crate::ral;
107
108/// Any GPT instance.
109type AnyGptInstance = crate::AnyInstance<ral::gpt::RegisterBlock>;
110
111/// A general purpose timer
112///
113/// The timers support three output compare registers. When a compare register
114/// matches the value of the counter, the GPT may trigger an interrupt.
115///
116/// By default, the timer runs in wait mode.
117pub struct Gpt {
118    /// Registers for this GPT instance
119    gpt: AnyGptInstance,
120}
121
122/// GPT clock source.
123#[cfg_attr(feature = "defmt", derive(defmt::Format))]
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125#[repr(u32)]
126pub enum ClockSource {
127    /// No clock selection
128    NoClock,
129    /// Peripheral clock (`ipg_clk`)
130    PeripheralClock,
131    /// High frequency reference clock (`ipg_clk_highfreq`)
132    HighFrequencyReferenceClock,
133    /// External clock
134    ExternalClock,
135    /// Low frequency reference clock (`ipg_clk_32k`)
136    LowFrequencyReferenceClock,
137    /// Crystal oscillator as reference clock (`ipg_clk_24M`)
138    CrystalOscillator,
139}
140
141/// An output compare register (OCR).
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
143#[derive(Clone, Copy, PartialEq, Eq, Debug)]
144#[repr(usize)]
145pub enum OutputCompareRegister {
146    /// The first output compare register.
147    OCR1 = 0,
148    /// The second output compare register.
149    OCR2 = 1,
150    /// The third output compare register.
151    OCR3 = 2,
152}
153
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156/// Possible modes of the GPT.
157pub enum Mode {
158    /// Restart mode
159    ///
160    /// A comparions event on channel 1 will reset the GPT counter.
161    /// Comparison events on channels 2 and 3 do not reset the counter.
162    Restart,
163    /// Free running mode
164    ///
165    /// Comparisons on channel 1 are treated like comparions on channels
166    /// 2 and 3. The counter continues to increment on comparison.
167    FreeRunning,
168}
169
170impl Gpt {
171    /// Create a GPT timer from the RAL's GPT instance.
172    ///
173    /// When `new` returns, the GPT is reset and disabled.
174    pub fn new<const N: u8>(gpt: ral::gpt::Instance<N>) -> Self {
175        new(crate::into_any(gpt))
176    }
177
178    /// Returns the clock divider value.
179    pub fn divider(&self) -> u32 {
180        ral::read_reg!(ral::gpt, self.gpt, PR, PRESCALER) + 1
181    }
182
183    /// Set the divider value.
184    ///
185    /// The divider value is clamped between 1 and 4096.
186    /// A change in the divider cause the prescaler counter
187    /// to reset and a new count period to start immediately.
188    pub fn set_divider(&mut self, divider: u32) {
189        let prescaler = divider.clamp(1, 4096) - 1;
190        ral::modify_reg!(ral::gpt, self.gpt, PR, PRESCALER: prescaler);
191    }
192
193    /// Return the 24MHz clock divider.
194    pub fn divider_24mhz(&self) -> u32 {
195        ral::read_reg!(ral::gpt, self.gpt, PR, PRESCALER24M) + 1
196    }
197
198    /// Set the 24MHz clock divider.
199    ///
200    /// The divider value is clamped between 1 and 16.
201    /// 24MHz crystal clock is divided by the divider
202    /// before selected by the clock selection. If 24M
203    /// crystal clock is not selected, this feild takes no effect.
204    pub fn set_divider_24mhz(&mut self, divider: u32) {
205        let prescaler = divider.clamp(1, 16) - 1;
206        ral::modify_reg!(ral::gpt, self.gpt, PR, PRESCALER24M: prescaler);
207    }
208
209    /// Returns the current mode of the GPT.
210    pub fn mode(&self) -> Mode {
211        if ral::read_reg!(ral::gpt, self.gpt, CR, FRR == 0) {
212            Mode::Restart
213        } else {
214            Mode::FreeRunning
215        }
216    }
217
218    /// Set the GPT mode.
219    ///
220    /// Refer to the module level documentation for more information on the GPT modes.
221    pub fn set_mode(&mut self, mode: Mode) {
222        ral::modify_reg!(ral::gpt, self.gpt, CR, FRR: (mode as u32))
223    }
224
225    /// Returns the GPT clock source
226    pub fn clock_source(&self) -> ClockSource {
227        let clock_source = ral::read_reg!(ral::gpt, self.gpt, CR, CLKSRC);
228        match clock_source {
229            0 => ClockSource::NoClock,
230            1 => ClockSource::PeripheralClock,
231            2 => ClockSource::HighFrequencyReferenceClock,
232            3 => ClockSource::ExternalClock,
233            4 => ClockSource::LowFrequencyReferenceClock,
234            5 => ClockSource::CrystalOscillator,
235            _ => unreachable!("Reserved GPT clock source"),
236        }
237    }
238
239    /// Set the GPT clock source.
240    pub fn set_clock_source(&mut self, clock_source: ClockSource) {
241        ral::modify_reg!(
242            ral::gpt,
243            self.gpt,
244            CR,
245            CLKSRC: clock_source as u32,
246            EN_24M: (ClockSource::CrystalOscillator == clock_source) as u32);
247    }
248
249    /// Set the reset on enable behavior
250    ///
251    /// See [the module-level docs](crate::gpt#reset-on-enable) for more information.
252    pub fn set_reset_on_enable(&mut self, reset_on_enable: bool) {
253        ral::modify_reg!(ral::gpt, self.gpt, CR, ENMOD: (reset_on_enable as u32));
254    }
255
256    /// Returns `true` if the GPT counter will reset the next time it is enabled.
257    pub fn is_reset_on_enable(&self) -> bool {
258        ral::read_reg!(ral::gpt, self.gpt, CR, ENMOD == 1)
259    }
260
261    /// Enable the GPT.
262    ///
263    /// When enabled, the counter starts counting. The value of the counter
264    /// is determined by the reset on enable setting. See
265    /// [`set_reset_on_enable`](Gpt::set_reset_on_enable).
266    pub fn enable(&mut self) {
267        ral::modify_reg!(ral::gpt, self.gpt, CR, EN: 1);
268    }
269
270    /// Disable the GPT.
271    ///
272    /// When disabled, the count will stop counting.
273    pub fn disable(&mut self) {
274        ral::modify_reg!(ral::gpt, self.gpt, CR, EN: 0);
275    }
276
277    /// Indicates if the GPT is enabled (`true`) or disabled (`false`).
278    pub fn is_enabled(&self) -> bool {
279        ral::read_reg!(ral::gpt, self.gpt, CR, EN == 1)
280    }
281
282    /// Allow the GPT to run in wait mode; or, prevent the GPT from running
283    /// in wait mode.
284    pub fn set_wait_mode_enable(&mut self, wait: bool) {
285        ral::modify_reg!(ral::gpt, self.gpt, CR, WAITEN: (wait as u32));
286    }
287
288    /// Indicates if the GPT runs while in wait mode.
289    pub fn is_wait_mode_enabled(&self) -> bool {
290        ral::read_reg!(ral::gpt, self.gpt, CR, WAITEN == 1)
291    }
292
293    /// Enable the GPT interrupt when the output compares.
294    pub fn set_output_interrupt_on_compare(&mut self, ocr: OutputCompareRegister, intr: bool) {
295        match ocr {
296            OutputCompareRegister::OCR1 => {
297                ral::modify_reg!(ral::gpt, self.gpt, IR, OF1IE: intr as u32)
298            }
299            OutputCompareRegister::OCR2 => {
300                ral::modify_reg!(ral::gpt, self.gpt, IR, OF2IE: intr as u32)
301            }
302            OutputCompareRegister::OCR3 => {
303                ral::modify_reg!(ral::gpt, self.gpt, IR, OF3IE: intr as u32)
304            }
305        }
306    }
307
308    /// Returns `true` if a comparison triggers an interrupt.
309    pub fn is_output_interrupt_on_compare(&self, ocr: OutputCompareRegister) -> bool {
310        match ocr {
311            OutputCompareRegister::OCR1 => ral::read_reg!(ral::gpt, self.gpt, IR, OF1IE == 1),
312            OutputCompareRegister::OCR2 => ral::read_reg!(ral::gpt, self.gpt, IR, OF2IE == 1),
313            OutputCompareRegister::OCR3 => ral::read_reg!(ral::gpt, self.gpt, IR, OF3IE == 1),
314        }
315    }
316
317    /// Returns the current count of the GPT.
318    pub fn count(&self) -> u32 {
319        ral::read_reg!(ral::gpt, self.gpt, CNT)
320    }
321
322    /// Set an output compare register to trigger on the next `count` value of the
323    /// counter.
324    pub fn set_output_compare_count(&self, ocr: OutputCompareRegister, count: u32) {
325        ral::write_reg!(ral::gpt, self.gpt, OCR[ocr as usize], count);
326    }
327
328    /// Returns the current output compare count for the specified register.
329    pub fn output_compare_count(&self, ocr: OutputCompareRegister) -> u32 {
330        ral::read_reg!(ral::gpt, self.gpt, OCR[ocr as usize])
331    }
332
333    /// Returns `true` if the time tracked by the OCR has elapsed.
334    pub fn is_elapsed(&self, ocr: OutputCompareRegister) -> bool {
335        match ocr {
336            OutputCompareRegister::OCR1 => ral::read_reg!(ral::gpt, self.gpt, SR, OF1 == 1),
337            OutputCompareRegister::OCR2 => ral::read_reg!(ral::gpt, self.gpt, SR, OF2 == 1),
338            OutputCompareRegister::OCR3 => ral::read_reg!(ral::gpt, self.gpt, SR, OF3 == 1),
339        }
340    }
341
342    /// Clear the elapsed flag.
343    pub fn clear_elapsed(&self, ocr: OutputCompareRegister) {
344        match ocr {
345            OutputCompareRegister::OCR1 => ral::modify_reg!(ral::gpt, self.gpt, SR, OF1: 1),
346            OutputCompareRegister::OCR2 => ral::modify_reg!(ral::gpt, self.gpt, SR, OF2: 1),
347            OutputCompareRegister::OCR3 => ral::modify_reg!(ral::gpt, self.gpt, SR, OF3: 1),
348        }
349    }
350
351    /// Enable / disable an interrupt when the GPT counter rolls over from `u32::max_value()` to
352    /// `0`.
353    ///
354    /// The GPT triggers a rollover regardless of the GPT mode.
355    pub fn set_rollover_interrupt_enable(&mut self, rov: bool) {
356        ral::modify_reg!(ral::gpt, self.gpt, IR, ROVIE: (rov as u32));
357    }
358
359    /// Returns `true` if a rollover generates an interrupt.
360    pub fn is_rollover_interrupt_enabled(&self) -> bool {
361        ral::read_reg!(ral::gpt, self.gpt, IR, ROVIE == 1)
362    }
363
364    /// Returns `true` if the rollover flag is set.
365    ///
366    /// A rollover occurs when the counter rolls over from `u32::max_value()` to `0`. Rollover
367    /// may occur regardless of the GPT mode.
368    pub fn is_rollover(&self) -> bool {
369        ral::read_reg!(ral::gpt, self.gpt, SR, ROV == 1)
370    }
371
372    /// Clear the rollover status flag.
373    ///
374    /// Users must clear the rollover flag if a rollover triggered an interrupt.
375    pub fn clear_rollover(&self) {
376        ral::modify_reg!(ral::gpt, self.gpt, SR, ROV: 1);
377    }
378
379    /// Issue a software reset.
380    ///
381    /// This reset does not change
382    ///
383    /// - the enable state.
384    /// - the clock selection.
385    /// - the wait, debug, and doze configurations.
386    pub fn reset(&mut self) {
387        ral::modify_reg!(ral::gpt, self.gpt, CR, SWR: 1);
388        while ral::read_reg!(ral::gpt, self.gpt, CR, SWR == SWR_1) {}
389    }
390}
391
392fn new(gpt: AnyGptInstance) -> Gpt {
393    // Disable the timer.
394    ral::modify_reg!(ral::gpt, gpt, CR, EN: 0);
395    // Software reset
396    ral::modify_reg!(ral::gpt, gpt, CR, SWR: 1);
397    while ral::read_reg!(ral::gpt, gpt, CR, SWR == 1) {}
398    // Clear all status registers.
399    ral::write_reg!(ral::gpt, gpt, SR, 0b11_1111);
400    Gpt { gpt }
401}