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_usbd::BusAdapter;
15//! use imxrt_usbd::gpt;
16//!
17//! # struct Ps;
18//! # unsafe impl imxrt_usbd::Peripherals for Ps { fn usb(&self) -> *const () { panic!() } fn usbphy(&self) -> *const () { panic!() } }
19//! # static EP_MEMORY: imxrt_usbd::EndpointMemory<1024> = imxrt_usbd::EndpointMemory::new();
20//! # static EP_STATE: imxrt_usbd::EndpointState = imxrt_usbd::EndpointState::max_endpoints();
21//!
22//! # let my_usb_peripherals = // Your Peripherals instance...
23//! #   Ps;
24//! let bus_adapter = BusAdapter::new(
25//!     // ...
26//! #    my_usb_peripherals,
27//! #    &EP_MEMORY,
28//! #    &EP_STATE,
29//! );
30//!
31//! // Prepare a GPT before creating a USB device;
32//! bus_adapter.gpt_mut(gpt::Instance::Gpt0, |gpt| {
33//!     gpt.stop(); // Stop the timer, just in case it's already running...
34//!     gpt.clear_elapsed(); // Clear any outstanding elapsed flags
35//!     gpt.set_interrupt_enabled(false); // Enable or disable interrupts
36//!     gpt.set_load(75_000); // Elapse after 75ms (75000us)
37//!     gpt.set_mode(gpt::Mode::Repeat); // Repeat the timer after it elapses
38//!     gpt.reset(); // Load the value into the counter
39//! });
40//! // The timer isn't running until you call run()...
41//!
42//! # use usb_device::prelude::*;
43//! let bus_allocator = usb_device::bus::UsbBusAllocator::new(bus_adapter);
44//!
45//! let mut device = UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x5824, 0x27dd))
46//!     .strings(&[StringDescriptors::default().product("imxrt-usbd")]).unwrap()
47//!     .build();
48//!
49//! // You can still access the timer through the bus() method on
50//! // the USB device.
51//! device.bus().gpt_mut(gpt::Instance::Gpt0, |gpt| gpt.run()); // Timer running!
52//!
53//! loop {
54//!     device.bus().gpt_mut(gpt::Instance::Gpt0, |gpt| {
55//!         if gpt.is_elapsed() {
56//!             gpt.clear_elapsed();
57//!             // Timer elapsed!
58//!
59//!             // If your mode is Mode::OneShot, you will need
60//!             // to call reset() to re-enable the timer. You also
61//!             // need to call reset() whenever you change the timer
62//!             // load value.
63//!         }
64//!     });
65//! }
66//! ```
67
68use crate::ral;
69
70/// GPT timer mode.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[repr(u32)]
73pub enum Mode {
74    /// In one shot mode, the timer will count down to zero, generate an interrupt,
75    /// and stop until the counter is reset by software.
76    OneShot = 0,
77    /// In repeat mode, the timer will count down to zero, generate an interrupt and
78    /// automatically reload the counter value to start again.
79    Repeat = 1,
80}
81
82/// GPT instance identifiers.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84#[repr(u32)]
85pub enum Instance {
86    /// The GPT0 timer instance.
87    Gpt0,
88    /// The GPT1 timer instance.
89    Gpt1,
90}
91
92/// General purpose timer (GPT).
93///
94/// USB GPTs have a 1us resolution. The counter is 24 bits wide. GPTs can generate
95/// USB interrupts that are independent of USB protocol interrupts. This lets you
96/// add additional, time-driven logic into your USB ISR and driver state machine.
97///
98/// See the module-level documentation for an example.
99pub struct Gpt<'a> {
100    /// Borrow of USB registers from a peripheral
101    usb: &'a mut ral::usb::Instance,
102    /// GPT instance
103    gpt: Instance,
104}
105
106impl<'a> Gpt<'a> {
107    /// Create a GPT instance over the USB core registers.
108    ///
109    /// *Why take a mutable reference?* The mutable reference prevents you from
110    /// creating two GPTs that alias the same GPT instance.
111    ///
112    /// *Why not `pub`?* The `ral::usb::Instance` isn't compatible with the
113    /// `imxrt_ral::usb::Instance` type, since we're duplicating the RAL module
114    /// in our package. Since that type isn't exposed outside of this crate, no
115    /// one could create this `Gpt`, anyway.
116    pub(crate) fn new(usb: &'a mut ral::usb::Instance, 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.min(0xFF_FFFF).max(1).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}