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}