mayio 0.2.0

A minimal no-std GPIO HAL for embedded systems
Documentation
//! `mayio` — typed GPIO abstractions for no_std environments.
//!
//! This crate provides a small, dependency-free, zero-cost, type-safe API to manage GPIO
//! pins using compile-time direction markers (`Input`/`Output`) and
//! platform `Bank`/`GpioRegisters` abstractions.
#![doc = include_str!("../doc/mock_example.md")]
#![no_std]

mod low;

use core::{marker::PhantomData, ops::Not};
pub use low::{Bank, io::Gpio, register::GpioRegisters};

mod private {
    use crate::OutputMode;

    // Sealed trait to prevent external implementations of `Direction`.
    pub trait Sealed {}
    impl Sealed for super::Input {}
    impl<Mode: OutputMode> Sealed for super::Output<Mode> {}
    impl Sealed for super::PushPull {}
    impl Sealed for super::OpenDrain {}
}

use self::private::Sealed;

/// Trait implemented by direction marker types (`Input`, `Output`).
///
/// This trait is sealed to keep direction implementations local to the
/// crate and to allow the API to rely on the two known directions.
#[doc(hidden)]
pub trait Direction: Sealed {
    /// Set the hardware direction for `pin` on the provided GPIO bank handle.
    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
    where
        R: GpioRegisters;
}

/// Interrupt configuration for a GPIO pin.
pub enum Interrupt {
    /// Disable interrupts for the pin.
    Off,
    /// Interrupt on rising edge.
    RisingEdge,
    /// Interrupt on falling edge. (Typo in original name preserved.)
    FallingEgdge,
    /// Interrupt on both edges.
    BothEdges,
}

/// Logical level of a GPIO pin.
#[derive(Copy, Clone, Debug)]
pub enum Level {
    /// Logical low / 0.
    Low,
    /// Logical high / 1.
    High,
}

/// Invert a `Level`.
impl Not for Level {
    type Output = Level;

    fn not(self) -> Self::Output {
        match self {
            Level::Low => Level::High,
            Level::High => Level::Low,
        }
    }
}

/// Direction for a single GPIO pin.
///
/// The value is forwarded to the platform register implementation which is
/// responsible for applying the direction in hardware.
pub enum IoDir {
    /// Configure the pin as input.
    In,
    /// Configure the pin as output.
    Out,
}

/// Typed GPIO pin handle.
///
/// Generic parameters:
/// - `N`: constant pin index within the bank.
/// - `B`: bank type which implements `Bank<R>`.
/// - `R`: register block implementing `GpioRegisters`.
/// - `D`: direction marker type (`Input` or `Output`).
pub struct Io<B, const N: u32, R, D>
where
    B: Bank<R>,
    R: GpioRegisters,
{
    dir: PhantomData<fn() -> D>,
    bank: PhantomData<fn() -> B>,
    register: PhantomData<fn() -> R>,
}
/// Trait implemented by types that provide a default output level for
/// output marker types. The `Output<S>` marker uses this to determine the
/// level to drive when the pin is initialized.
#[doc(hidden)]
pub trait OutputMode: Sealed {
    fn active_state() -> Level;
}

/// Marker type for an input pin.
///
/// Use `Io::<N, Bank, Regs, Input>` to obtain a typed input handle. Inputs
/// are initialized with interrupts disabled by default and can be configured
/// via `set_interrupt`.
pub struct Input;

/// Marker type representing an output configured as push-pull.
///
/// Use as `Output<PushPull>` to request that the pin be driven high when
/// active.
pub struct PushPull;
impl OutputMode for PushPull {
    fn active_state() -> Level {
        Level::High
    }
}

/// Marker type representing an output configured as open-drain.
///
/// Use as `Output<OpenDrain>` to request that the pin be driven low when
/// active.
pub struct OpenDrain;
impl OutputMode for OpenDrain {
    fn active_state() -> Level {
        Level::Low
    }
}

/// Marker type for an output pin.
///
/// `Output<S>` carries a phantom type parameter `S` which implements
/// `OutputMode` and selects the level the pin should assume when
/// initialized. Example: `Io::<3, MyBank, MyRegs, Output<Active>>`.
pub struct Output<Mode: OutputMode> {
    default: PhantomData<fn() -> Mode>,
}

impl Direction for Input {
    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
    where
        R: GpioRegisters,
    {
        gpio.set_dir(pin, IoDir::In);
        gpio.set_interrupt(pin, Interrupt::Off);
    }
}

impl<Mode: OutputMode> Direction for Output<Mode> {
    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
    where
        R: GpioRegisters,
    {
        let active_state = Mode::active_state();
        gpio.set_dir(pin, IoDir::Out);
        gpio.set_active_state(pin, active_state);
        // Ensure the pin starts low regardless of active state
        gpio.write(pin, Level::Low);
    }
}

impl<B, const N: u32, R, D> Io<B, N, R, D>
where
    B: Bank<R>,
    R: GpioRegisters,
    D: Direction,
{
    /// Initialize the typed IO for pin `N`.
    ///
    /// This configures the hardware direction using the marker types `Input` and `Output`.
    pub fn init() -> Self {
        let mut bank = <B as Bank<R>>::get_handle();
        D::init(&mut bank, N);
        Self {
            dir: PhantomData,
            bank: PhantomData,
            register: PhantomData,
        }
    }
}
impl<B, const N: u32, R> Io<B, N, R, Input>
where
    B: Bank<R>,
    R: GpioRegisters,
{
    /// Set the interrupt configuration for this input pin.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        let mut bank = <B as Bank<R>>::get_handle();
        bank.set_interrupt(N, interrupt);
    }

    /// Read the current logical level of the pin.
    pub fn read(&self) -> Level {
        let bank = <B as Bank<R>>::get_handle();
        bank.read(N)
    }

    /// Read whether an interrupt is pending for this pin.
    pub fn interrupt_pending(&self) -> bool {
        let mut bank = <B as Bank<R>>::get_handle();
        bank.interrupt_pending(N)
    }
}

impl<B, const N: u32, R, Mode: OutputMode> Io<B, N, R, Output<Mode>>
where
    B: Bank<R>,
    R: GpioRegisters,
{
    /// Write a logical level to the pin.
    fn write(&mut self, level: Level) {
        let mut bank = <B as Bank<R>>::get_handle();
        bank.write(N, level);
    }

    /// Activate the pin (drive to active state).
    #[inline]
    pub fn activate(&mut self) {
        self.write(Mode::active_state());
    }

    /// Deactivate the pin (drive to inactive state).
    #[inline]
    pub fn deactivate(&mut self) {
        self.write(!Mode::active_state());
    }
}