use crate::{iomuxc, ral};
pub use crate::PinPortIncompatibleError;
type AnyInstance = crate::AnyInstance<ral::gpio::RegisterBlock>;
pub struct Port {
gpio: AnyInstance,
}
impl Port {
pub fn new<const N: u8>(gpio: ral::gpio::Instance<N>) -> Self {
let gpio: AnyInstance = crate::into_any(gpio);
Self { gpio }
}
fn instance(&self) -> u8 {
ral::gpio::number(&*self.gpio).unwrap()
}
fn duplicate_instance(&self) -> AnyInstance {
unsafe { AnyInstance::new(&*self.gpio) }
}
pub fn output<P, const N: u8>(
&mut self,
mut pin: P,
) -> Result<Output, PinPortIncompatibleError<P>>
where
P: iomuxc::gpio::Pin<N>,
{
if N != self.instance() {
return Err(PinPortIncompatibleError(pin));
}
iomuxc::gpio::prepare(&mut pin);
Ok(Output::new(self.duplicate_instance(), P::OFFSET))
}
pub fn input<P, const N: u8>(
&mut self,
mut pin: P,
) -> Result<Input, PinPortIncompatibleError<P>>
where
P: iomuxc::gpio::Pin<N>,
{
if N != self.instance() {
return Err(PinPortIncompatibleError(pin));
}
iomuxc::gpio::prepare(&mut pin);
Ok(Input::new(self.duplicate_instance(), P::OFFSET))
}
pub fn set_interrupt(
&mut self,
input: &Input,
trigger: Option<Trigger>,
) -> Result<(), PinPortIncompatibleError<()>> {
if !crate::is_same_instance(&self.gpio, &input.gpio) {
return Err(PinPortIncompatibleError(()));
}
self.set_interrupt_enable(input, false);
if let Some(trigger) = trigger {
self.set_interrupt_trigger(input, trigger);
self.set_interrupt_enable(input, true);
}
Ok(())
}
fn set_interrupt_trigger(&mut self, input: &Input, trigger: Trigger) {
if Trigger::EitherEdge == trigger {
ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
edge_sel | input.mask()
});
} else {
ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
edge_sel & !input.mask()
});
let icr = trigger as u32;
let icr_modify =
|reg| reg & !(0b11 << input.icr_offset()) | (icr << input.icr_offset());
if input.offset < 16 {
ral::modify_reg!(ral::gpio, self.gpio, ICR1, icr_modify);
} else {
ral::modify_reg!(ral::gpio, self.gpio, ICR2, icr_modify);
}
}
}
fn set_interrupt_enable(&mut self, input: &Input, enable: bool) {
if enable {
ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr | input.mask());
} else {
ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr & !input.mask());
}
}
}
pub struct Output {
gpio: AnyInstance,
offset: u32,
}
impl Output {
fn new(gpio: AnyInstance, offset: u32) -> Self {
let output = Self { gpio, offset };
ral::modify_reg!(ral::gpio, output.gpio, GDIR, |gdir| gdir | output.mask());
output
}
const fn mask(&self) -> u32 {
1 << self.offset
}
pub fn set(&self) {
ral::write_reg!(ral::gpio, self.gpio, DR_SET, self.mask());
}
pub fn clear(&self) {
ral::write_reg!(ral::gpio, self.gpio, DR_CLEAR, self.mask());
}
pub fn toggle(&self) {
ral::write_reg!(ral::gpio, self.gpio, DR_TOGGLE, self.mask());
}
pub fn is_set(&self) -> bool {
ral::read_reg!(ral::gpio, self.gpio, DR) & self.mask() != 0
}
pub fn is_pad_high(&self) -> bool {
ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
}
pub fn without_pin(port: &mut Port, offset: u32) -> Self {
Self::new(port.duplicate_instance(), offset)
}
}
pub struct Input {
gpio: AnyInstance,
offset: u32,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Trigger {
Low = 0,
High = 1,
RisingEdge = 2,
FallingEdge = 3,
EitherEdge = 4,
}
impl Input {
fn new(gpio: AnyInstance, offset: u32) -> Self {
let input = Self { gpio, offset };
ral::modify_reg!(ral::gpio, input.gpio, GDIR, |gdir| gdir & !input.mask());
input
}
const fn mask(&self) -> u32 {
1 << self.offset
}
const fn icr_offset(&self) -> u32 {
(self.offset % 16) * 2
}
pub fn is_set(&self) -> bool {
ral::read_reg!(ral::gpio, self.gpio, PSR) & self.mask() != 0
}
pub fn is_triggered(&self) -> bool {
ral::read_reg!(ral::gpio, self.gpio, ISR) & self.mask() != 0
}
pub fn clear_triggered(&self) {
ral::write_reg!(ral::gpio, self.gpio, ISR, self.mask());
}
pub fn is_interrupt_enabled(&self) -> bool {
ral::read_reg!(ral::gpio, self.gpio, IMR) & self.mask() != 0
}
pub fn without_pin(port: &mut Port, offset: u32) -> Self {
Self::new(port.duplicate_instance(), offset)
}
}
impl eh02::digital::v2::OutputPin for Output {
type Error = core::convert::Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.clear();
Ok(())
}
}
impl eh1::digital::ErrorType for Output {
type Error = core::convert::Infallible;
}
impl eh1::digital::OutputPin for Output {
fn set_high(&mut self) -> Result<(), Self::Error> {
Output::set(self);
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
Output::clear(self);
Ok(())
}
}
impl eh1::digital::StatefulOutputPin for Output {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(Output::is_set(self))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!Output::is_set(self))
}
fn toggle(&mut self) -> Result<(), Self::Error> {
Output::toggle(self);
Ok(())
}
}
impl eh1::digital::InputPin for Output {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(Output::is_pad_high(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!Output::is_pad_high(self))
}
}
impl eh1::digital::ErrorType for Input {
type Error = core::convert::Infallible;
}
impl eh1::digital::InputPin for Input {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(Input::is_set(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!Input::is_set(self))
}
}