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}