aboutsummaryrefslogtreecommitdiff
path: root/embassy-nxp/src
diff options
context:
space:
mode:
authori509VCB <[email protected]>2025-07-09 23:08:59 -0500
committeri509VCB <[email protected]>2025-07-22 11:07:10 -0500
commit1ad5d5a771d5109a763361454fb724b85ae25fdd (patch)
tree62beeabed93b1108b8b4888aaac259f0a3256d01 /embassy-nxp/src
parentd656b174ed8976b9d77ba8098042d75dd76ef733 (diff)
nxp: Add MIMXRT1011 GPIO and time driver
PIT is used for the time driver
Diffstat (limited to 'embassy-nxp/src')
-rw-r--r--embassy-nxp/src/chips/mimxrt1011.rs113
-rw-r--r--embassy-nxp/src/gpio.rs2
-rw-r--r--embassy-nxp/src/gpio/rt1xxx.rs895
-rw-r--r--embassy-nxp/src/lib.rs87
-rw-r--r--embassy-nxp/src/time_driver/pit.rs187
5 files changed, 1279 insertions, 5 deletions
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs
new file mode 100644
index 000000000..a74d953fc
--- /dev/null
+++ b/embassy-nxp/src/chips/mimxrt1011.rs
@@ -0,0 +1,113 @@
1// This must be imported so that __preinit is defined.
2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_hal_internal::peripherals! {
6 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
7 // peripheral types (e.g. I2C).
8 GPIO_00,
9 GPIO_01,
10 GPIO_02,
11 GPIO_03,
12 GPIO_04,
13 GPIO_05,
14 GPIO_06,
15 GPIO_07,
16 GPIO_08,
17 GPIO_09,
18 GPIO_10,
19 GPIO_11,
20 GPIO_12,
21 GPIO_13,
22 GPIO_AD_00,
23 GPIO_AD_01,
24 GPIO_AD_02,
25 GPIO_AD_03,
26 GPIO_AD_04,
27 GPIO_AD_05,
28 GPIO_AD_06,
29 GPIO_AD_07,
30 GPIO_AD_08,
31 GPIO_AD_09,
32 GPIO_AD_10,
33 GPIO_AD_11,
34 GPIO_AD_12,
35 GPIO_AD_13,
36 GPIO_AD_14,
37 GPIO_SD_00,
38 GPIO_SD_01,
39 GPIO_SD_02,
40 GPIO_SD_03,
41 GPIO_SD_04,
42 GPIO_SD_05,
43 GPIO_SD_06,
44 GPIO_SD_07,
45 GPIO_SD_08,
46 GPIO_SD_09,
47 GPIO_SD_10,
48 GPIO_SD_11,
49 GPIO_SD_12,
50 GPIO_SD_13,
51 PMIC_ON_REQ,
52}
53
54impl_gpio! {
55 // GPIO Bank 1
56 GPIO_00(Gpio1, 0);
57 GPIO_01(Gpio1, 1);
58 GPIO_02(Gpio1, 2);
59 GPIO_03(Gpio1, 3);
60 GPIO_04(Gpio1, 4);
61 GPIO_05(Gpio1, 5);
62 GPIO_06(Gpio1, 6);
63 GPIO_07(Gpio1, 7);
64 GPIO_08(Gpio1, 8);
65 GPIO_09(Gpio1, 9);
66 GPIO_10(Gpio1, 10);
67 GPIO_11(Gpio1, 11);
68 GPIO_12(Gpio1, 12);
69 GPIO_13(Gpio1, 13);
70 GPIO_AD_00(Gpio1, 14);
71 GPIO_AD_01(Gpio1, 15);
72 GPIO_AD_02(Gpio1, 16);
73 GPIO_AD_03(Gpio1, 17);
74 GPIO_AD_04(Gpio1, 18);
75 GPIO_AD_05(Gpio1, 19);
76 GPIO_AD_06(Gpio1, 20);
77 GPIO_AD_07(Gpio1, 21);
78 GPIO_AD_08(Gpio1, 22);
79 GPIO_AD_09(Gpio1, 23);
80 GPIO_AD_10(Gpio1, 24);
81 GPIO_AD_11(Gpio1, 25);
82 GPIO_AD_12(Gpio1, 26);
83 GPIO_AD_13(Gpio1, 27);
84 GPIO_AD_14(Gpio1, 28);
85
86 // GPIO Bank 2
87 GPIO_SD_00(Gpio2, 0);
88 GPIO_SD_01(Gpio2, 1);
89 GPIO_SD_02(Gpio2, 2);
90 GPIO_SD_03(Gpio2, 3);
91 GPIO_SD_04(Gpio2, 4);
92 GPIO_SD_05(Gpio2, 5);
93 GPIO_SD_06(Gpio2, 6);
94 GPIO_SD_07(Gpio2, 7);
95 GPIO_SD_08(Gpio2, 8);
96 GPIO_SD_09(Gpio2, 9);
97 GPIO_SD_10(Gpio2, 10);
98 GPIO_SD_11(Gpio2, 11);
99 GPIO_SD_12(Gpio2, 12);
100 GPIO_SD_13(Gpio2, 13);
101
102 // GPIO Bank 5
103 PMIC_ON_REQ(Gpio5, 0);
104}
105
106pub(crate) mod _generated {
107 #![allow(dead_code)]
108 #![allow(unused_imports)]
109 #![allow(non_snake_case)]
110 #![allow(missing_docs)]
111
112 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
113}
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs
index 809903d97..3049cc12d 100644
--- a/embassy-nxp/src/gpio.rs
+++ b/embassy-nxp/src/gpio.rs
@@ -1,5 +1,7 @@
1//! General purpose input/output (GPIO) driver. 1//! General purpose input/output (GPIO) driver.
2#![macro_use]
2 3
3#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] 4#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")]
5#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")]
4mod inner; 6mod inner;
5pub use inner::*; 7pub use inner::*;
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs
new file mode 100644
index 000000000..9c58e8a7d
--- /dev/null
+++ b/embassy-nxp/src/gpio/rt1xxx.rs
@@ -0,0 +1,895 @@
1#![macro_use]
2
3use core::future::Future;
4use core::ops::Not;
5use core::pin::Pin as FuturePin;
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10use nxp_pac::gpio::vals::Icr;
11use nxp_pac::iomuxc::vals::Pus;
12
13use crate::chip::{mux_address, pad_address};
14use crate::pac::common::{Reg, RW};
15#[cfg(feature = "rt")]
16use crate::pac::interrupt;
17use crate::pac::iomuxc::regs::{Ctl, MuxCtl};
18#[cfg(gpio5)]
19use crate::pac::{self, gpio::Gpio};
20
21/// The GPIO pin level for pins set on "Digital" mode.
22#[derive(Debug, Eq, PartialEq, Clone, Copy)]
23pub enum Level {
24 /// Logical low. Corresponds to 0V.
25 Low,
26 /// Logical high. Corresponds to VDD.
27 High,
28}
29
30impl From<bool> for Level {
31 fn from(val: bool) -> Self {
32 match val {
33 true => Self::High,
34 false => Self::Low,
35 }
36 }
37}
38
39impl From<Level> for bool {
40 fn from(level: Level) -> bool {
41 match level {
42 Level::Low => false,
43 Level::High => true,
44 }
45 }
46}
47
48impl Not for Level {
49 type Output = Self;
50
51 fn not(self) -> Self::Output {
52 match self {
53 Level::Low => Level::High,
54 Level::High => Level::Low,
55 }
56 }
57}
58
59/// Pull setting for a GPIO input set on "Digital" mode.
60#[derive(Debug, Clone, Copy, Eq, PartialEq)]
61pub enum Pull {
62 /// No pull.
63 None,
64
65 // TODO: What Does PUE::KEEPER mean here?
66
67 // 22 kOhm pull-up resistor.
68 Up22K,
69
70 // 47 kOhm pull-up resistor.
71 Up47K,
72
73 // 100 kOhm pull-up resistor.
74 Up100K,
75
76 // 100 kOhm pull-down resistor.
77 Down100K,
78}
79
80/// Drive strength of an output
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum Drive {
84 Disabled,
85 _150R,
86 _75R,
87 _50R,
88 _37R,
89 _30R,
90 _25R,
91 _20R,
92}
93
94/// Slew rate of an output
95#[derive(Copy, Clone, Debug, Eq, PartialEq)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub enum SlewRate {
98 Slow,
99
100 Fast,
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum Bank {
105 /// Bank 1
106 #[cfg(gpio1)]
107 Gpio1,
108
109 /// Bank 2
110 #[cfg(gpio2)]
111 Gpio2,
112
113 /// Bank 5
114 #[cfg(gpio5)]
115 Gpio5,
116}
117
118/// GPIO flexible pin.
119///
120/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
121/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
122/// mode.
123pub struct Flex<'d> {
124 pub(crate) pin: Peri<'d, AnyPin>,
125}
126
127impl<'d> Flex<'d> {
128 /// Wrap the pin in a `Flex`.
129 ///
130 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
131 /// before the pin is put into output mode.
132 #[inline]
133 pub fn new(pin: Peri<'d, impl Pin>) -> Self {
134 Self { pin: pin.into() }
135 }
136
137 /// Set the pin's pull.
138 #[inline]
139 pub fn set_pull(&mut self, pull: Pull) {
140 let (pke, pue, pus) = match pull {
141 Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
142 Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP),
143 Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP),
144 Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP),
145 Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
146 };
147
148 self.pin.pad().modify(|w| {
149 w.set_pke(pke);
150 w.set_pue(pue);
151 w.set_pus(pus);
152 });
153 }
154
155 // Set the pin's slew rate.
156 #[inline]
157 pub fn set_slewrate(&mut self, rate: SlewRate) {
158 self.pin.pad().modify(|w| {
159 w.set_sre(match rate {
160 SlewRate::Slow => false,
161 SlewRate::Fast => true,
162 });
163 });
164 }
165
166 /// Set the pin's Schmitt trigger.
167 #[inline]
168 pub fn set_schmitt(&mut self, enable: bool) {
169 self.pin.pad().modify(|w| {
170 w.set_hys(enable);
171 });
172 }
173
174 /// Put the pin into input mode.
175 ///
176 /// The pull setting is left unchanged.
177 #[inline]
178 pub fn set_as_input(&mut self) {
179 self.pin.mux().modify(|w| {
180 w.set_mux_mode(GPIO_MUX_MODE);
181 });
182
183 // Setting direction is RMW
184 critical_section::with(|_cs| {
185 self.pin.block().gdir().modify(|w| {
186 w.set_gdir(self.pin.pin_number() as usize, false);
187 });
188 })
189 }
190
191 /// Put the pin into output mode.
192 ///
193 /// The pin level will be whatever was set before (or low by default). If you want it to begin
194 /// at a specific level, call `set_high`/`set_low` on the pin first.
195 #[inline]
196 pub fn set_as_output(&mut self) {
197 self.pin.mux().modify(|w| {
198 w.set_mux_mode(GPIO_MUX_MODE);
199 });
200
201 // Setting direction is RMW
202 critical_section::with(|_cs| {
203 self.pin.block().gdir().modify(|w| {
204 w.set_gdir(self.pin.pin_number() as usize, true);
205 });
206 })
207 }
208
209 /// Put the pin into input + open-drain output mode.
210 ///
211 /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
212 /// it to high, in which case you can read the input to figure out whether another device
213 /// is driving the line low.
214 ///
215 /// The pin level will be whatever was set before (or low by default). If you want it to begin
216 /// at a specific level, call `set_high`/`set_low` on the pin first.
217 ///
218 /// The internal weak pull-up and pull-down resistors will be disabled.
219 #[inline]
220 pub fn set_as_input_output(&mut self) {
221 self.pin.pad().modify(|w| {
222 w.set_ode(true);
223 });
224 }
225
226 /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
227 /// amount of power possible.
228 ///
229 /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
230 /// really. Drivers should `set_as_disconnected()` pins when dropped.
231 ///
232 /// Note that this also disables the pull-up and pull-down resistors.
233 #[inline]
234 pub fn set_as_disconnected(&mut self) {
235 self.pin.pad().modify(|w| {
236 w.set_ode(false);
237 w.set_pke(false);
238 w.set_pue(false);
239 w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN);
240 });
241 }
242
243 /// Get whether the pin input level is high.
244 #[inline]
245 pub fn is_high(&self) -> bool {
246 self.pin.block().psr().read().psr(self.pin.pin_number() as usize)
247 }
248
249 /// Get whether the pin input level is low.
250 #[inline]
251 pub fn is_low(&self) -> bool {
252 !self.is_high()
253 }
254
255 /// Returns current pin level
256 #[inline]
257 pub fn get_level(&self) -> Level {
258 self.is_high().into()
259 }
260
261 /// Set the output as high.
262 #[inline]
263 pub fn set_high(&mut self) {
264 self.pin.block().dr_set().write(|w| {
265 w.set_dr_set(self.pin.pin_number() as usize, true);
266 });
267 }
268
269 /// Set the output as low.
270 #[inline]
271 pub fn set_low(&mut self) {
272 self.pin.block().dr_clear().write(|w| {
273 w.set_dr_clear(self.pin.pin_number() as usize, true);
274 });
275 }
276
277 /// Toggle pin output
278 #[inline]
279 pub fn toggle(&mut self) {
280 self.pin.block().dr_toggle().write(|w| {
281 w.set_dr_toggle(self.pin.pin_number() as usize, true);
282 });
283 }
284
285 /// Set the output level.
286 #[inline]
287 pub fn set_level(&mut self, level: Level) {
288 match level {
289 Level::Low => self.set_low(),
290 Level::High => self.set_high(),
291 }
292 }
293
294 /// Get the current pin output level.
295 #[inline]
296 pub fn get_output_level(&self) -> Level {
297 self.is_set_high().into()
298 }
299
300 /// Is the output level high?
301 ///
302 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`].
303 #[inline]
304 pub fn is_set_high(&self) -> bool {
305 self.pin.block().dr().read().dr(self.pin.pin_number() as usize)
306 }
307
308 /// Is the output level low?
309 ///
310 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`].
311 #[inline]
312 pub fn is_set_low(&self) -> bool {
313 !self.is_set_high()
314 }
315
316 /// Wait until the pin is high. If it is already high, return immediately.
317 #[inline]
318 pub async fn wait_for_high(&mut self) {
319 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await
320 }
321
322 /// Wait until the pin is low. If it is already low, return immediately.
323 #[inline]
324 pub async fn wait_for_low(&mut self) {
325 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await
326 }
327
328 /// Wait for the pin to undergo a transition from low to high.
329 #[inline]
330 pub async fn wait_for_rising_edge(&mut self) {
331 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await
332 }
333
334 /// Wait for the pin to undergo a transition from high to low.
335 #[inline]
336 pub async fn wait_for_falling_edge(&mut self) {
337 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await
338 }
339
340 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
341 #[inline]
342 pub async fn wait_for_any_edge(&mut self) {
343 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await
344 }
345}
346
347impl<'d> Drop for Flex<'d> {
348 fn drop(&mut self) {
349 self.set_as_disconnected();
350 }
351}
352
353/// GPIO input driver.
354pub struct Input<'d> {
355 pin: Flex<'d>,
356}
357
358impl<'d> Input<'d> {
359 /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
360 #[inline]
361 pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
362 let mut pin = Flex::new(pin);
363 pin.set_as_input();
364 pin.set_pull(pull);
365 Self { pin }
366 }
367
368 /// Get whether the pin input level is high.
369 #[inline]
370 pub fn is_high(&self) -> bool {
371 self.pin.is_high()
372 }
373
374 /// Get whether the pin input level is low.
375 #[inline]
376 pub fn is_low(&self) -> bool {
377 self.pin.is_low()
378 }
379
380 /// Get the current pin input level.
381 #[inline]
382 pub fn get_level(&self) -> Level {
383 self.pin.get_level()
384 }
385
386 /// Wait until the pin is high. If it is already high, return immediately.
387 #[inline]
388 pub async fn wait_for_high(&mut self) {
389 self.pin.wait_for_high().await
390 }
391
392 /// Wait until the pin is low. If it is already low, return immediately.
393 #[inline]
394 pub async fn wait_for_low(&mut self) {
395 self.pin.wait_for_low().await
396 }
397
398 /// Wait for the pin to undergo a transition from low to high.
399 #[inline]
400 pub async fn wait_for_rising_edge(&mut self) {
401 self.pin.wait_for_rising_edge().await
402 }
403
404 /// Wait for the pin to undergo a transition from high to low.
405 #[inline]
406 pub async fn wait_for_falling_edge(&mut self) {
407 self.pin.wait_for_falling_edge().await
408 }
409
410 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
411 #[inline]
412 pub async fn wait_for_any_edge(&mut self) {
413 self.pin.wait_for_any_edge().await
414 }
415}
416
417/// GPIO output driver.
418///
419/// Note that pins will **return to their floating state** when `Output` is dropped.
420/// If pins should retain their state indefinitely, either keep ownership of the
421/// `Output`, or pass it to [`core::mem::forget`].
422pub struct Output<'d> {
423 pin: Flex<'d>,
424}
425
426impl<'d> Output<'d> {
427 /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
428 #[inline]
429 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
430 let mut pin = Flex::new(pin);
431 pin.set_as_output();
432 pin.set_level(initial_output);
433 Self { pin }
434 }
435
436 /// Set the output as high.
437 #[inline]
438 pub fn set_high(&mut self) {
439 self.pin.set_high();
440 }
441
442 /// Set the output as low.
443 #[inline]
444 pub fn set_low(&mut self) {
445 self.pin.set_low();
446 }
447
448 /// Set the output level.
449 #[inline]
450 pub fn set_level(&mut self, level: Level) {
451 self.pin.set_level(level)
452 }
453
454 /// Is the output pin set as high?
455 #[inline]
456 pub fn is_set_high(&self) -> bool {
457 self.pin.is_set_high()
458 }
459
460 /// Is the output pin set as low?
461 #[inline]
462 pub fn is_set_low(&self) -> bool {
463 self.pin.is_set_low()
464 }
465
466 /// What level output is set to
467 #[inline]
468 pub fn get_output_level(&self) -> Level {
469 self.pin.get_output_level()
470 }
471
472 /// Toggle pin output
473 #[inline]
474 pub fn toggle(&mut self) {
475 self.pin.toggle();
476 }
477}
478
479/// GPIO output open-drain driver.
480///
481/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
482/// If pins should retain their state indefinitely, either keep ownership of the
483/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
484pub struct OutputOpenDrain<'d> {
485 pin: Flex<'d>,
486}
487
488impl<'d> OutputOpenDrain<'d> {
489 /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
490 #[inline]
491 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
492 let mut pin = Flex::new(pin);
493 pin.set_level(initial_output);
494 pin.set_as_input_output();
495 Self { pin }
496 }
497
498 /// Get whether the pin input level is high.
499 #[inline]
500 pub fn is_high(&self) -> bool {
501 !self.pin.is_low()
502 }
503
504 /// Get whether the pin input level is low.
505 #[inline]
506 pub fn is_low(&self) -> bool {
507 self.pin.is_low()
508 }
509
510 /// Get the current pin input level.
511 #[inline]
512 pub fn get_level(&self) -> Level {
513 self.pin.get_level()
514 }
515
516 /// Set the output as high.
517 #[inline]
518 pub fn set_high(&mut self) {
519 self.pin.set_high();
520 }
521
522 /// Set the output as low.
523 #[inline]
524 pub fn set_low(&mut self) {
525 self.pin.set_low();
526 }
527
528 /// Set the output level.
529 #[inline]
530 pub fn set_level(&mut self, level: Level) {
531 self.pin.set_level(level);
532 }
533
534 /// Get whether the output level is set to high.
535 #[inline]
536 pub fn is_set_high(&self) -> bool {
537 self.pin.is_set_high()
538 }
539
540 /// Get whether the output level is set to low.
541 #[inline]
542 pub fn is_set_low(&self) -> bool {
543 self.pin.is_set_low()
544 }
545
546 /// Get the current output level.
547 #[inline]
548 pub fn get_output_level(&self) -> Level {
549 self.pin.get_output_level()
550 }
551
552 /// Toggle pin output
553 #[inline]
554 pub fn toggle(&mut self) {
555 self.pin.toggle()
556 }
557
558 /// Wait until the pin is high. If it is already high, return immediately.
559 #[inline]
560 pub async fn wait_for_high(&mut self) {
561 self.pin.wait_for_high().await
562 }
563
564 /// Wait until the pin is low. If it is already low, return immediately.
565 #[inline]
566 pub async fn wait_for_low(&mut self) {
567 self.pin.wait_for_low().await
568 }
569
570 /// Wait for the pin to undergo a transition from low to high.
571 #[inline]
572 pub async fn wait_for_rising_edge(&mut self) {
573 self.pin.wait_for_rising_edge().await
574 }
575
576 /// Wait for the pin to undergo a transition from high to low.
577 #[inline]
578 pub async fn wait_for_falling_edge(&mut self) {
579 self.pin.wait_for_falling_edge().await
580 }
581
582 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
583 #[inline]
584 pub async fn wait_for_any_edge(&mut self) {
585 self.pin.wait_for_any_edge().await
586 }
587}
588
589#[allow(private_bounds)]
590pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
591 /// Returns the pin number within a bank
592 #[inline]
593 fn pin(&self) -> u8 {
594 self.pin_number()
595 }
596
597 #[inline]
598 fn bank(&self) -> Bank {
599 self._bank()
600 }
601}
602
603/// Type-erased GPIO pin.
604pub struct AnyPin {
605 pub(crate) pin_number: u8,
606 pub(crate) bank: Bank,
607}
608
609impl AnyPin {
610 /// Unsafely create a new type-erased pin.
611 ///
612 /// # Safety
613 ///
614 /// You must ensure that you’re only using one instance of this type at a time.
615 pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> {
616 Peri::new_unchecked(Self { pin_number, bank })
617 }
618}
619
620impl_peripheral!(AnyPin);
621
622impl Pin for AnyPin {}
623impl SealedPin for AnyPin {
624 #[inline]
625 fn pin_number(&self) -> u8 {
626 self.pin_number
627 }
628
629 #[inline]
630 fn _bank(&self) -> Bank {
631 self.bank
632 }
633}
634
635// Impl details
636
637/// Mux mode for GPIO pins. This is constant across all RT1xxx parts.
638const GPIO_MUX_MODE: u8 = 0b101;
639
640// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14.
641#[cfg(gpio1)]
642static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
643#[cfg(gpio2)]
644static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
645#[cfg(gpio5)]
646static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
647
648/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
649pub(crate) trait SealedPin: Sized {
650 fn pin_number(&self) -> u8;
651
652 fn _bank(&self) -> Bank;
653
654 #[inline]
655 fn block(&self) -> Gpio {
656 match self._bank() {
657 #[cfg(gpio1)]
658 Bank::Gpio1 => pac::GPIO1,
659 #[cfg(gpio2)]
660 Bank::Gpio2 => pac::GPIO2,
661 #[cfg(gpio5)]
662 Bank::Gpio5 => pac::GPIO5,
663 }
664 }
665
666 #[inline]
667 fn mux(&self) -> Reg<MuxCtl, RW> {
668 // SAFETY: The generated mux address table is valid since it is generated from the SVD files.
669 let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() };
670
671 // SAFETY: The register at the address is an instance of MuxCtl.
672 unsafe { Reg::from_ptr(address as *mut _) }
673 }
674
675 #[inline]
676 fn pad(&self) -> Reg<Ctl, RW> {
677 // SAFETY: The generated pad address table is valid since it is generated from the SVD files.
678 let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() };
679
680 // SAFETY: The register at the address is an instance of Ctl.
681 unsafe { Reg::from_ptr(address as *mut _) }
682 }
683
684 fn waker(&self) -> &AtomicWaker {
685 match self._bank() {
686 #[cfg(gpio1)]
687 Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize],
688 #[cfg(gpio2)]
689 Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize],
690 #[cfg(gpio5)]
691 Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize],
692 }
693 }
694}
695
696/// This enum matches the layout of Icr.
697enum InterruptConfiguration {
698 Low,
699 High,
700 RisingEdge,
701 FallingEdge,
702 AnyEdge,
703}
704
705#[must_use = "futures do nothing unless you `.await` or poll them"]
706struct InputFuture<'d> {
707 pin: Peri<'d, AnyPin>,
708}
709
710impl<'d> InputFuture<'d> {
711 fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self {
712 let block = pin.block();
713
714 let (icr, edge_sel) = match config {
715 InterruptConfiguration::Low => (Icr::LOW_LEVEL, false),
716 InterruptConfiguration::High => (Icr::HIGH_LEVEL, false),
717 InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false),
718 InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false),
719 InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true),
720 };
721
722 let index = if pin.pin_number() > 15 { 1 } else { 0 };
723
724 // Interrupt configuration performs RMW
725 critical_section::with(|_cs| {
726 // Disable interrupt so a level/edge detection change does not cause ISR to be set.
727 block.imr().modify(|w| {
728 w.set_imr(pin.pin_number() as usize, false);
729 });
730
731 block.icr(index).modify(|w| {
732 w.set_pin(pin.pin_number() as usize, icr);
733 });
734
735 block.edge_sel().modify(|w| {
736 w.set_edge_sel(pin.pin_number() as usize, edge_sel);
737 });
738
739 // Clear the previous interrupt.
740 block.isr().modify(|w| {
741 // "Status flags are cleared by writing a 1 to the corresponding bit position."
742 w.set_isr(pin.pin_number() as usize, true);
743 });
744 });
745
746 Self { pin }
747 }
748}
749
750impl<'d> Future for InputFuture<'d> {
751 type Output = ();
752
753 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
754 // We need to register/re-register the waker for each poll because any
755 // calls to wake will deregister the waker.
756 let waker = self.pin.waker();
757 waker.register(cx.waker());
758
759 // Enabling interrupt is RMW
760 critical_section::with(|_cs| {
761 self.pin.block().imr().modify(|w| {
762 w.set_imr(self.pin.pin_number() as usize, true);
763 });
764 });
765
766 let isr = self.pin.block().isr().read();
767
768 if isr.isr(self.pin.pin_number() as usize) {
769 return Poll::Ready(());
770 }
771
772 Poll::Pending
773 }
774}
775
776/// A macro to generate all GPIO pins.
777///
778/// This generates a lookup table for IOMUX register addresses.
779macro_rules! impl_gpio {
780 (
781 $($name: ident($bank: ident, $pin_number: expr);)*
782 ) => {
783 #[inline]
784 pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
785 match (bank, pin) {
786 $(
787 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name),
788 )*
789 _ => None
790 }
791 }
792
793 #[inline]
794 pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
795 match (bank, pin) {
796 $(
797 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name),
798 )*
799 _ => None
800 }
801 }
802
803 $(
804 impl_pin!($name, $bank, $pin_number);
805 )*
806 };
807}
808
809macro_rules! impl_pin {
810 ($name: ident, $bank: ident, $pin_num: expr) => {
811 impl crate::gpio::Pin for crate::peripherals::$name {}
812 impl crate::gpio::SealedPin for crate::peripherals::$name {
813 #[inline]
814 fn pin_number(&self) -> u8 {
815 $pin_num
816 }
817
818 #[inline]
819 fn _bank(&self) -> crate::gpio::Bank {
820 crate::gpio::Bank::$bank
821 }
822 }
823
824 impl From<peripherals::$name> for crate::gpio::AnyPin {
825 fn from(val: peripherals::$name) -> Self {
826 use crate::gpio::SealedPin;
827
828 Self {
829 pin_number: val.pin_number(),
830 bank: val._bank(),
831 }
832 }
833 }
834 };
835}
836
837pub(crate) fn init() {
838 #[cfg(feature = "rt")]
839 unsafe {
840 use embassy_hal_internal::interrupt::InterruptExt;
841
842 pac::Interrupt::GPIO1_COMBINED_0_15.enable();
843 pac::Interrupt::GPIO1_COMBINED_16_31.enable();
844 pac::Interrupt::GPIO2_COMBINED_0_15.enable();
845 pac::Interrupt::GPIO5_COMBINED_0_15.enable();
846 }
847}
848
849/// IRQ handler for GPIO pins.
850///
851/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt
852/// is for pins 16 through 31
853#[cfg(feature = "rt")]
854fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) {
855 use crate::BitIter;
856
857 let isr = block.isr().read().0;
858 let imr = block.imr().read().0;
859 let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF };
860 let bits = isr & imr & mask;
861
862 for bit in BitIter(bits) {
863 wakers[bit as usize].wake();
864
865 // Disable further interrupts for this pin. The input future will check ISR (which is kept
866 // until reset).
867 block.imr().modify(|w| {
868 w.set_imr(bit as usize, false);
869 });
870 }
871}
872
873#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
874#[interrupt]
875fn GPIO1_COMBINED_0_15() {
876 irq_handler(pac::GPIO1, &GPIO1_WAKERS, false);
877}
878
879#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
880#[interrupt]
881fn GPIO1_COMBINED_16_31() {
882 irq_handler(pac::GPIO1, &GPIO1_WAKERS, true);
883}
884
885#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
886#[interrupt]
887fn GPIO2_COMBINED_0_15() {
888 irq_handler(pac::GPIO2, &GPIO2_WAKERS, false);
889}
890
891#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
892#[interrupt]
893fn GPIO5_COMBINED_0_15() {
894 irq_handler(pac::GPIO5, &GPIO5_WAKERS, false);
895}
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index 1abaca708..a715770c4 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -1,12 +1,19 @@
1#![no_std] 1#![no_std]
2 2
3pub mod fmt; 3// This mod MUST go first, so that the others see its macros.
4pub(crate) mod fmt;
5
4pub mod gpio; 6pub mod gpio;
5#[cfg(feature = "lpc55")] 7#[cfg(feature = "lpc55")]
6pub mod pint; 8pub mod pint;
7 9
10#[cfg(feature = "_time_driver")]
11#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")]
12mod time_driver;
13
8// This mod MUST go last, so that it sees all the `impl_foo!` macros 14// This mod MUST go last, so that it sees all the `impl_foo!` macros
9#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] 15#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")]
16#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")]
10mod chip; 17mod chip;
11 18
12#[cfg(feature = "unstable-pac")] 19#[cfg(feature = "unstable-pac")]
@@ -22,13 +29,66 @@ pub use embassy_hal_internal::{Peri, PeripheralType};
22/// 29///
23/// This should only be called once and at startup, otherwise it panics. 30/// This should only be called once and at startup, otherwise it panics.
24pub fn init(_config: config::Config) -> Peripherals { 31pub fn init(_config: config::Config) -> Peripherals {
25 #[cfg(feature = "lpc55")] 32 // Do this first, so that it panics if user is calling `init` a second time
33 // before doing anything important.
34 let peripherals = Peripherals::take();
35
36 #[cfg(feature = "mimxrt1011")]
26 { 37 {
27 gpio::init(); 38 // The RT1010 Reference manual states that core clock root must be switched before
28 pint::init(); 39 // reprogramming PLL2.
40 pac::CCM.cbcdr().modify(|w| {
41 w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1);
42 });
43
44 while matches!(
45 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
46 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
47 ) {}
48
49 info!("Core clock root switched");
50
51 // 480 * 18 / 24 = 360
52 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12));
53
54 //480*18/24(pfd0)/4
55 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24));
56 pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into()));
57
58 // CPU Core
59 pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18));
60 cortex_m::asm::delay(500_000);
61
62 // Clock core clock with PLL 2.
63 pac::CCM
64 .cbcdr()
65 .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false
66
67 while matches!(
68 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
69 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
70 ) {}
71
72 pac::CCM
73 .cbcmr()
74 .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0));
75
76 // TODO: Some for USB PLLs
77
78 // DCDC clock?
79 pac::CCM.ccgr6().modify(|v| v.set_cg0(1));
29 } 80 }
30 81
31 crate::Peripherals::take() 82 #[cfg(any(feature = "lpc55", rt1xxx))]
83 gpio::init();
84
85 #[cfg(feature = "lpc55")]
86 pint::init();
87
88 #[cfg(feature = "_time_driver")]
89 time_driver::init();
90
91 peripherals
32} 92}
33 93
34/// HAL configuration for the NXP board. 94/// HAL configuration for the NXP board.
@@ -36,3 +96,20 @@ pub mod config {
36 #[derive(Default)] 96 #[derive(Default)]
37 pub struct Config {} 97 pub struct Config {}
38} 98}
99
100#[allow(unused)]
101struct BitIter(u32);
102
103impl Iterator for BitIter {
104 type Item = u32;
105
106 fn next(&mut self) -> Option<Self::Item> {
107 match self.0.trailing_zeros() {
108 32 => None,
109 b => {
110 self.0 &= !(1 << b);
111 Some(b)
112 }
113 }
114 }
115}
diff --git a/embassy-nxp/src/time_driver/pit.rs b/embassy-nxp/src/time_driver/pit.rs
new file mode 100644
index 000000000..985e5e815
--- /dev/null
+++ b/embassy-nxp/src/time_driver/pit.rs
@@ -0,0 +1,187 @@
1//! Time driver using Periodic Interrupt Timer (PIT)
2//!
3//! This driver is used with the iMXRT1xxx parts.
4//!
5//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer.
6//! The 64-bit timer is used to track how many ticks since boot.
7//!
8//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when
9//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to
10//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower.
11//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few
12//! microseconds late.
13//!
14//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift.
15
16use core::cell::{Cell, RefCell};
17use core::task::Waker;
18
19use critical_section::{CriticalSection, Mutex};
20use embassy_hal_internal::interrupt::InterruptExt;
21use embassy_time_driver::Driver as _;
22use embassy_time_queue_utils::Queue;
23
24use crate::pac::{self, interrupt};
25
26struct Driver {
27 alarm: Mutex<Cell<u64>>,
28 queue: Mutex<RefCell<Queue>>,
29}
30
31impl embassy_time_driver::Driver for Driver {
32 fn now(&self) -> u64 {
33 loop {
34 // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the
35 // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to
36 // thread which was preempted.
37 let hi = pac::PIT.ltmr64h().read().lth();
38 let lo = pac::PIT.ltmr64l().read().ltl();
39 let hi2 = pac::PIT.ltmr64h().read().lth();
40
41 if hi == hi2 {
42 // PIT timers always count down.
43 return u64::MAX - ((hi as u64) << 32 | (lo as u64));
44 }
45 }
46 }
47
48 fn schedule_wake(&self, at: u64, waker: &Waker) {
49 critical_section::with(|cs| {
50 let mut queue = self.queue.borrow(cs).borrow_mut();
51
52 if queue.schedule_wake(at, waker) {
53 let mut next = queue.next_expiration(self.now());
54
55 while !self.set_alarm(cs, next) {
56 next = queue.next_expiration(self.now());
57 }
58 }
59 })
60 }
61}
62
63impl Driver {
64 fn init(&'static self) {
65 // Disable PIT clock during mux configuration.
66 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00));
67
68 // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you
69 // could divide the clock root by up to 64.
70 pac::CCM.cscmr1().modify(|r| {
71 // 1 MHz
72 r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24);
73 r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1);
74 });
75
76 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11));
77
78 // Disable clock during init.
79 //
80 // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else
81 // you will need to recover the device with boot mode switches when using any PIT registers.
82 pac::PIT.mcr().modify(|w| {
83 w.set_mdis(true);
84 });
85
86 pac::PIT.timer(0).ldval().write_value(u32::MAX);
87 pac::PIT.timer(1).ldval().write_value(u32::MAX);
88 pac::PIT.timer(2).ldval().write_value(0);
89 pac::PIT.timer(3).ldval().write_value(0);
90
91 pac::PIT.timer(1).tctrl().write(|w| {
92 // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer.
93 w.set_chn(true);
94 w.set_ten(true);
95 w.set_tie(false);
96 });
97
98 pac::PIT.timer(0).tctrl().write(|w| {
99 w.set_chn(false);
100 w.set_ten(true);
101 w.set_tie(false);
102 });
103
104 pac::PIT.timer(2).tctrl().write(|w| {
105 w.set_tie(true);
106 });
107
108 unsafe { interrupt::PIT.enable() };
109
110 pac::PIT.mcr().write(|w| {
111 w.set_mdis(false);
112 });
113 }
114
115 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
116 let alarm = self.alarm.borrow(cs);
117 alarm.set(timestamp);
118
119 let timer = pac::PIT.timer(2);
120 let now = self.now();
121
122 if timestamp <= now {
123 alarm.set(u64::MAX);
124
125 return false;
126 }
127
128 timer.tctrl().modify(|x| x.set_ten(false));
129 timer.tflg().modify(|x| x.set_tif(true));
130
131 // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later.
132 timer.ldval().write_value((timestamp - now) as u32);
133 timer.tctrl().modify(|x| x.set_ten(true));
134
135 true
136 }
137
138 fn trigger_alarm(&self, cs: CriticalSection) {
139 let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
140
141 while !self.set_alarm(cs, next) {
142 next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
143 }
144 }
145
146 fn on_interrupt(&self) {
147 critical_section::with(|cs| {
148 let timer = pac::PIT.timer(2);
149 let alarm = self.alarm.borrow(cs);
150 let interrupted = timer.tflg().read().tif();
151 timer.tflg().write(|r| r.set_tif(true));
152
153 if interrupted {
154 // A new load value will not apply until the next timer expiration.
155 //
156 // The expiry may be up to u32::MAX cycles away, so the timer must be restarted.
157 timer.tctrl().modify(|r| r.set_ten(false));
158
159 let now = self.now();
160 let timestamp = alarm.get();
161
162 if timestamp <= now {
163 self.trigger_alarm(cs);
164 } else {
165 // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm.
166 timer.ldval().write_value((timestamp - now) as u32);
167 timer.tctrl().modify(|r| r.set_ten(true));
168 }
169 }
170 });
171 }
172}
173
174embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver {
175 alarm: Mutex::new(Cell::new(0)),
176 queue: Mutex::new(RefCell::new(Queue::new()))
177});
178
179pub(crate) fn init() {
180 DRIVER.init();
181}
182
183#[cfg(feature = "rt")]
184#[interrupt]
185fn PIT() {
186 DRIVER.on_interrupt();
187}