From eed314ebb58772476971af49b36b68301eb0d8cc Mon Sep 17 00:00:00 2001 From: MathisDerooNXP <52401665+MathisDeroo@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:00:01 -0800 Subject: Gpio support v2 (#26) * Improve GPIO driver and add button example - port and pcr registers are defined using paste! - added pin configuration for slew rate, drive strength, mux function, etc... - added button example to showcase input gpio feature Signed-off-by: Mathis Deroo * Add pull-up pull-down config support for input gpio Signed-off-by: Mathis Deroo * Replace GPIOs enum with existing ones in the PAC Signed-off-by: Mathis Deroo * Remove init_gpio_pin function as it is done in hal init config Signed-off-by: Mathis Deroo * Integrate feedback for the GPIO driver - Add again missing IO peripherals - Added function to configure separately slew rate, drive strength, pull. - Revert comment changes Signed-off-by: Mathis Deroo * Create user-readable field for the pin configuration Signed-off-by: Mathis Deroo * examples: button: remove left-over OSTIMER initialization While at that, also cargo fmt * Fix warnings * Add documentation for public functions Signed-off-by: Mathis Deroo * Expose port and pcr registers to AnyPin implementation Signed-off-by: Mathis Deroo * Remove unnecessary change Signed-off-by: Mathis Deroo * Run cargo fmt --------- Signed-off-by: Mathis Deroo Co-authored-by: Felipe Balbi --- examples/src/bin/blinky.rs | 8 +- examples/src/bin/button.rs | 21 ++++ src/gpio.rs | 285 +++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 8 +- src/pins.rs | 61 ---------- 5 files changed, 278 insertions(+), 105 deletions(-) create mode 100644 examples/src/bin/button.rs diff --git a/examples/src/bin/blinky.rs b/examples/src/bin/blinky.rs index ab1e59bdb..dd08ec0d9 100644 --- a/examples/src/bin/blinky.rs +++ b/examples/src/bin/blinky.rs @@ -3,7 +3,7 @@ use embassy_executor::Spawner; use embassy_time::Timer; -use hal::gpio::{Level, Output}; +use hal::gpio::{DriveStrength, Level, Output, SlewRate}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; #[embassy_executor::main] @@ -12,9 +12,9 @@ async fn main(_spawner: Spawner) { defmt::info!("Blink example"); - let mut red = Output::new(p.P3_18, Level::High); - let mut green = Output::new(p.P3_19, Level::High); - let mut blue = Output::new(p.P3_21, Level::High); + let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast); + let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast); + let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast); loop { defmt::info!("Toggle LEDs"); diff --git a/examples/src/bin/button.rs b/examples/src/bin/button.rs new file mode 100644 index 000000000..2abfe0a9f --- /dev/null +++ b/examples/src/bin/button.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use hal::gpio::{DriveStrength, Input, Pull, SlewRate}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("Button example"); + + let monitor = Input::new(p.P1_7, Pull::Disabled, DriveStrength::Normal, SlewRate::Slow); + + loop { + defmt::info!("Pin level is {:?}", monitor.get_level()); + Timer::after_millis(1000).await; + } +} diff --git a/src/gpio.rs b/src/gpio.rs index 09a414d3b..9565fbb6e 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -8,13 +8,87 @@ use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; use paste::paste; +use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre}; + /// Logical level for GPIO pins. #[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Level { Low, High, } +impl From for Level { + fn from(val: bool) -> Self { + match val { + true => Self::High, + false => Self::Low, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Pull { + Disabled, + Up, + Down, +} + +impl From for (Pe, Ps) { + fn from(pull: Pull) -> Self { + match pull { + Pull::Disabled => (Pe::Pe0, Ps::Ps0), + Pull::Up => (Pe::Pe1, Ps::Ps1), + Pull::Down => (Pe::Pe1, Ps::Ps0), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum SlewRate { + Fast, + Slow, +} + +impl From for Sre { + fn from(slew_rate: SlewRate) -> Self { + match slew_rate { + SlewRate::Fast => Sre::Sre0, + SlewRate::Slow => Sre::Sre1, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum DriveStrength { + Normal, + Double, +} + +impl From for Dse { + fn from(strength: DriveStrength) -> Self { + match strength { + DriveStrength::Normal => Dse::Dse0, + DriveStrength::Double => Dse::Dse1, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Inverter { + Disabled, + Enabled, +} + +impl From for Inv { + fn from(strength: Inverter) -> Self { + match strength { + Inverter::Disabled => Inv::Inv0, + Inverter::Enabled => Inv::Inv1, + } + } +} + pub type Gpio = crate::peripherals::GPIO0; /// Type-erased representation of a GPIO pin. @@ -22,12 +96,26 @@ pub struct AnyPin { port: usize, pin: usize, gpio: &'static crate::pac::gpio0::RegisterBlock, + port_reg: &'static crate::pac::port0::RegisterBlock, + pcr_reg: &'static crate::pac::port0::Pcr0, } impl AnyPin { /// Create an `AnyPin` from raw components. - pub fn new(port: usize, pin: usize, gpio: &'static crate::pac::gpio0::RegisterBlock) -> Self { - Self { port, pin, gpio } + pub fn new( + port: usize, + pin: usize, + gpio: &'static crate::pac::gpio0::RegisterBlock, + port_reg: &'static crate::pac::port0::RegisterBlock, + pcr_reg: &'static crate::pac::port0::Pcr0, + ) -> Self { + Self { + port, + pin, + gpio, + port_reg, + pcr_reg, + } } #[inline(always)] @@ -49,6 +137,16 @@ impl AnyPin { pub fn pin_index(&self) -> usize { self.pin } + + #[inline(always)] + fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { + self.port_reg + } + + #[inline(always)] + fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { + self.pcr_reg + } } embassy_hal_internal::impl_peripheral!(AnyPin); @@ -65,6 +163,20 @@ trait SealedPin { } fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock; + + fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock; + + fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0; + + fn set_function(&self, function: Mux); + + fn set_pull(&self, pull: Pull); + + fn set_drive_strength(&self, strength: Dse); + + fn set_slew_rate(&self, slew_rate: Sre); + + fn set_enable_input_buffer(&self); } /// GPIO pin trait. @@ -75,57 +187,120 @@ pub trait GpioPin: SealedPin + Sized + PeripheralType + Into + 'static { // SAFETY: This is only called within the GpioPin trait, which is only // implemented within this module on valid pin peripherals and thus // has been verified to be correct. - AnyPin::new(self.port(), self.pin(), self.gpio()) + AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) } } impl SealedPin for AnyPin { - #[inline] fn pin_port(&self) -> usize { self.port * 32 + self.pin } - #[inline] fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { self.gpio() } + + fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { + self.port_reg() + } + + fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { + self.pcr_reg() + } + + fn set_function(&self, function: Mux) { + self.pcr_reg().modify(|_, w| w.mux().variant(function)); + } + + fn set_pull(&self, pull: Pull) { + let (pull_enable, pull_select) = pull.into(); + self.pcr_reg().modify(|_, w| { + w.pe().variant(pull_enable); + w.ps().variant(pull_select) + }); + } + + fn set_drive_strength(&self, strength: Dse) { + self.pcr_reg().modify(|_, w| w.dse().variant(strength)); + } + + fn set_slew_rate(&self, slew_rate: Sre) { + self.pcr_reg().modify(|_, w| w.sre().variant(slew_rate)); + } + + fn set_enable_input_buffer(&self) { + self.pcr_reg().modify(|_, w| w.ibe().ibe1()); + } } impl GpioPin for AnyPin {} macro_rules! impl_pin { ($peri:ident, $port:expr, $pin:expr, $block:ident) => { - impl SealedPin for crate::peripherals::$peri { - #[inline] - fn pin_port(&self) -> usize { - $port * 32 + $pin - } + paste! { + impl SealedPin for crate::peripherals::$peri { + fn pin_port(&self) -> usize { + $port * 32 + $pin + } - #[inline] - fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { - unsafe { &*crate::pac::$block::ptr() } - } - } + fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock { + unsafe { &*crate::pac::$block::ptr() } + } + + fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock { + unsafe { &*crate::pac::[]::ptr() } + } + + fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 { + self.port_reg().[]() + } + + fn set_function(&self, function: Mux) { + unsafe { + let port_reg = &*crate::pac::[]::ptr(); + port_reg.[]().modify(|_, w| { + w.mux().variant(function) + }); + } + } + + fn set_pull(&self, pull: Pull) { + let port_reg = unsafe {&*crate::pac::[]::ptr()}; + let (pull_enable, pull_select) = pull.into(); + port_reg.[]().modify(|_, w| { + w.pe().variant(pull_enable); + w.ps().variant(pull_select) + }); + } + + fn set_drive_strength(&self, strength: Dse) { + let port_reg = unsafe {&*crate::pac::[]::ptr()}; + port_reg.[]().modify(|_, w| w.dse().variant(strength)); + } - impl GpioPin for crate::peripherals::$peri {} + fn set_slew_rate(&self, slew_rate: Sre) { + let port_reg = unsafe {&*crate::pac::[]::ptr()}; + port_reg.[]().modify(|_, w| w.sre().variant(slew_rate)); + } - impl From for AnyPin { - fn from(value: crate::peripherals::$peri) -> Self { - value.degrade() + fn set_enable_input_buffer(&self) { + let port_reg = unsafe {&*crate::pac::[]::ptr()}; + port_reg.[]().modify(|_, w| w.ibe().ibe1()); + } } - } - impl crate::peripherals::$peri { - /// Convenience helper to obtain a type-erased handle to this pin. - pub fn degrade(&self) -> AnyPin { - AnyPin::new(self.port(), self.pin(), self.gpio()) + impl GpioPin for crate::peripherals::$peri {} + + impl From for AnyPin { + fn from(value: crate::peripherals::$peri) -> Self { + value.degrade() + } } - #[inline] - pub fn set_mux_gpio() { - paste! { - let port = unsafe { crate::pac::[]::steal()}; - port.[]().write(|w| w.mux().mux00()); + impl crate::peripherals::$peri { + /// Convenience helper to obtain a type-erased handle to this pin. + pub fn degrade(&self) -> AnyPin { + AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) } } } @@ -309,6 +484,7 @@ impl<'d> Flex<'d> { /// The pin remains unmodified. The initial output level is unspecified, but /// can be changed before the pin is put into output mode. pub fn new(pin: Peri<'d, impl GpioPin>) -> Self { + pin.set_function(Mux::Mux00); Self { pin: pin.into(), _marker: PhantomData, @@ -326,22 +502,22 @@ impl<'d> Flex<'d> { } /// Put the pin into input mode. - /// - /// The pull setting is left unchanged. - #[inline] pub fn set_as_input(&mut self) { let mask = self.mask(); let gpio = self.gpio(); + + self.set_enable_input_buffer(); + gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); } /// Put the pin into output mode. - /// - /// The initial output level is left unchanged. - #[inline] pub fn set_as_output(&mut self) { let mask = self.mask(); let gpio = self.gpio(); + + self.set_pull(Pull::Disabled); + gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); } @@ -395,6 +571,31 @@ impl<'d> Flex<'d> { pub fn is_set_low(&self) -> bool { !self.is_set_high() } + + /// Configure the pin pull up/down level. + pub fn set_pull(&mut self, pull_select: Pull) { + self.pin.set_pull(pull_select); + } + + /// Configure the pin drive strength. + pub fn set_drive_strength(&mut self, strength: DriveStrength) { + self.pin.set_drive_strength(strength.into()); + } + + /// Configure the pin slew rate. + pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { + self.pin.set_slew_rate(slew_rate.into()); + } + + /// Enable input buffer for the pin. + pub fn set_enable_input_buffer(&mut self) { + self.pin.set_enable_input_buffer(); + } + + /// Get pin level. + pub fn get_level(&self) -> Level { + self.is_high().into() + } } /// GPIO output driver that owns a `Flex` pin. @@ -404,10 +605,12 @@ pub struct Output<'d> { impl<'d> Output<'d> { /// Create a GPIO output driver for a [GpioPin] with the provided [Level]. - pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level) -> Self { + pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self { let mut flex = Flex::new(pin); flex.set_level(initial); flex.set_as_output(); + flex.set_drive_strength(strength); + flex.set_slew_rate(slew_rate); Self { flex } } @@ -461,9 +664,12 @@ pub struct Input<'d> { impl<'d> Input<'d> { /// Create a GPIO input driver for a [GpioPin]. - pub fn new(pin: Peri<'d, impl GpioPin>) -> Self { + pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull, strength: DriveStrength, slew_rate: SlewRate) -> Self { let mut flex = Flex::new(pin); flex.set_as_input(); + flex.set_drive_strength(strength); + flex.set_slew_rate(slew_rate); + flex.set_pull(pull_select); Self { flex } } @@ -484,6 +690,11 @@ impl<'d> Input<'d> { pub fn into_flex(self) -> Flex<'d> { self.flex } + + // Get the pin level. + pub fn get_level(&self) -> Level { + self.flex.get_level() + } } // Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs. diff --git a/src/lib.rs b/src/lib.rs index e93ff61a6..175642f75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] -// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs -// are complete prior to release. -#![allow(clippy::missing_safety_doc)] +#![allow(async_fn_in_trait)] +#![doc = include_str!("../README.md")] + +// //! ## Feature flags +// #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] pub mod clocks; // still provide clock helpers pub mod gpio; diff --git a/src/pins.rs b/src/pins.rs index f802568f3..f65195dd2 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -60,64 +60,3 @@ pub unsafe fn configure_adc_pins() { }); core::arch::asm!("dsb sy; isb sy"); } - -/// Configure a pin for a specific mux alternative. -/// -/// # Arguments -/// * `port` - Port number (0-4) -/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7) -/// * `mux` - Mux alternative (0-15, where 0 = GPIO, 1-15 = other functions) -pub unsafe fn set_pin_mux(port: u8, pin: u8, mux: u8) { - // Validate mux value (0-15) - if mux > 15 { - panic!("Invalid mux value: {}, must be 0-15", mux); - } - - // Validate pin number based on port - let max_pin = match port { - 0 => 7, // PORT0: pins 0-7 - 1 => 19, // PORT1: pins 0-19 - 2 => 26, // PORT2: pins 0-26 - 3 => 31, // PORT3: pins 0-31 - 4 => 7, // PORT4: pins 0-7 - _ => panic!("Unsupported GPIO port: {}", port), - }; - - if pin > max_pin { - panic!("Invalid pin {} for PORT{}, max pin is {}", pin, port, max_pin); - } - - // Get the base address for the port - let port_base: *mut u32 = match port { - 0 => pac::Port0::ptr() as *mut u32, - 1 => pac::Port1::ptr() as *mut u32, - 2 => pac::Port2::ptr() as *mut u32, - 3 => pac::Port3::ptr() as *mut u32, - 4 => pac::Port4::ptr() as *mut u32, - _ => panic!("Unsupported GPIO port: {}", port), - }; - - // PCR registers are 4 bytes apart, starting at offset 0 for PCR0 - let pcr_addr = port_base.add(pin as usize); - - // Read current PCR value - let current_val = pcr_addr.read_volatile(); - - // Clear mux bits (bits 8-11) and set new mux value - let new_val = (current_val & !(0xF << 8)) | ((mux as u32) << 8); - - // Write back the new value - pcr_addr.write_volatile(new_val); - - core::arch::asm!("dsb sy; isb sy"); -} - -/// Configure a pin for GPIO mode (ALT0). -/// This is a convenience function that calls set_pin_mux with mux=0. -/// -/// # Arguments -/// * `port` - Port number (0-4) -/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7) -pub unsafe fn set_pin_mux_gpio(port: u8, pin: u8) { - set_pin_mux(port, pin, 0); -} -- cgit