zynq7000_hal/gpio/
mod.rs

1//! # GPIO module
2//!
3//! This module contains a MIO and EMIO pin resource managements singleton as well as abstractions
4//! to use these pins as GPIOs.
5//!
6//! # Examples
7//!
8//! - [Blinky](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/simple/src/main.rs)
9//! - [Logger example](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/simple/src/bin/logger.rs)
10//!   which uses MIO pins for the UART.
11pub mod emio;
12pub mod ll;
13pub mod mio;
14
15use core::convert::Infallible;
16use ll::PinOffset;
17use mio::{MioPin, MuxConfig};
18
19use crate::gpio::ll::LowLevelGpio;
20use crate::{enable_amba_peripheral_clock, slcr::Slcr};
21pub use embedded_hal::digital::PinState;
22use zynq7000::{gpio::MmioGpio, slcr::reset::GpioClockReset};
23
24#[derive(Debug, thiserror::Error)]
25#[error("MIO pins 7 and 8 can only be output pins")]
26pub struct PinIsOutputOnly;
27
28/// GPIO pin singleton to allow resource management of both MIO and EMIO pins.
29pub struct GpioPins {
30    pub mio: mio::Pins,
31    pub emio: emio::Pins,
32}
33
34impl GpioPins {
35    pub fn new(gpio: MmioGpio) -> Self {
36        enable_amba_peripheral_clock(crate::PeriphSelect::Gpio);
37        Self {
38            mio: mio::Pins::new(unsafe { gpio.clone() }),
39            emio: emio::Pins::new(gpio),
40        }
41    }
42}
43
44/// Reset the GPIO peripheral using the SLCR reset register for GPIO.
45#[inline]
46pub fn reset() {
47    unsafe {
48        Slcr::with(|regs| {
49            regs.reset_ctrl()
50                .write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(true).build());
51            // Keep it in reset for one cycle.. not sure if this is necessary.
52            cortex_ar::asm::nop();
53            regs.reset_ctrl()
54                .write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(false).build());
55        });
56    }
57}
58
59/// Enumeration of all pin modes. Some of the modes are only valid for MIO pins.
60#[derive(Debug, Copy, Clone, PartialEq, Eq)]
61pub enum PinMode {
62    OutputPushPull,
63    /// See [super::gpio] documentation for more information on running an output pin in
64    /// open-drain configuration.
65    OutputOpenDrain,
66    InputFloating,
67    InputPullUp,
68    /// MIO-only peripheral pin configuration
69    MioIoPeriph(MuxConfig),
70}
71
72#[derive(Debug, thiserror::Error)]
73#[error("invalid pin mode for MIO pin: {0:?}")]
74pub struct InvalidPinMode(pub PinMode);
75
76impl embedded_hal::digital::Error for InvalidPinMode {
77    fn kind(&self) -> embedded_hal::digital::ErrorKind {
78        embedded_hal::digital::ErrorKind::Other
79    }
80}
81
82pub trait IoPinProvider {
83    fn mode(&self) -> PinMode;
84
85    fn offset(&self) -> PinOffset;
86
87    #[inline]
88    fn is_input(&self) -> bool {
89        matches!(self.mode(), PinMode::InputFloating | PinMode::InputPullUp)
90    }
91    #[inline]
92    fn is_output(&self) -> bool {
93        matches!(
94            self.mode(),
95            PinMode::OutputPushPull | PinMode::OutputOpenDrain
96        )
97    }
98
99    #[inline]
100    fn is_io_periph(&self) -> bool {
101        matches!(self.mode(), PinMode::MioIoPeriph(_))
102    }
103}
104
105/// Flex pin abstraction which can be dynamically re-configured.
106///
107/// The following functions can be configured at run-time:
108///
109///  - Input Floating
110///  - Input with Pull-Up
111///  - Output Push-Pull
112///  - Output Open-Drain.
113///
114/// Flex pins are always floating input pins after construction except for MIO7 and MIO8,
115/// which are Push-Pull Output pins with initial low-level.
116///
117/// ## Notes on [PinMode::OutputOpenDrain] configuration
118///
119/// For MIO, the open-drain functionality is simulated by only enabling the output driver
120/// when driving the pin low, and leaving the pin floating when the pin is driven high.
121/// The internal pull-up will also be enabled to have a high state if the pin is not driven.
122///
123/// For EMIO, the pull-up and the IO buffer needs to be provided in the FPGA design for the
124/// used EMIO pins because the EMIO pins are just wires going out to the FPGA design.
125/// The software will still perform the necessary logic when driving the pin low or high.
126///
127/// ## Notes on [PinMode::InputPullUp] configuration
128///
129/// For EMIO, the pull-up wiring needs to be provided by the FPGA design.
130pub struct Flex {
131    ll: LowLevelGpio,
132    mode: PinMode,
133}
134
135impl Flex {
136    pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Self {
137        let mut ll = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
138        if I::OFFSET == 7 || I::OFFSET == 8 {
139            ll.configure_as_output_push_pull(PinState::Low);
140        } else {
141            ll.configure_as_input_floating().unwrap();
142        }
143        Self {
144            ll,
145            mode: PinMode::InputFloating,
146        }
147    }
148
149    pub fn new_for_emio(pin: emio::EmioPin) -> Self {
150        let mut ll = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
151        ll.configure_as_input_floating().unwrap();
152        Self {
153            ll,
154            mode: PinMode::InputFloating,
155        }
156    }
157
158    pub fn configure_as_input_floating(&mut self) -> Result<(), PinIsOutputOnly> {
159        self.mode = PinMode::InputFloating;
160        self.ll.configure_as_input_floating()
161    }
162
163    pub fn configure_as_input_with_pull_up(&mut self) -> Result<(), PinIsOutputOnly> {
164        self.mode = PinMode::InputPullUp;
165        self.ll.configure_as_input_with_pull_up()
166    }
167
168    pub fn configure_as_output_push_pull(&mut self, level: PinState) {
169        self.mode = PinMode::OutputPushPull;
170        self.ll.configure_as_output_push_pull(level);
171    }
172
173    pub fn configure_as_output_open_drain(&mut self, level: PinState, with_internal_pullup: bool) {
174        self.mode = PinMode::OutputOpenDrain;
175        self.ll
176            .configure_as_output_open_drain(level, with_internal_pullup);
177    }
178
179    /// If the pin is configured as an input pin, this function does nothing.
180    pub fn set_high(&mut self) {
181        if self.is_input() {
182            return;
183        }
184        if self.mode == PinMode::OutputOpenDrain {
185            self.ll.disable_output_driver();
186        } else {
187            self.ll.set_high();
188        }
189    }
190
191    /// If the pin is configured as an input pin, this function does nothing.
192    pub fn set_low(&mut self) {
193        if self.is_input() {
194            return;
195        }
196        self.ll.set_low();
197        if self.mode == PinMode::OutputOpenDrain {
198            self.ll.enable_output_driver();
199        }
200    }
201
202    /// Reads the input state of the pin, regardless of configured mode.
203    #[inline]
204    pub fn is_high(&self) -> bool {
205        self.ll.is_high()
206    }
207
208    /// Reads the input state of the pin, regardless of configured mode.
209    #[inline]
210    pub fn is_low(&self) -> bool {
211        !self.ll.is_high()
212    }
213
214    /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
215    /// of this function is undefined.
216    #[inline]
217    pub fn is_set_low(&self) -> bool {
218        self.ll.is_set_low()
219    }
220
221    /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
222    /// of this function is undefined.
223    #[inline]
224    pub fn is_set_high(&self) -> bool {
225        !self.is_set_low()
226    }
227}
228
229impl IoPinProvider for Flex {
230    fn mode(&self) -> PinMode {
231        self.mode
232    }
233
234    fn offset(&self) -> PinOffset {
235        self.ll.offset()
236    }
237}
238
239impl embedded_hal::digital::ErrorType for Flex {
240    type Error = Infallible;
241}
242
243impl embedded_hal::digital::InputPin for Flex {
244    /// Reads the input state of the pin, regardless of configured mode.
245    #[inline]
246    fn is_high(&mut self) -> Result<bool, Self::Error> {
247        Ok(self.ll.is_high())
248    }
249
250    /// Reads the input state of the pin, regardless of configured mode.
251    #[inline]
252    fn is_low(&mut self) -> Result<bool, Self::Error> {
253        Ok(self.ll.is_low())
254    }
255}
256
257impl embedded_hal::digital::OutputPin for Flex {
258    /// If the pin is configured as an input pin, this function does nothing.
259    #[inline]
260    fn set_low(&mut self) -> Result<(), Self::Error> {
261        self.set_low();
262        Ok(())
263    }
264
265    /// If the pin is configured as an input pin, this function does nothing.
266    #[inline]
267    fn set_high(&mut self) -> Result<(), Self::Error> {
268        self.set_high();
269        Ok(())
270    }
271}
272
273impl embedded_hal::digital::StatefulOutputPin for Flex {
274    /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
275    /// of this function is undefined.
276    #[inline]
277    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
278        Ok(self.ll.is_set_high())
279    }
280
281    /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
282    /// of this function is undefined.
283    #[inline]
284    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
285        Ok(self.ll.is_set_low())
286    }
287}
288
289/// Push-Pull output pin.
290pub struct Output(LowLevelGpio);
291
292impl Output {
293    pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>, init_level: PinState) -> Self {
294        let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
295        low_level.configure_as_output_push_pull(init_level);
296        Self(low_level)
297    }
298
299    pub fn new_for_emio(pin: emio::EmioPin, init_level: PinState) -> Self {
300        let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
301        low_level.configure_as_output_push_pull(init_level);
302        Self(low_level)
303    }
304
305    #[inline]
306    pub fn set_low(&mut self) {
307        self.0.set_low();
308    }
309
310    #[inline]
311    pub fn set_high(&mut self) {
312        self.0.set_high();
313    }
314}
315
316impl embedded_hal::digital::ErrorType for Output {
317    type Error = Infallible;
318}
319
320impl embedded_hal::digital::OutputPin for Output {
321    fn set_low(&mut self) -> Result<(), Self::Error> {
322        self.0.set_low();
323        Ok(())
324    }
325    fn set_high(&mut self) -> Result<(), Self::Error> {
326        self.0.set_high();
327        Ok(())
328    }
329}
330
331impl embedded_hal::digital::StatefulOutputPin for Output {
332    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
333        Ok(self.0.is_set_high())
334    }
335
336    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
337        Ok(self.0.is_set_low())
338    }
339}
340
341/// Input pin.
342pub struct Input(LowLevelGpio);
343
344impl Input {
345    pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Result<Self, PinIsOutputOnly> {
346        let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
347        low_level.configure_as_input_floating()?;
348        Ok(Self(low_level))
349    }
350
351    pub fn new_for_emio(pin: emio::EmioPin) -> Result<Self, PinIsOutputOnly> {
352        let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
353        low_level.configure_as_input_floating()?;
354        Ok(Self(low_level))
355    }
356
357    pub fn is_high(&self) -> bool {
358        self.0.is_high()
359    }
360
361    pub fn is_low(&self) -> bool {
362        self.0.is_low()
363    }
364}
365
366impl embedded_hal::digital::ErrorType for Input {
367    type Error = Infallible;
368}
369
370impl embedded_hal::digital::InputPin for Input {
371    fn is_high(&mut self) -> Result<bool, Self::Error> {
372        Ok(self.0.is_high())
373    }
374
375    fn is_low(&mut self) -> Result<bool, Self::Error> {
376        Ok(self.0.is_low())
377    }
378}
379
380/// IO peripheral pin.
381pub struct IoPeriphPin {
382    pin: LowLevelGpio,
383    mux_conf: MuxConfig,
384}
385
386impl IoPeriphPin {
387    /// Constructor for IO peripheral pins where only the multiplexer and pullup configuration
388    /// need to be changed.
389    pub fn new(pin: impl MioPin, mux_conf: MuxConfig, pullup: Option<bool>) -> Self {
390        let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
391        low_level.configure_as_io_periph_pin(mux_conf, pullup);
392        Self {
393            pin: low_level,
394            mux_conf,
395        }
396    }
397
398    /// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
399    pub fn new_with_full_config(pin: impl MioPin, config: zynq7000::slcr::mio::Config) -> Self {
400        let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
401        low_level.set_mio_pin_config(config);
402        Self {
403            pin: low_level,
404            mux_conf: MuxConfig::new(
405                config.l0_sel(),
406                config.l1_sel(),
407                config.l2_sel(),
408                config.l3_sel(),
409            ),
410        }
411    }
412
413    /// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
414    pub fn new_with_full_config_and_unlocked_slcr(
415        pin: impl MioPin,
416        slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
417        config: zynq7000::slcr::mio::Config,
418    ) -> Self {
419        let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
420        low_level.set_mio_pin_config_with_unlocked_slcr(slcr, config);
421        Self {
422            pin: low_level,
423            mux_conf: MuxConfig::new(
424                config.l0_sel(),
425                config.l1_sel(),
426                config.l2_sel(),
427                config.l3_sel(),
428            ),
429        }
430    }
431}
432
433impl IoPinProvider for IoPeriphPin {
434    #[inline]
435    fn mode(&self) -> PinMode {
436        PinMode::MioIoPeriph(self.mux_conf)
437    }
438
439    #[inline]
440    fn offset(&self) -> PinOffset {
441        self.pin.offset()
442    }
443}