mayio/
lib.rs

1//! `mayio` — typed GPIO abstractions for no_std environments.
2//!
3//! This crate provides a small, dependency-free, zero-cost, type-safe API to manage GPIO
4//! pins using compile-time direction markers (`Input`/`Output`) and
5//! platform `Bank`/`GpioRegisters` abstractions.
6#![doc = include_str!("../doc/mock_example.md")]
7#![no_std]
8
9mod low;
10
11use core::{marker::PhantomData, ops::Not};
12pub use low::{Bank, io::Gpio, register::GpioRegisters};
13
14mod private {
15    use crate::OutputMode;
16
17    // Sealed trait to prevent external implementations of `Direction`.
18    pub trait Sealed {}
19    impl Sealed for super::Input {}
20    impl<Mode: OutputMode> Sealed for super::Output<Mode> {}
21    impl Sealed for super::PushPull {}
22    impl Sealed for super::OpenDrain {}
23}
24
25use self::private::Sealed;
26
27/// Trait implemented by direction marker types (`Input`, `Output`).
28///
29/// This trait is sealed to keep direction implementations local to the
30/// crate and to allow the API to rely on the two known directions.
31#[doc(hidden)]
32pub trait Direction: Sealed {
33    /// Set the hardware direction for `pin` on the provided GPIO bank handle.
34    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
35    where
36        R: GpioRegisters;
37}
38
39/// Interrupt configuration for a GPIO pin.
40pub enum Interrupt {
41    /// Disable interrupts for the pin.
42    Off,
43    /// Interrupt on rising edge.
44    RisingEdge,
45    /// Interrupt on falling edge. (Typo in original name preserved.)
46    FallingEgdge,
47    /// Interrupt on both edges.
48    BothEdges,
49}
50
51/// Logical level of a GPIO pin.
52#[derive(Copy, Clone, Debug)]
53pub enum Level {
54    /// Logical low / 0.
55    Low,
56    /// Logical high / 1.
57    High,
58}
59
60/// Invert a `Level`.
61impl Not for Level {
62    type Output = Level;
63
64    fn not(self) -> Self::Output {
65        match self {
66            Level::Low => Level::High,
67            Level::High => Level::Low,
68        }
69    }
70}
71
72/// Direction for a single GPIO pin.
73///
74/// The value is forwarded to the platform register implementation which is
75/// responsible for applying the direction in hardware.
76pub enum IoDir {
77    /// Configure the pin as input.
78    In,
79    /// Configure the pin as output.
80    Out,
81}
82
83/// Typed GPIO pin handle.
84///
85/// Generic parameters:
86/// - `N`: constant pin index within the bank.
87/// - `B`: bank type which implements `Bank<R>`.
88/// - `R`: register block implementing `GpioRegisters`.
89/// - `D`: direction marker type (`Input` or `Output`).
90pub struct Io<B, const N: u32, R, D>
91where
92    B: Bank<R>,
93    R: GpioRegisters,
94{
95    dir: PhantomData<fn() -> D>,
96    bank: PhantomData<fn() -> B>,
97    register: PhantomData<fn() -> R>,
98}
99/// Trait implemented by types that provide a default output level for
100/// output marker types. The `Output<S>` marker uses this to determine the
101/// level to drive when the pin is initialized.
102#[doc(hidden)]
103pub trait OutputMode: Sealed {
104    fn active_state() -> Level;
105}
106
107/// Marker type for an input pin.
108///
109/// Use `Io::<N, Bank, Regs, Input>` to obtain a typed input handle. Inputs
110/// are initialized with interrupts disabled by default and can be configured
111/// via `set_interrupt`.
112pub struct Input;
113
114/// Marker type representing an output configured as push-pull.
115///
116/// Use as `Output<PushPull>` to request that the pin be driven high when
117/// active.
118pub struct PushPull;
119impl OutputMode for PushPull {
120    fn active_state() -> Level {
121        Level::High
122    }
123}
124
125/// Marker type representing an output configured as open-drain.
126///
127/// Use as `Output<OpenDrain>` to request that the pin be driven low when
128/// active.
129pub struct OpenDrain;
130impl OutputMode for OpenDrain {
131    fn active_state() -> Level {
132        Level::Low
133    }
134}
135
136/// Marker type for an output pin.
137///
138/// `Output<S>` carries a phantom type parameter `S` which implements
139/// `OutputMode` and selects the level the pin should assume when
140/// initialized. Example: `Io::<3, MyBank, MyRegs, Output<Active>>`.
141pub struct Output<Mode: OutputMode> {
142    default: PhantomData<fn() -> Mode>,
143}
144
145impl Direction for Input {
146    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
147    where
148        R: GpioRegisters,
149    {
150        gpio.set_dir(pin, IoDir::In);
151        gpio.set_interrupt(pin, Interrupt::Off);
152    }
153}
154
155impl<Mode: OutputMode> Direction for Output<Mode> {
156    fn init<R>(gpio: &mut Gpio<R>, pin: u32)
157    where
158        R: GpioRegisters,
159    {
160        let active_state = Mode::active_state();
161        gpio.set_dir(pin, IoDir::Out);
162        gpio.set_active_state(pin, active_state);
163        // Ensure the pin starts low regardless of active state
164        gpio.write(pin, Level::Low);
165    }
166}
167
168impl<B, const N: u32, R, D> Io<B, N, R, D>
169where
170    B: Bank<R>,
171    R: GpioRegisters,
172    D: Direction,
173{
174    /// Initialize the typed IO for pin `N`.
175    ///
176    /// This configures the hardware direction using the marker types `Input` and `Output`.
177    pub fn init() -> Self {
178        let mut bank = <B as Bank<R>>::get_handle();
179        D::init(&mut bank, N);
180        Self {
181            dir: PhantomData,
182            bank: PhantomData,
183            register: PhantomData,
184        }
185    }
186}
187impl<B, const N: u32, R> Io<B, N, R, Input>
188where
189    B: Bank<R>,
190    R: GpioRegisters,
191{
192    /// Set the interrupt configuration for this input pin.
193    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
194        let mut bank = <B as Bank<R>>::get_handle();
195        bank.set_interrupt(N, interrupt);
196    }
197
198    /// Read the current logical level of the pin.
199    pub fn read(&self) -> Level {
200        let bank = <B as Bank<R>>::get_handle();
201        bank.read(N)
202    }
203
204    /// Read whether an interrupt is pending for this pin.
205    pub fn interrupt_pending(&self) -> bool {
206        let mut bank = <B as Bank<R>>::get_handle();
207        bank.interrupt_pending(N)
208    }
209}
210
211impl<B, const N: u32, R, Mode: OutputMode> Io<B, N, R, Output<Mode>>
212where
213    B: Bank<R>,
214    R: GpioRegisters,
215{
216    /// Write a logical level to the pin.
217    fn write(&mut self, level: Level) {
218        let mut bank = <B as Bank<R>>::get_handle();
219        bank.write(N, level);
220    }
221
222    /// Activate the pin (drive to active state).
223    #[inline]
224    pub fn activate(&mut self) {
225        self.write(Mode::active_state());
226    }
227
228    /// Deactivate the pin (drive to inactive state).
229    #[inline]
230    pub fn deactivate(&mut self) {
231        self.write(!Mode::active_state());
232    }
233}