Skip to main content

imxrt_usbd/
gpt.rs

1//! USB general purpose timers.
2//!
3//! Each USB OTG peripheral has two general purpose timers (GPT). You can access
4//! GPTs through your USB driver.
5//!
6//! # Example
7//!
8//! This example shows how to access a GPT through the
9//! [`BusAdapter`](crate::BusAdapter) API. The example skips
10//! the bus adapter and USB device setup in order to focus on the GPT API. See the bus
11//! adapter documentation for more information.
12//!
13//! ```no_run
14//! use imxrt_ral as ral;
15//! use imxrt_usbd::{BusAdapter, Instances};
16//! use imxrt_usbd::gpt;
17//!
18//! # static EP_MEMORY: imxrt_usbd::EndpointMemory<1024> = imxrt_usbd::EndpointMemory::new();
19//! # static EP_STATE: imxrt_usbd::EndpointState = imxrt_usbd::EndpointState::max_endpoints();
20//!
21//! # let instances = Instances {
22//! #     usb: unsafe { ral::usb::USB::instance() },
23//! #     usbnc: unsafe { ral::usbnc::USBNC::instance() },
24//! #     usbphy: unsafe { ral::usbphy::USBPHY::instance() },
25//! # };
26//! let bus_adapter = BusAdapter::new(
27//!     // ...
28//! #    instances,
29//! #    &EP_MEMORY,
30//! #    &EP_STATE,
31//! );
32//!
33//! // Prepare a GPT before creating a USB device;
34//! bus_adapter.gpt_mut(gpt::Instance::Gpt0, |gpt| {
35//!     gpt.stop(); // Stop the timer, just in case it's already running...
36//!     gpt.clear_elapsed(); // Clear any outstanding elapsed flags
37//!     gpt.set_interrupt_enabled(false); // Enable or disable interrupts
38//!     gpt.set_load(75_000); // Elapse after 75ms (75000us)
39//!     gpt.set_mode(gpt::Mode::Repeat); // Repeat the timer after it elapses
40//!     gpt.reset(); // Load the value into the counter
41//! });
42//! // The timer isn't running until you call run()...
43//!
44//! # use usb_device::prelude::*;
45//! let bus_allocator = usb_device::bus::UsbBusAllocator::new(bus_adapter);
46//!
47//! let mut device = UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x5824, 0x27dd))
48//!     .strings(&[StringDescriptors::default().product("imxrt-usbd")]).unwrap()
49//!     .build();
50//!
51//! // You can still access the timer through the bus() method on
52//! // the USB device.
53//! device.bus().gpt_mut(gpt::Instance::Gpt0, |gpt| gpt.run()); // Timer running!
54//!
55//! loop {
56//!     device.bus().gpt_mut(gpt::Instance::Gpt0, |gpt| {
57//!         if gpt.is_elapsed() {
58//!             gpt.clear_elapsed();
59//!             // Timer elapsed!
60//!
61//!             // If your mode is Mode::OneShot, you will need
62//!             // to call reset() to re-enable the timer. You also
63//!             // need to call reset() whenever you change the timer
64//!             // load value.
65//!         }
66//!     });
67//! }
68//! ```
69
70use crate::ral;
71
72/// GPT timer mode.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74#[repr(u32)]
75pub enum Mode {
76    /// In one shot mode, the timer will count down to zero, generate an interrupt,
77    /// and stop until the counter is reset by software.
78    OneShot = 0,
79    /// In repeat mode, the timer will count down to zero, generate an interrupt and
80    /// automatically reload the counter value to start again.
81    Repeat = 1,
82}
83
84/// GPT instance identifiers.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86#[repr(u32)]
87pub enum Instance {
88    /// The GPT0 timer instance.
89    Gpt0,
90    /// The GPT1 timer instance.
91    Gpt1,
92}
93
94/// General purpose timer (GPT).
95///
96/// USB GPTs have a 1us resolution. The counter is 24 bits wide. GPTs can generate
97/// USB interrupts that are independent of USB protocol interrupts. This lets you
98/// add additional, time-driven logic into your USB ISR and driver state machine.
99///
100/// See the module-level documentation for an example.
101pub struct Gpt<'a> {
102    /// Borrow of USB registers from a peripheral
103    usb: &'a mut ral::AnyUsbInstance,
104    /// GPT instance
105    gpt: Instance,
106}
107
108impl<'a> Gpt<'a> {
109    /// Create a GPT instance over the USB core registers.
110    ///
111    /// *Why take a mutable reference?* The mutable reference prevents you from
112    /// creating two GPTs that alias the same GPT instance.
113    ///
114    /// *Why not `pub`?* The `ral::AnyUsbInstance` type has an erased instance
115    /// number and isn't exposed outside of this crate.
116    pub(crate) fn new(usb: &'a mut ral::AnyUsbInstance, gpt: Instance) -> Self {
117        Self { usb, gpt }
118    }
119
120    /// Returns the GPT instance identifier.
121    pub fn instance(&self) -> Instance {
122        self.gpt
123    }
124
125    /// Run the GPT timer.
126    ///
127    /// Run will start counting down the timer. Use `stop()` to cancel a running timer.
128    pub fn run(&mut self) {
129        match self.gpt {
130            Instance::Gpt0 => ral::modify_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTRUN: 1),
131            Instance::Gpt1 => ral::modify_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTRUN: 1),
132        }
133    }
134
135    /// Indicates if the timer is running (`true`) or stopped (`false`).
136    pub fn is_running(&self) -> bool {
137        match self.gpt {
138            Instance::Gpt0 => ral::read_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTRUN == 1),
139            Instance::Gpt1 => ral::read_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTRUN == 1),
140        }
141    }
142
143    /// Stop the timer.
144    pub fn stop(&mut self) {
145        match self.gpt {
146            Instance::Gpt0 => ral::modify_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTRUN: 0),
147            Instance::Gpt1 => ral::modify_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTRUN: 0),
148        }
149    }
150
151    /// Reset the timer.
152    ///
153    /// `reset` loads the counter value. It does not stop a running counter.
154    pub fn reset(&mut self) {
155        match self.gpt {
156            Instance::Gpt0 => ral::modify_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTRST: 1),
157            Instance::Gpt1 => ral::modify_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTRST: 1),
158        }
159    }
160
161    /// Set the timer mode.
162    pub fn set_mode(&mut self, mode: Mode) {
163        match self.gpt {
164            Instance::Gpt0 => {
165                ral::modify_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTMODE: mode as u32)
166            }
167            Instance::Gpt1 => {
168                ral::modify_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTMODE: mode as u32)
169            }
170        }
171    }
172
173    /// Returns the timer mode.
174    pub fn mode(&self) -> Mode {
175        let mode: u32 = match self.gpt {
176            Instance::Gpt0 => {
177                ral::read_reg!(ral::usb, self.usb, GPTIMER0CTRL, GPTMODE)
178            }
179            Instance::Gpt1 => {
180                ral::read_reg!(ral::usb, self.usb, GPTIMER1CTRL, GPTMODE)
181            }
182        };
183
184        if mode == (Mode::Repeat as u32) {
185            Mode::Repeat
186        } else if mode == (Mode::OneShot as u32) {
187            Mode::OneShot
188        } else {
189            // All raw mode values handled
190            unreachable!()
191        }
192    }
193
194    /// Set the counter load value.
195    ///
196    /// `us` is the number of microseconds to count. `us` will saturate at a 24-bit value (0xFFFFFF,
197    /// or 16.777215 seconds). A value of `0` will result in a 1us delay.
198    ///
199    /// Note that the load count value is not loaded until the next call to `reset()` (one shot mode)
200    /// or until after the timer elapses (repeat mode).
201    pub fn set_load(&mut self, us: u32) {
202        let count = us.clamp(1, 0xFF_FFFF).saturating_sub(1);
203        match self.gpt {
204            Instance::Gpt0 => ral::write_reg!(ral::usb, self.usb, GPTIMER0LD, count),
205            Instance::Gpt1 => ral::write_reg!(ral::usb, self.usb, GPTIMER1LD, count),
206        }
207    }
208
209    /// Returns the counter load value.
210    pub fn load(&self) -> u32 {
211        match self.gpt {
212            Instance::Gpt0 => ral::read_reg!(ral::usb, self.usb, GPTIMER0LD),
213            Instance::Gpt1 => ral::read_reg!(ral::usb, self.usb, GPTIMER1LD),
214        }
215    }
216
217    /// Indicates if the timer has elapsed.
218    ///
219    /// If the timer has elapsed, you should clear the elapsed flag with `clear_elapsed()`.
220    pub fn is_elapsed(&self) -> bool {
221        match self.gpt {
222            Instance::Gpt0 => ral::read_reg!(ral::usb, self.usb, USBSTS, TI0 == 1),
223            Instance::Gpt1 => ral::read_reg!(ral::usb, self.usb, USBSTS, TI1 == 1),
224        }
225    }
226
227    /// Clear the flag that indicates the timer has elapsed.
228    pub fn clear_elapsed(&mut self) {
229        match self.gpt {
230            Instance::Gpt0 => ral::write_reg!(ral::usb, self.usb, USBSTS, TI0: 1),
231            Instance::Gpt1 => ral::write_reg!(ral::usb, self.usb, USBSTS, TI1: 1),
232        }
233    }
234
235    /// Enable or disable interrupt generation when the timer elapses.
236    ///
237    /// If enabled (`true`), an elapsed GPT will generate an interrupt. This happens regardless of the USB
238    /// interrupt enable state.
239    pub fn set_interrupt_enabled(&mut self, enable: bool) {
240        match self.gpt {
241            Instance::Gpt0 => ral::modify_reg!(ral::usb, self.usb, USBINTR, TIE0: enable as u32),
242            Instance::Gpt1 => ral::modify_reg!(ral::usb, self.usb, USBINTR, TIE1: enable as u32),
243        }
244    }
245
246    /// Indicates if interrupt generation is enabled.
247    pub fn is_interrupt_enabled(&self) -> bool {
248        match self.gpt {
249            Instance::Gpt0 => ral::read_reg!(ral::usb, self.usb, USBINTR, TIE0 == 1),
250            Instance::Gpt1 => ral::read_reg!(ral::usb, self.usb, USBINTR, TIE1 == 1),
251        }
252    }
253}