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}