From 6d79c4c81187d5f2704e2d2f72a3deba05ca449a Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 2 Jul 2025 15:24:01 +0300 Subject: feat: added log-to-defmt feature Signed-off-by: Roy --- embassy-nxp/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'embassy-nxp/src') diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 433aca9e0..79c66e7f6 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -3,7 +3,6 @@ pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; - // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] mod chip; @@ -25,8 +24,10 @@ pub fn init(_config: config::Config) -> Peripherals { { gpio::init(); pint::init(); + #[cfg(feature = "log-to-defmt")] + log_to_defmt::setup(); + log::info!("Initialization complete"); } - crate::Peripherals::take() } -- cgit From 0fc1ab290fed27301b455a039c2ae9c16ce7c30c Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 21 Jul 2025 10:47:21 +0300 Subject: Revert "feat: added log-to-defmt feature" This reverts commit 6d79c4c81187d5f2704e2d2f72a3deba05ca449a. --- embassy-nxp/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'embassy-nxp/src') diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 79c66e7f6..433aca9e0 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -3,6 +3,7 @@ pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; + // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] mod chip; @@ -24,10 +25,8 @@ pub fn init(_config: config::Config) -> Peripherals { { gpio::init(); pint::init(); - #[cfg(feature = "log-to-defmt")] - log_to_defmt::setup(); - log::info!("Initialization complete"); } + crate::Peripherals::take() } -- cgit From 2a696579275a035ab56023313284f8a66ef37a45 Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 21 Jul 2025 12:49:17 +0300 Subject: feat: fmt.rs was added --- embassy-nxp/src/fmt.rs | 284 ++++++++++++++++++++++++++++++++++++++++++ embassy-nxp/src/gpio/lpc55.rs | 1 + embassy-nxp/src/lib.rs | 1 + embassy-nxp/src/pint.rs | 2 + 4 files changed, 288 insertions(+) create mode 100644 embassy-nxp/src/fmt.rs (limited to 'embassy-nxp/src') diff --git a/embassy-nxp/src/fmt.rs b/embassy-nxp/src/fmt.rs new file mode 100644 index 000000000..27d41ace6 --- /dev/null +++ b/embassy-nxp/src/fmt.rs @@ -0,0 +1,284 @@ +//! Copied from embassy-rp + +#![macro_use] +#![allow(unused)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +#[collapse_debuginfo(yes)] +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! unimplemented { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unimplemented!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unimplemented!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +#[collapse_debuginfo(yes)] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +#[collapse_debuginfo(yes)] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 94cd8b7f8..8f407bb3a 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -7,6 +7,7 @@ pub(crate) fn init() { syscon_reg() .ahbclkctrl0 .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); + info!("GPIO initialized"); } /// The GPIO pin level for pins set on "Digital" mode. diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 433aca9e0..1abaca708 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] +pub mod fmt; pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index dc117e7e3..ff414b4e6 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -101,6 +101,8 @@ pub(crate) fn init() { crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); }; + + info!("Pin interrupts initialized"); } #[must_use = "futures do nothing unless you `.await` or poll them"] -- cgit From 1ad5d5a771d5109a763361454fb724b85ae25fdd Mon Sep 17 00:00:00 2001 From: i509VCB Date: Wed, 9 Jul 2025 23:08:59 -0500 Subject: nxp: Add MIMXRT1011 GPIO and time driver PIT is used for the time driver --- embassy-nxp/src/chips/mimxrt1011.rs | 113 +++++ embassy-nxp/src/gpio.rs | 2 + embassy-nxp/src/gpio/rt1xxx.rs | 895 ++++++++++++++++++++++++++++++++++++ embassy-nxp/src/lib.rs | 87 +++- embassy-nxp/src/time_driver/pit.rs | 187 ++++++++ 5 files changed, 1279 insertions(+), 5 deletions(-) create mode 100644 embassy-nxp/src/chips/mimxrt1011.rs create mode 100644 embassy-nxp/src/gpio/rt1xxx.rs create mode 100644 embassy-nxp/src/time_driver/pit.rs (limited to 'embassy-nxp/src') 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 @@ +// This must be imported so that __preinit is defined. +use imxrt_rt as _; +pub use nxp_pac as pac; + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + GPIO_00, + GPIO_01, + GPIO_02, + GPIO_03, + GPIO_04, + GPIO_05, + GPIO_06, + GPIO_07, + GPIO_08, + GPIO_09, + GPIO_10, + GPIO_11, + GPIO_12, + GPIO_13, + GPIO_AD_00, + GPIO_AD_01, + GPIO_AD_02, + GPIO_AD_03, + GPIO_AD_04, + GPIO_AD_05, + GPIO_AD_06, + GPIO_AD_07, + GPIO_AD_08, + GPIO_AD_09, + GPIO_AD_10, + GPIO_AD_11, + GPIO_AD_12, + GPIO_AD_13, + GPIO_AD_14, + GPIO_SD_00, + GPIO_SD_01, + GPIO_SD_02, + GPIO_SD_03, + GPIO_SD_04, + GPIO_SD_05, + GPIO_SD_06, + GPIO_SD_07, + GPIO_SD_08, + GPIO_SD_09, + GPIO_SD_10, + GPIO_SD_11, + GPIO_SD_12, + GPIO_SD_13, + PMIC_ON_REQ, +} + +impl_gpio! { + // GPIO Bank 1 + GPIO_00(Gpio1, 0); + GPIO_01(Gpio1, 1); + GPIO_02(Gpio1, 2); + GPIO_03(Gpio1, 3); + GPIO_04(Gpio1, 4); + GPIO_05(Gpio1, 5); + GPIO_06(Gpio1, 6); + GPIO_07(Gpio1, 7); + GPIO_08(Gpio1, 8); + GPIO_09(Gpio1, 9); + GPIO_10(Gpio1, 10); + GPIO_11(Gpio1, 11); + GPIO_12(Gpio1, 12); + GPIO_13(Gpio1, 13); + GPIO_AD_00(Gpio1, 14); + GPIO_AD_01(Gpio1, 15); + GPIO_AD_02(Gpio1, 16); + GPIO_AD_03(Gpio1, 17); + GPIO_AD_04(Gpio1, 18); + GPIO_AD_05(Gpio1, 19); + GPIO_AD_06(Gpio1, 20); + GPIO_AD_07(Gpio1, 21); + GPIO_AD_08(Gpio1, 22); + GPIO_AD_09(Gpio1, 23); + GPIO_AD_10(Gpio1, 24); + GPIO_AD_11(Gpio1, 25); + GPIO_AD_12(Gpio1, 26); + GPIO_AD_13(Gpio1, 27); + GPIO_AD_14(Gpio1, 28); + + // GPIO Bank 2 + GPIO_SD_00(Gpio2, 0); + GPIO_SD_01(Gpio2, 1); + GPIO_SD_02(Gpio2, 2); + GPIO_SD_03(Gpio2, 3); + GPIO_SD_04(Gpio2, 4); + GPIO_SD_05(Gpio2, 5); + GPIO_SD_06(Gpio2, 6); + GPIO_SD_07(Gpio2, 7); + GPIO_SD_08(Gpio2, 8); + GPIO_SD_09(Gpio2, 9); + GPIO_SD_10(Gpio2, 10); + GPIO_SD_11(Gpio2, 11); + GPIO_SD_12(Gpio2, 12); + GPIO_SD_13(Gpio2, 13); + + // GPIO Bank 5 + PMIC_ON_REQ(Gpio5, 0); +} + +pub(crate) mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + #![allow(missing_docs)] + + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); +} 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 @@ //! General purpose input/output (GPIO) driver. +#![macro_use] #[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] +#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] mod inner; pub 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 @@ +#![macro_use] + +use core::future::Future; +use core::ops::Not; +use core::pin::Pin as FuturePin; +use core::task::{Context, Poll}; + +use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +use nxp_pac::gpio::vals::Icr; +use nxp_pac::iomuxc::vals::Pus; + +use crate::chip::{mux_address, pad_address}; +use crate::pac::common::{Reg, RW}; +#[cfg(feature = "rt")] +use crate::pac::interrupt; +use crate::pac::iomuxc::regs::{Ctl, MuxCtl}; +#[cfg(gpio5)] +use crate::pac::{self, gpio::Gpio}; + +/// The GPIO pin level for pins set on "Digital" mode. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Level { + /// Logical low. Corresponds to 0V. + Low, + /// Logical high. Corresponds to VDD. + High, +} + +impl From for Level { + fn from(val: bool) -> Self { + match val { + true => Self::High, + false => Self::Low, + } + } +} + +impl From for bool { + fn from(level: Level) -> bool { + match level { + Level::Low => false, + Level::High => true, + } + } +} + +impl Not for Level { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Level::Low => Level::High, + Level::High => Level::Low, + } + } +} + +/// Pull setting for a GPIO input set on "Digital" mode. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Pull { + /// No pull. + None, + + // TODO: What Does PUE::KEEPER mean here? + + // 22 kOhm pull-up resistor. + Up22K, + + // 47 kOhm pull-up resistor. + Up47K, + + // 100 kOhm pull-up resistor. + Up100K, + + // 100 kOhm pull-down resistor. + Down100K, +} + +/// Drive strength of an output +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Drive { + Disabled, + _150R, + _75R, + _50R, + _37R, + _30R, + _25R, + _20R, +} + +/// Slew rate of an output +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SlewRate { + Slow, + + Fast, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Bank { + /// Bank 1 + #[cfg(gpio1)] + Gpio1, + + /// Bank 2 + #[cfg(gpio2)] + Gpio2, + + /// Bank 5 + #[cfg(gpio5)] + Gpio5, +} + +/// GPIO flexible pin. +/// +/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain +/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output +/// mode. +pub struct Flex<'d> { + pub(crate) pin: Peri<'d, AnyPin>, +} + +impl<'d> Flex<'d> { + /// Wrap the pin in a `Flex`. + /// + /// The pin remains disconnected. The initial output level is unspecified, but can be changed + /// before the pin is put into output mode. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>) -> Self { + Self { pin: pin.into() } + } + + /// Set the pin's pull. + #[inline] + pub fn set_pull(&mut self, pull: Pull) { + let (pke, pue, pus) = match pull { + Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN), + Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP), + Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP), + Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP), + Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN), + }; + + self.pin.pad().modify(|w| { + w.set_pke(pke); + w.set_pue(pue); + w.set_pus(pus); + }); + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slewrate(&mut self, rate: SlewRate) { + self.pin.pad().modify(|w| { + w.set_sre(match rate { + SlewRate::Slow => false, + SlewRate::Fast => true, + }); + }); + } + + /// Set the pin's Schmitt trigger. + #[inline] + pub fn set_schmitt(&mut self, enable: bool) { + self.pin.pad().modify(|w| { + w.set_hys(enable); + }); + } + + /// Put the pin into input mode. + /// + /// The pull setting is left unchanged. + #[inline] + pub fn set_as_input(&mut self) { + self.pin.mux().modify(|w| { + w.set_mux_mode(GPIO_MUX_MODE); + }); + + // Setting direction is RMW + critical_section::with(|_cs| { + self.pin.block().gdir().modify(|w| { + w.set_gdir(self.pin.pin_number() as usize, false); + }); + }) + } + + /// Put the pin into output mode. + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + #[inline] + pub fn set_as_output(&mut self) { + self.pin.mux().modify(|w| { + w.set_mux_mode(GPIO_MUX_MODE); + }); + + // Setting direction is RMW + critical_section::with(|_cs| { + self.pin.block().gdir().modify(|w| { + w.set_gdir(self.pin.pin_number() as usize, true); + }); + }) + } + + /// Put the pin into input + open-drain output mode. + /// + /// The hardware will drive the line low if you set it to low, and will leave it floating if you set + /// it to high, in which case you can read the input to figure out whether another device + /// is driving the line low. + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + /// + /// The internal weak pull-up and pull-down resistors will be disabled. + #[inline] + pub fn set_as_input_output(&mut self) { + self.pin.pad().modify(|w| { + w.set_ode(true); + }); + } + + /// Set the pin as "disconnected", ie doing nothing and consuming the lowest + /// amount of power possible. + /// + /// This is currently the same as [`Self::set_as_analog()`] but is semantically different + /// really. Drivers should `set_as_disconnected()` pins when dropped. + /// + /// Note that this also disables the pull-up and pull-down resistors. + #[inline] + pub fn set_as_disconnected(&mut self) { + self.pin.pad().modify(|w| { + w.set_ode(false); + w.set_pke(false); + w.set_pue(false); + w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN); + }); + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.block().psr().read().psr(self.pin.pin_number() as usize) + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + !self.is_high() + } + + /// Returns current pin level + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.block().dr_set().write(|w| { + w.set_dr_set(self.pin.pin_number() as usize, true); + }); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.block().dr_clear().write(|w| { + w.set_dr_clear(self.pin.pin_number() as usize, true); + }); + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.block().dr_toggle().write(|w| { + w.set_dr_toggle(self.pin.pin_number() as usize, true); + }); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + match level { + Level::Low => self.set_low(), + Level::High => self.set_high(), + } + } + + /// Get the current pin output level. + #[inline] + pub fn get_output_level(&self) -> Level { + self.is_set_high().into() + } + + /// Is the output level high? + /// + /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`]. + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.block().dr().read().dr(self.pin.pin_number() as usize) + } + + /// Is the output level low? + /// + /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`]. + #[inline] + pub fn is_set_low(&self) -> bool { + !self.is_set_high() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await + } +} + +impl<'d> Drop for Flex<'d> { + fn drop(&mut self) { + self.set_as_disconnected(); + } +} + +/// GPIO input driver. +pub struct Input<'d> { + pin: Flex<'d>, +} + +impl<'d> Input<'d> { + /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_input(); + pin.set_pull(pull); + Self { pin } + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.pin.get_level() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + self.pin.wait_for_high().await + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + self.pin.wait_for_low().await + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + self.pin.wait_for_rising_edge().await + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + self.pin.wait_for_falling_edge().await + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await + } +} + +/// GPIO output driver. +/// +/// Note that pins will **return to their floating state** when `Output` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `Output`, or pass it to [`core::mem::forget`]. +pub struct Output<'d> { + pin: Flex<'d>, +} + +impl<'d> Output<'d> { + /// Create GPIO output driver for a [Pin] with the provided [Level] configuration. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_output(); + pin.set_level(initial_output); + Self { pin } + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_high(); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_low(); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_level(level) + } + + /// Is the output pin set as high? + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Is the output pin set as low? + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } + + /// What level output is set to + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.get_output_level() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle(); + } +} + +/// GPIO output open-drain driver. +/// +/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `OutputOpenDrain`, or pass it to [`core::mem::forget`]. +pub struct OutputOpenDrain<'d> { + pin: Flex<'d>, +} + +impl<'d> OutputOpenDrain<'d> { + /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level]. + #[inline] + pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_level(initial_output); + pin.set_as_input_output(); + Self { pin } + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + !self.pin.is_low() + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.pin.get_level() + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_high(); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_low(); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_level(level); + } + + /// Get whether the output level is set to high. + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Get whether the output level is set to low. + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } + + /// Get the current output level. + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.get_output_level() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + self.pin.wait_for_high().await + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + self.pin.wait_for_low().await + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + self.pin.wait_for_rising_edge().await + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + self.pin.wait_for_falling_edge().await + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await + } +} + +#[allow(private_bounds)] +pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self.pin_number() + } + + #[inline] + fn bank(&self) -> Bank { + self._bank() + } +} + +/// Type-erased GPIO pin. +pub struct AnyPin { + pub(crate) pin_number: u8, + pub(crate) bank: Bank, +} + +impl AnyPin { + /// Unsafely create a new type-erased pin. + /// + /// # Safety + /// + /// You must ensure that you’re only using one instance of this type at a time. + pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> { + Peri::new_unchecked(Self { pin_number, bank }) + } +} + +impl_peripheral!(AnyPin); + +impl Pin for AnyPin {} +impl SealedPin for AnyPin { + #[inline] + fn pin_number(&self) -> u8 { + self.pin_number + } + + #[inline] + fn _bank(&self) -> Bank { + self.bank + } +} + +// Impl details + +/// Mux mode for GPIO pins. This is constant across all RT1xxx parts. +const GPIO_MUX_MODE: u8 = 0b101; + +// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14. +#[cfg(gpio1)] +static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio2)] +static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio5)] +static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; + +/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. +pub(crate) trait SealedPin: Sized { + fn pin_number(&self) -> u8; + + fn _bank(&self) -> Bank; + + #[inline] + fn block(&self) -> Gpio { + match self._bank() { + #[cfg(gpio1)] + Bank::Gpio1 => pac::GPIO1, + #[cfg(gpio2)] + Bank::Gpio2 => pac::GPIO2, + #[cfg(gpio5)] + Bank::Gpio5 => pac::GPIO5, + } + } + + #[inline] + fn mux(&self) -> Reg { + // SAFETY: The generated mux address table is valid since it is generated from the SVD files. + let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + + // SAFETY: The register at the address is an instance of MuxCtl. + unsafe { Reg::from_ptr(address as *mut _) } + } + + #[inline] + fn pad(&self) -> Reg { + // SAFETY: The generated pad address table is valid since it is generated from the SVD files. + let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + + // SAFETY: The register at the address is an instance of Ctl. + unsafe { Reg::from_ptr(address as *mut _) } + } + + fn waker(&self) -> &AtomicWaker { + match self._bank() { + #[cfg(gpio1)] + Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize], + #[cfg(gpio2)] + Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize], + #[cfg(gpio5)] + Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], + } + } +} + +/// This enum matches the layout of Icr. +enum InterruptConfiguration { + Low, + High, + RisingEdge, + FallingEdge, + AnyEdge, +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputFuture<'d> { + pin: Peri<'d, AnyPin>, +} + +impl<'d> InputFuture<'d> { + fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self { + let block = pin.block(); + + let (icr, edge_sel) = match config { + InterruptConfiguration::Low => (Icr::LOW_LEVEL, false), + InterruptConfiguration::High => (Icr::HIGH_LEVEL, false), + InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false), + InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false), + InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true), + }; + + let index = if pin.pin_number() > 15 { 1 } else { 0 }; + + // Interrupt configuration performs RMW + critical_section::with(|_cs| { + // Disable interrupt so a level/edge detection change does not cause ISR to be set. + block.imr().modify(|w| { + w.set_imr(pin.pin_number() as usize, false); + }); + + block.icr(index).modify(|w| { + w.set_pin(pin.pin_number() as usize, icr); + }); + + block.edge_sel().modify(|w| { + w.set_edge_sel(pin.pin_number() as usize, edge_sel); + }); + + // Clear the previous interrupt. + block.isr().modify(|w| { + // "Status flags are cleared by writing a 1 to the corresponding bit position." + w.set_isr(pin.pin_number() as usize, true); + }); + }); + + Self { pin } + } +} + +impl<'d> Future for InputFuture<'d> { + type Output = (); + + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // We need to register/re-register the waker for each poll because any + // calls to wake will deregister the waker. + let waker = self.pin.waker(); + waker.register(cx.waker()); + + // Enabling interrupt is RMW + critical_section::with(|_cs| { + self.pin.block().imr().modify(|w| { + w.set_imr(self.pin.pin_number() as usize, true); + }); + }); + + let isr = self.pin.block().isr().read(); + + if isr.isr(self.pin.pin_number() as usize) { + return Poll::Ready(()); + } + + Poll::Pending + } +} + +/// A macro to generate all GPIO pins. +/// +/// This generates a lookup table for IOMUX register addresses. +macro_rules! impl_gpio { + ( + $($name: ident($bank: ident, $pin_number: expr);)* + ) => { + #[inline] + pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option { + match (bank, pin) { + $( + (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name), + )* + _ => None + } + } + + #[inline] + pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option { + match (bank, pin) { + $( + (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name), + )* + _ => None + } + } + + $( + impl_pin!($name, $bank, $pin_number); + )* + }; +} + +macro_rules! impl_pin { + ($name: ident, $bank: ident, $pin_num: expr) => { + impl crate::gpio::Pin for crate::peripherals::$name {} + impl crate::gpio::SealedPin for crate::peripherals::$name { + #[inline] + fn pin_number(&self) -> u8 { + $pin_num + } + + #[inline] + fn _bank(&self) -> crate::gpio::Bank { + crate::gpio::Bank::$bank + } + } + + impl From for crate::gpio::AnyPin { + fn from(val: peripherals::$name) -> Self { + use crate::gpio::SealedPin; + + Self { + pin_number: val.pin_number(), + bank: val._bank(), + } + } + } + }; +} + +pub(crate) fn init() { + #[cfg(feature = "rt")] + unsafe { + use embassy_hal_internal::interrupt::InterruptExt; + + pac::Interrupt::GPIO1_COMBINED_0_15.enable(); + pac::Interrupt::GPIO1_COMBINED_16_31.enable(); + pac::Interrupt::GPIO2_COMBINED_0_15.enable(); + pac::Interrupt::GPIO5_COMBINED_0_15.enable(); + } +} + +/// IRQ handler for GPIO pins. +/// +/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt +/// is for pins 16 through 31 +#[cfg(feature = "rt")] +fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) { + use crate::BitIter; + + let isr = block.isr().read().0; + let imr = block.imr().read().0; + let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF }; + let bits = isr & imr & mask; + + for bit in BitIter(bits) { + wakers[bit as usize].wake(); + + // Disable further interrupts for this pin. The input future will check ISR (which is kept + // until reset). + block.imr().modify(|w| { + w.set_imr(bit as usize, false); + }); + } +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO1_COMBINED_0_15() { + irq_handler(pac::GPIO1, &GPIO1_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO1_COMBINED_16_31() { + irq_handler(pac::GPIO1, &GPIO1_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO2_COMBINED_0_15() { + irq_handler(pac::GPIO2, &GPIO2_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[interrupt] +fn GPIO5_COMBINED_0_15() { + irq_handler(pac::GPIO5, &GPIO5_WAKERS, false); +} 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 @@ #![no_std] -pub mod fmt; +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + pub mod gpio; #[cfg(feature = "lpc55")] pub mod pint; +#[cfg(feature = "_time_driver")] +#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] +mod time_driver; + // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] +#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] mod chip; #[cfg(feature = "unstable-pac")] @@ -22,13 +29,66 @@ pub use embassy_hal_internal::{Peri, PeripheralType}; /// /// This should only be called once and at startup, otherwise it panics. pub fn init(_config: config::Config) -> Peripherals { - #[cfg(feature = "lpc55")] + // Do this first, so that it panics if user is calling `init` a second time + // before doing anything important. + let peripherals = Peripherals::take(); + + #[cfg(feature = "mimxrt1011")] { - gpio::init(); - pint::init(); + // The RT1010 Reference manual states that core clock root must be switched before + // reprogramming PLL2. + pac::CCM.cbcdr().modify(|w| { + w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1); + }); + + while matches!( + pac::CCM.cdhipr().read().periph_clk_sel_busy(), + pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 + ) {} + + info!("Core clock root switched"); + + // 480 * 18 / 24 = 360 + pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12)); + + //480*18/24(pfd0)/4 + pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24)); + pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into())); + + // CPU Core + pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18)); + cortex_m::asm::delay(500_000); + + // Clock core clock with PLL 2. + pac::CCM + .cbcdr() + .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false + + while matches!( + pac::CCM.cdhipr().read().periph_clk_sel_busy(), + pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 + ) {} + + pac::CCM + .cbcmr() + .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0)); + + // TODO: Some for USB PLLs + + // DCDC clock? + pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); } - crate::Peripherals::take() + #[cfg(any(feature = "lpc55", rt1xxx))] + gpio::init(); + + #[cfg(feature = "lpc55")] + pint::init(); + + #[cfg(feature = "_time_driver")] + time_driver::init(); + + peripherals } /// HAL configuration for the NXP board. @@ -36,3 +96,20 @@ pub mod config { #[derive(Default)] pub struct Config {} } + +#[allow(unused)] +struct BitIter(u32); + +impl Iterator for BitIter { + type Item = u32; + + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) + } + } + } +} 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 @@ +//! Time driver using Periodic Interrupt Timer (PIT) +//! +//! This driver is used with the iMXRT1xxx parts. +//! +//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer. +//! The 64-bit timer is used to track how many ticks since boot. +//! +//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when +//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to +//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower. +//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few +//! microseconds late. +//! +//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift. + +use core::cell::{Cell, RefCell}; +use core::task::Waker; + +use critical_section::{CriticalSection, Mutex}; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_time_driver::Driver as _; +use embassy_time_queue_utils::Queue; + +use crate::pac::{self, interrupt}; + +struct Driver { + alarm: Mutex>, + queue: Mutex>, +} + +impl embassy_time_driver::Driver for Driver { + fn now(&self) -> u64 { + loop { + // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the + // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to + // thread which was preempted. + let hi = pac::PIT.ltmr64h().read().lth(); + let lo = pac::PIT.ltmr64l().read().ltl(); + let hi2 = pac::PIT.ltmr64h().read().lth(); + + if hi == hi2 { + // PIT timers always count down. + return u64::MAX - ((hi as u64) << 32 | (lo as u64)); + } + } + } + + fn schedule_wake(&self, at: u64, waker: &Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +} + +impl Driver { + fn init(&'static self) { + // Disable PIT clock during mux configuration. + pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00)); + + // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you + // could divide the clock root by up to 64. + pac::CCM.cscmr1().modify(|r| { + // 1 MHz + r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24); + r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1); + }); + + pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11)); + + // Disable clock during init. + // + // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else + // you will need to recover the device with boot mode switches when using any PIT registers. + pac::PIT.mcr().modify(|w| { + w.set_mdis(true); + }); + + pac::PIT.timer(0).ldval().write_value(u32::MAX); + pac::PIT.timer(1).ldval().write_value(u32::MAX); + pac::PIT.timer(2).ldval().write_value(0); + pac::PIT.timer(3).ldval().write_value(0); + + pac::PIT.timer(1).tctrl().write(|w| { + // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer. + w.set_chn(true); + w.set_ten(true); + w.set_tie(false); + }); + + pac::PIT.timer(0).tctrl().write(|w| { + w.set_chn(false); + w.set_ten(true); + w.set_tie(false); + }); + + pac::PIT.timer(2).tctrl().write(|w| { + w.set_tie(true); + }); + + unsafe { interrupt::PIT.enable() }; + + pac::PIT.mcr().write(|w| { + w.set_mdis(false); + }); + } + + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let alarm = self.alarm.borrow(cs); + alarm.set(timestamp); + + let timer = pac::PIT.timer(2); + let now = self.now(); + + if timestamp <= now { + alarm.set(u64::MAX); + + return false; + } + + timer.tctrl().modify(|x| x.set_ten(false)); + timer.tflg().modify(|x| x.set_tif(true)); + + // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later. + timer.ldval().write_value((timestamp - now) as u32); + timer.tctrl().modify(|x| x.set_ten(true)); + + true + } + + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + + while !self.set_alarm(cs, next) { + next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + } + } + + fn on_interrupt(&self) { + critical_section::with(|cs| { + let timer = pac::PIT.timer(2); + let alarm = self.alarm.borrow(cs); + let interrupted = timer.tflg().read().tif(); + timer.tflg().write(|r| r.set_tif(true)); + + if interrupted { + // A new load value will not apply until the next timer expiration. + // + // The expiry may be up to u32::MAX cycles away, so the timer must be restarted. + timer.tctrl().modify(|r| r.set_ten(false)); + + let now = self.now(); + let timestamp = alarm.get(); + + if timestamp <= now { + self.trigger_alarm(cs); + } else { + // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm. + timer.ldval().write_value((timestamp - now) as u32); + timer.tctrl().modify(|r| r.set_ten(true)); + } + } + }); + } +} + +embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver { + alarm: Mutex::new(Cell::new(0)), + queue: Mutex::new(RefCell::new(Queue::new())) +}); + +pub(crate) fn init() { + DRIVER.init(); +} + +#[cfg(feature = "rt")] +#[interrupt] +fn PIT() { + DRIVER.on_interrupt(); +} -- cgit From 1d46f55bddf402c33143959e1ad26af59bb15855 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Mon, 21 Jul 2025 20:29:34 -0500 Subject: nxp: Add mimxrt1062 support The examples in this case are designed for the IMXRT1060-EVK. The same chip is used in the Teensy 4.0/1, but that will probably get its own set of examples due to some differences such as the FCB. --- embassy-nxp/src/chips/mimxrt1062.rs | 282 ++++++++++++++++++++++++++++++++++++ embassy-nxp/src/gpio/rt1xxx.rs | 62 +++++++- embassy-nxp/src/lib.rs | 1 + 3 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 embassy-nxp/src/chips/mimxrt1062.rs (limited to 'embassy-nxp/src') diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs new file mode 100644 index 000000000..ef153bd66 --- /dev/null +++ b/embassy-nxp/src/chips/mimxrt1062.rs @@ -0,0 +1,282 @@ +// This must be imported so that __preinit is defined. +use imxrt_rt as _; +pub use nxp_pac as pac; + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + GPIO_AD_B0_00, + GPIO_AD_B0_01, + GPIO_AD_B0_02, + GPIO_AD_B0_03, + GPIO_AD_B0_04, + GPIO_AD_B0_05, + GPIO_AD_B0_06, + GPIO_AD_B0_07, + GPIO_AD_B0_08, + GPIO_AD_B0_09, + GPIO_AD_B0_10, + GPIO_AD_B0_11, + GPIO_AD_B0_12, + GPIO_AD_B0_13, + GPIO_AD_B0_14, + GPIO_AD_B0_15, + GPIO_AD_B1_00, + GPIO_AD_B1_01, + GPIO_AD_B1_02, + GPIO_AD_B1_03, + GPIO_AD_B1_04, + GPIO_AD_B1_05, + GPIO_AD_B1_06, + GPIO_AD_B1_07, + GPIO_AD_B1_08, + GPIO_AD_B1_09, + GPIO_AD_B1_10, + GPIO_AD_B1_11, + GPIO_AD_B1_12, + GPIO_AD_B1_13, + GPIO_AD_B1_14, + GPIO_AD_B1_15, + GPIO_B0_00, + GPIO_B0_01, + GPIO_B0_02, + GPIO_B0_03, + GPIO_B0_04, + GPIO_B0_05, + GPIO_B0_06, + GPIO_B0_07, + GPIO_B0_08, + GPIO_B0_09, + GPIO_B0_10, + GPIO_B0_11, + GPIO_B0_12, + GPIO_B0_13, + GPIO_B0_14, + GPIO_B0_15, + GPIO_B1_00, + GPIO_B1_01, + GPIO_B1_02, + GPIO_B1_03, + GPIO_B1_04, + GPIO_B1_05, + GPIO_B1_06, + GPIO_B1_07, + GPIO_B1_08, + GPIO_B1_09, + GPIO_B1_10, + GPIO_B1_11, + GPIO_B1_12, + GPIO_B1_13, + GPIO_B1_14, + GPIO_B1_15, + GPIO_EMC_00, + GPIO_EMC_01, + GPIO_EMC_02, + GPIO_EMC_03, + GPIO_EMC_04, + GPIO_EMC_05, + GPIO_EMC_06, + GPIO_EMC_07, + GPIO_EMC_08, + GPIO_EMC_09, + GPIO_EMC_10, + GPIO_EMC_11, + GPIO_EMC_12, + GPIO_EMC_13, + GPIO_EMC_14, + GPIO_EMC_15, + GPIO_EMC_16, + GPIO_EMC_17, + GPIO_EMC_18, + GPIO_EMC_19, + GPIO_EMC_20, + GPIO_EMC_21, + GPIO_EMC_22, + GPIO_EMC_23, + GPIO_EMC_24, + GPIO_EMC_25, + GPIO_EMC_26, + GPIO_EMC_27, + GPIO_EMC_28, + GPIO_EMC_29, + GPIO_EMC_30, + GPIO_EMC_31, + GPIO_EMC_32, + GPIO_EMC_33, + GPIO_EMC_34, + GPIO_EMC_35, + GPIO_EMC_36, + GPIO_EMC_37, + GPIO_EMC_38, + GPIO_EMC_39, + GPIO_EMC_40, + GPIO_EMC_41, + GPIO_SD_B0_00, + GPIO_SD_B0_01, + GPIO_SD_B0_02, + GPIO_SD_B0_03, + GPIO_SD_B0_04, + GPIO_SD_B0_05, + GPIO_SD_B1_00, + GPIO_SD_B1_01, + GPIO_SD_B1_02, + GPIO_SD_B1_03, + GPIO_SD_B1_04, + GPIO_SD_B1_05, + GPIO_SD_B1_06, + GPIO_SD_B1_07, + GPIO_SD_B1_08, + GPIO_SD_B1_09, + GPIO_SD_B1_10, + GPIO_SD_B1_11, + WAKEUP, + PMIC_ON_REQ, + PMIC_STBY_REQ, +} + +impl_gpio! { + // GPIO Bank 1 + GPIO_AD_B0_00(Gpio1, 0); + GPIO_AD_B0_01(Gpio1, 1); + GPIO_AD_B0_02(Gpio1, 2); + GPIO_AD_B0_03(Gpio1, 3); + GPIO_AD_B0_04(Gpio1, 4); + GPIO_AD_B0_05(Gpio1, 5); + GPIO_AD_B0_06(Gpio1, 6); + GPIO_AD_B0_07(Gpio1, 7); + GPIO_AD_B0_08(Gpio1, 8); + GPIO_AD_B0_09(Gpio1, 9); + GPIO_AD_B0_10(Gpio1, 10); + GPIO_AD_B0_11(Gpio1, 11); + GPIO_AD_B0_12(Gpio1, 12); + GPIO_AD_B0_13(Gpio1, 13); + GPIO_AD_B0_14(Gpio1, 14); + GPIO_AD_B0_15(Gpio1, 15); + GPIO_AD_B1_00(Gpio1, 16); + GPIO_AD_B1_01(Gpio1, 17); + GPIO_AD_B1_02(Gpio1, 18); + GPIO_AD_B1_03(Gpio1, 19); + GPIO_AD_B1_04(Gpio1, 20); + GPIO_AD_B1_05(Gpio1, 21); + GPIO_AD_B1_06(Gpio1, 22); + GPIO_AD_B1_07(Gpio1, 23); + GPIO_AD_B1_08(Gpio1, 24); + GPIO_AD_B1_09(Gpio1, 25); + GPIO_AD_B1_10(Gpio1, 26); + GPIO_AD_B1_11(Gpio1, 27); + GPIO_AD_B1_12(Gpio1, 28); + GPIO_AD_B1_13(Gpio1, 29); + GPIO_AD_B1_14(Gpio1, 30); + GPIO_AD_B1_15(Gpio1, 31); + + // GPIO Bank 2 + GPIO_B0_00(Gpio2, 0); + GPIO_B0_01(Gpio2, 1); + GPIO_B0_02(Gpio2, 2); + GPIO_B0_03(Gpio2, 3); + GPIO_B0_04(Gpio2, 4); + GPIO_B0_05(Gpio2, 5); + GPIO_B0_06(Gpio2, 6); + GPIO_B0_07(Gpio2, 7); + GPIO_B0_08(Gpio2, 8); + GPIO_B0_09(Gpio2, 9); + GPIO_B0_10(Gpio2, 10); + GPIO_B0_11(Gpio2, 11); + GPIO_B0_12(Gpio2, 12); + GPIO_B0_13(Gpio2, 13); + GPIO_B0_14(Gpio2, 14); + GPIO_B0_15(Gpio2, 15); + GPIO_B1_00(Gpio2, 16); + GPIO_B1_01(Gpio2, 17); + GPIO_B1_02(Gpio2, 18); + GPIO_B1_03(Gpio2, 19); + GPIO_B1_04(Gpio2, 20); + GPIO_B1_05(Gpio2, 21); + GPIO_B1_06(Gpio2, 22); + GPIO_B1_07(Gpio2, 23); + GPIO_B1_08(Gpio2, 24); + GPIO_B1_09(Gpio2, 25); + GPIO_B1_10(Gpio2, 26); + GPIO_B1_11(Gpio2, 27); + GPIO_B1_12(Gpio2, 28); + GPIO_B1_13(Gpio2, 29); + GPIO_B1_14(Gpio2, 30); + GPIO_B1_15(Gpio2, 31); + + // GPIO Bank 4 (EMC is 4, then 3) + GPIO_EMC_00(Gpio4, 0); + GPIO_EMC_01(Gpio4, 1); + GPIO_EMC_02(Gpio4, 2); + GPIO_EMC_03(Gpio4, 3); + GPIO_EMC_04(Gpio4, 4); + GPIO_EMC_05(Gpio4, 5); + GPIO_EMC_06(Gpio4, 6); + GPIO_EMC_07(Gpio4, 7); + GPIO_EMC_08(Gpio4, 8); + GPIO_EMC_09(Gpio4, 9); + GPIO_EMC_10(Gpio4, 10); + GPIO_EMC_11(Gpio4, 11); + GPIO_EMC_12(Gpio4, 12); + GPIO_EMC_13(Gpio4, 13); + GPIO_EMC_14(Gpio4, 14); + GPIO_EMC_15(Gpio4, 15); + GPIO_EMC_16(Gpio4, 16); + GPIO_EMC_17(Gpio4, 17); + GPIO_EMC_18(Gpio4, 18); + GPIO_EMC_19(Gpio4, 19); + GPIO_EMC_20(Gpio4, 20); + GPIO_EMC_21(Gpio4, 21); + GPIO_EMC_22(Gpio4, 22); + GPIO_EMC_23(Gpio4, 23); + GPIO_EMC_24(Gpio4, 24); + GPIO_EMC_25(Gpio4, 25); + GPIO_EMC_26(Gpio4, 26); + GPIO_EMC_27(Gpio4, 27); + GPIO_EMC_28(Gpio4, 28); + GPIO_EMC_29(Gpio4, 29); + GPIO_EMC_30(Gpio4, 30); + GPIO_EMC_31(Gpio4, 31); + + // GPIO Bank 3 + GPIO_EMC_32(Gpio3, 18); + GPIO_EMC_33(Gpio3, 19); + GPIO_EMC_34(Gpio3, 20); + GPIO_EMC_35(Gpio3, 21); + GPIO_EMC_36(Gpio3, 22); + GPIO_EMC_37(Gpio3, 23); + GPIO_EMC_38(Gpio3, 24); + GPIO_EMC_39(Gpio3, 25); + GPIO_EMC_40(Gpio3, 26); + GPIO_EMC_41(Gpio3, 27); + GPIO_SD_B0_00(Gpio3, 12); + GPIO_SD_B0_01(Gpio3, 13); + GPIO_SD_B0_02(Gpio3, 14); + GPIO_SD_B0_03(Gpio3, 15); + GPIO_SD_B0_04(Gpio3, 16); + GPIO_SD_B0_05(Gpio3, 17); + GPIO_SD_B1_00(Gpio3, 0); + GPIO_SD_B1_01(Gpio3, 1); + GPIO_SD_B1_02(Gpio3, 2); + GPIO_SD_B1_03(Gpio3, 3); + GPIO_SD_B1_04(Gpio3, 4); + GPIO_SD_B1_05(Gpio3, 5); + GPIO_SD_B1_06(Gpio3, 6); + GPIO_SD_B1_07(Gpio3, 7); + GPIO_SD_B1_08(Gpio3, 8); + GPIO_SD_B1_09(Gpio3, 9); + GPIO_SD_B1_10(Gpio3, 10); + GPIO_SD_B1_11(Gpio3, 11); + + WAKEUP(Gpio5, 0); + PMIC_ON_REQ(Gpio5, 1); + PMIC_STBY_REQ(Gpio5, 2); +} + +pub(crate) mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + #![allow(missing_docs)] + + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); +} diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs index 9c58e8a7d..1d60a0d51 100644 --- a/embassy-nxp/src/gpio/rt1xxx.rs +++ b/embassy-nxp/src/gpio/rt1xxx.rs @@ -12,11 +12,11 @@ use nxp_pac::iomuxc::vals::Pus; use crate::chip::{mux_address, pad_address}; use crate::pac::common::{Reg, RW}; +use crate::pac::gpio::Gpio; #[cfg(feature = "rt")] use crate::pac::interrupt; use crate::pac::iomuxc::regs::{Ctl, MuxCtl}; -#[cfg(gpio5)] -use crate::pac::{self, gpio::Gpio}; +use crate::pac::{self}; /// The GPIO pin level for pins set on "Digital" mode. #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -110,6 +110,14 @@ pub enum Bank { #[cfg(gpio2)] Gpio2, + /// Bank 3 + #[cfg(gpio3)] + Gpio3, + + /// Bank 4 + #[cfg(gpio4)] + Gpio4, + /// Bank 5 #[cfg(gpio5)] Gpio5, @@ -642,6 +650,10 @@ const GPIO_MUX_MODE: u8 = 0b101; static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; #[cfg(gpio2)] static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio3)] +static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio4)] +static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; #[cfg(gpio5)] static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; @@ -658,6 +670,10 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio1 => pac::GPIO1, #[cfg(gpio2)] Bank::Gpio2 => pac::GPIO2, + #[cfg(gpio3)] + Bank::Gpio3 => pac::GPIO3, + #[cfg(gpio4)] + Bank::Gpio4 => pac::GPIO4, #[cfg(gpio5)] Bank::Gpio5 => pac::GPIO5, } @@ -687,6 +703,10 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize], #[cfg(gpio2)] Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize], + #[cfg(gpio3)] + Bank::Gpio3 => &GPIO3_WAKERS[self.pin_number() as usize], + #[cfg(gpio4)] + Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], #[cfg(gpio5)] Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], } @@ -870,25 +890,55 @@ fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) { } } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO1_COMBINED_0_15() { irq_handler(pac::GPIO1, &GPIO1_WAKERS, false); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO1_COMBINED_16_31() { irq_handler(pac::GPIO1, &GPIO1_WAKERS, true); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO2_COMBINED_0_15() { irq_handler(pac::GPIO2, &GPIO2_WAKERS, false); } -#[cfg(all(feature = "mimxrt1011", feature = "rt"))] +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO2_COMBINED_16_31() { + irq_handler(pac::GPIO2, &GPIO2_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO3_COMBINED_0_15() { + irq_handler(pac::GPIO3, &GPIO3_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO3_COMBINED_16_31() { + irq_handler(pac::GPIO3, &GPIO3_WAKERS, true); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO4_COMBINED_0_15() { + irq_handler(pac::GPIO4, &GPIO4_WAKERS, false); +} + +#[cfg(all(feature = "mimxrt1062", feature = "rt"))] +#[interrupt] +fn GPIO4_COMBINED_16_31() { + irq_handler(pac::GPIO4, &GPIO4_WAKERS, true); +} + +#[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] #[interrupt] fn GPIO5_COMBINED_0_15() { irq_handler(pac::GPIO5, &GPIO5_WAKERS, false); diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index a715770c4..5e77fc0db 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -14,6 +14,7 @@ mod time_driver; // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] +#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] mod chip; #[cfg(feature = "unstable-pac")] -- cgit