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}