use crate::{iomuxc, ral};
pub struct Port<const N: u8> {
gpio: ral::gpio::Instance<N>,
}
impl<const N: u8> Port<N> {
pub fn new(gpio: ral::gpio::Instance<N>) -> Self {
Self { gpio }
}
fn register_block(&self) -> &'static ral::gpio::RegisterBlock {
let register_block: &ral::gpio::RegisterBlock = &self.gpio;
let register_block: &'static ral::gpio::RegisterBlock =
unsafe { core::mem::transmute(register_block) };
register_block
}
pub fn output<P>(&mut self, mut pin: P) -> Output<P>
where
P: iomuxc::gpio::Pin<N>,
{
iomuxc::gpio::prepare(&mut pin);
Output::new(pin, self.register_block(), P::OFFSET)
}
pub fn input<P>(&mut self, mut pin: P) -> Input<P>
where
P: iomuxc::gpio::Pin<N>,
{
iomuxc::gpio::prepare(&mut pin);
Input::new(pin, self.register_block(), P::OFFSET)
}
pub fn set_interrupt<P>(&mut self, pin: &Input<P>, trigger: Option<Trigger>) {
self.set_interrupt_enable(pin, false);
if let Some(trigger) = trigger {
self.set_interrupt_trigger(pin, trigger);
self.set_interrupt_enable(pin, true);
}
}
fn set_interrupt_trigger<P>(&mut self, pin: &Input<P>, trigger: Trigger) {
if Trigger::EitherEdge == trigger {
ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
edge_sel | pin.mask()
});
} else {
ral::modify_reg!(ral::gpio, self.gpio, EDGE_SEL, |edge_sel| {
edge_sel & !pin.mask()
});
let icr = trigger as u32;
let icr_modify = |reg| reg & !(0b11 << pin.icr_offset()) | (icr << pin.icr_offset());
if pin.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<P>(&mut self, pin: &Input<P>, enable: bool) {
if enable {
ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr | pin.mask());
} else {
ral::modify_reg!(ral::gpio, self.gpio, IMR, |imr| imr & !pin.mask());
}
}
}
pub struct Output<P> {
pin: P,
gpio: &'static ral::gpio::RegisterBlock,
offset: u32,
}
unsafe impl<P: Send> Send for Output<P> {}
impl<P> Output<P> {
fn new(pin: P, gpio: &'static ral::gpio::RegisterBlock, offset: u32) -> Self {
let output = Self { pin, gpio, offset };
ral::modify_reg!(ral::gpio, 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 release(self) -> P {
self.pin
}
pub fn pin(&self) -> &P {
&self.pin
}
pub fn pin_mut(&mut self) -> &mut P {
&mut self.pin
}
}
impl Output<()> {
pub fn without_pin<const N: u8>(port: &mut Port<N>, offset: u32) -> Self {
Self::new((), port.register_block(), offset)
}
}
pub struct Input<P> {
pin: P,
gpio: &'static ral::gpio::RegisterBlock,
offset: u32,
}
unsafe impl<P: Send> Send for Input<P> {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Trigger {
Low = 0,
High = 1,
RisingEdge = 2,
FallingEdge = 3,
EitherEdge = 4,
}
impl<P> Input<P> {
fn new(pin: P, gpio: &'static ral::gpio::RegisterBlock, offset: u32) -> Self {
let input = Self { pin, gpio, offset };
ral::modify_reg!(ral::gpio, 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 release(self) -> P {
self.pin
}
pub fn pin(&self) -> &P {
&self.pin
}
pub fn pin_mut(&mut self) -> &mut P {
&mut self.pin
}
}
impl Input<()> {
pub fn without_pin<const N: u8>(port: &mut Port<N>, offset: u32) -> Self {
Self::new((), port.register_block(), offset)
}
}
impl<P> eh02::digital::v2::OutputPin for Output<P> {
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(())
}
}
#[cfg(feature = "eh02-unproven")]
impl<P> eh02::digital::v2::StatefulOutputPin for Output<P> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set())
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_set())
}
}
#[cfg(feature = "eh02-unproven")]
impl<P> eh02::digital::v2::ToggleableOutputPin for Output<P> {
type Error = core::convert::Infallible;
fn toggle(&mut self) -> Result<(), Self::Error> {
Output::<P>::toggle(self);
Ok(())
}
}
#[cfg(feature = "eh02-unproven")]
impl<P> eh02::digital::v2::InputPin for Input<P> {
type Error = core::convert::Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set())
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_set())
}
}