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