Skip to content

Commit a987067

Browse files
authored
Merge pull request #5687 from embassy-rs/mcxa/gpio
[MCXA] gpio: Add output open-drain and input inversion support
2 parents 6dccb68 + b0155bb commit a987067

File tree

1 file changed

+129
-1
lines changed

1 file changed

+129
-1
lines changed

embassy-mcxa/src/gpio.rs

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::interrupt::typelevel::{Handler, Interrupt};
1515
use crate::pac::common::{RW, Reg};
1616
use crate::pac::gpio::vals::{Irqc, Isf, Pdd, Pid, Ptco, Ptso};
1717
use crate::pac::port::regs::Pcr;
18-
use crate::pac::port::vals::{Dse, Ibe, Inv, Mux, Pe, Ps, Sre};
18+
use crate::pac::port::vals::{Dse, Ibe, Inv, Mux, Ode, Pe, Ps, Sre};
1919

2020
struct BitIter(u32);
2121

@@ -145,6 +145,25 @@ impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
145145
}
146146
}
147147

148+
/// Open-drain for GPIO pins.
149+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
150+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151+
pub enum OpenDrain {
152+
/// Output is push-pull (not open-drain)
153+
No,
154+
/// Output is open-drain
155+
Yes,
156+
}
157+
158+
impl From<OpenDrain> for Ode {
159+
fn from(open_drain: OpenDrain) -> Self {
160+
match open_drain {
161+
OpenDrain::No => Ode::ODE0,
162+
OpenDrain::Yes => Ode::ODE1,
163+
}
164+
}
165+
}
166+
148167
/// Logical level for GPIO pins.
149168
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
150169
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -643,6 +662,18 @@ impl<'d, M: Mode> Flex<'d, M> {
643662
!self.is_set_high()
644663
}
645664

665+
/// Configure open-drain output.
666+
#[inline]
667+
pub fn set_open_drain(&mut self, open_drain: OpenDrain) {
668+
self.pin.pcr_reg().modify(|w| w.set_ode(open_drain.into()));
669+
}
670+
671+
/// Configure the input logic inversion of this pin.
672+
#[inline]
673+
pub fn set_input_inversion(&mut self, invert: Inverter) {
674+
self.pin.pcr_reg().modify(|w| w.set_inv(invert.into()));
675+
}
676+
646677
/// Configure the pin pull up/down level.
647678
pub fn set_pull(&mut self, pull_select: Pull) {
648679
self.pin.set_pull(pull_select);
@@ -799,6 +830,7 @@ impl<'d> Output<'d> {
799830
flex.set_as_output();
800831
flex.set_drive_strength(strength);
801832
flex.set_slew_rate(slew_rate);
833+
flex.set_open_drain(OpenDrain::No);
802834
Self { flex }
803835
}
804836

@@ -843,6 +875,96 @@ impl<'d> Output<'d> {
843875
pub fn into_flex(self) -> Flex<'d> {
844876
self.flex
845877
}
878+
879+
/// Convert this output pin into an open-drain output pin.
880+
#[inline]
881+
pub fn into_open_drain(mut self) -> OutputOpenDrain<'d> {
882+
self.flex.set_open_drain(OpenDrain::Yes);
883+
OutputOpenDrain { flex: self.flex }
884+
}
885+
}
886+
887+
/// GPIO output open-drain driver that owns a `Flex` pin.
888+
pub struct OutputOpenDrain<'d> {
889+
flex: Flex<'d>,
890+
}
891+
892+
impl<'d> OutputOpenDrain<'d> {
893+
/// Create a GPIO output open-drain driver for a [GpioPin] with the provided [Level].
894+
pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {
895+
let mut flex = Flex::new(pin);
896+
flex.set_level(initial);
897+
flex.set_as_output();
898+
flex.set_drive_strength(strength);
899+
flex.set_slew_rate(slew_rate);
900+
flex.set_enable_input_buffer(true);
901+
flex.set_open_drain(OpenDrain::Yes);
902+
Self { flex }
903+
}
904+
905+
/// Get whether the pin level is high.
906+
#[inline]
907+
pub fn is_high(&self) -> bool {
908+
self.flex.is_high()
909+
}
910+
911+
/// Get whether the pin level is low.
912+
#[inline]
913+
pub fn is_low(&self) -> bool {
914+
self.flex.is_low()
915+
}
916+
917+
/// Set the output as high (open-drain high is just letting go of the line).
918+
#[inline]
919+
pub fn set_high(&mut self) {
920+
self.flex.set_high();
921+
}
922+
923+
/// Set the output as low (open-drain low is driving the line low).
924+
#[inline]
925+
pub fn set_low(&mut self) {
926+
self.flex.set_low();
927+
}
928+
929+
/// Set the output level.
930+
#[inline]
931+
pub fn set_level(&mut self, level: Level) {
932+
self.flex.set_level(level);
933+
}
934+
935+
/// Get the pin level.
936+
pub fn get_level(&self) -> Level {
937+
self.flex.get_level()
938+
}
939+
940+
/// Toggle the output level.
941+
#[inline]
942+
pub fn toggle(&mut self) {
943+
if self.flex.is_set_low() {
944+
self.set_high();
945+
} else {
946+
self.set_low();
947+
}
948+
}
949+
950+
/// Configure the input logic inversion of this pin.
951+
#[inline]
952+
pub fn set_inversion(&mut self, invert: Inverter) {
953+
self.flex.set_input_inversion(invert)
954+
}
955+
956+
/// Expose the inner `Flex` if callers need to reconfigure the pin.
957+
#[inline]
958+
pub fn into_flex(self) -> Flex<'d> {
959+
self.flex
960+
}
961+
962+
/// Convert this output pin into an push-pull output pin.
963+
#[inline]
964+
pub fn into_push_pull(mut self) -> Output<'d> {
965+
self.flex.set_open_drain(OpenDrain::No);
966+
Output { flex: self.flex }
967+
}
846968
}
847969

848970
/// GPIO input driver that owns a `Flex` pin.
@@ -885,6 +1007,12 @@ impl<'d, M: Mode> Input<'d, M> {
8851007
self.flex
8861008
}
8871009

1010+
/// Configure the input logic inversion of this pin.
1011+
#[inline]
1012+
pub fn set_inversion(&mut self, invert: Inverter) {
1013+
self.flex.set_input_inversion(invert)
1014+
}
1015+
8881016
/// Get the pin level.
8891017
pub fn get_level(&self) -> Level {
8901018
self.flex.get_level()

0 commit comments

Comments
 (0)