1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use super::*;

/// Pin type with dynamic mode
///
/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
/// - `N` is pin number: from `0` to `15`.
pub struct DynamicPin<const P: char, const N: u8> {
    /// Current pin mode
    pub(crate) mode: Dynamic,
}

/// Tracks the current pin state for dynamic pins
pub enum Dynamic {
    /// Floating input mode
    InputFloating,
    /// Pull-up input mode
    InputPullUp,
    /// Pull-down input mode
    InputPullDown,
    /// Push-pull output mode
    OutputPushPull,
    /// Open-drain output mode
    OutputOpenDrain,
}

/// Error for [DynamicPin]
#[derive(Debug, PartialEq)]
pub enum PinModeError {
    /// For operations unsupported in current mode
    IncorrectMode,
}

impl Dynamic {
    /// Is pin in readable mode
    pub fn is_input(&self) -> bool {
        use Dynamic::*;
        match self {
            InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true,
            OutputPushPull => false,
        }
    }

    /// Is pin in writable mode
    pub fn is_output(&self) -> bool {
        use Dynamic::*;
        match self {
            InputFloating | InputPullUp | InputPullDown => false,
            OutputPushPull | OutputOpenDrain => true,
        }
    }
}

// For convertion simplify
struct Unknown;

impl crate::Sealed for Unknown {}
impl PinMode for Unknown {}

impl<const P: char, const N: u8> DynamicPin<P, N> {
    pub(super) const fn new(mode: Dynamic) -> Self {
        Self { mode }
    }

    /// Switch pin into pull-up input
    #[inline]
    pub fn make_pull_up_input(&mut self) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_pull_up_input();
        self.mode = Dynamic::InputPullUp;
    }
    /// Switch pin into pull-down input
    #[inline]
    pub fn make_pull_down_input(&mut self) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_pull_down_input();
        self.mode = Dynamic::InputPullDown;
    }
    /// Switch pin into floating input
    #[inline]
    pub fn make_floating_input(&mut self) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_floating_input();
        self.mode = Dynamic::InputFloating;
    }
    /// Switch pin into push-pull output
    #[inline]
    pub fn make_push_pull_output(&mut self) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_push_pull_output();
        self.mode = Dynamic::OutputPushPull;
    }
    /// Switch pin into push-pull output with required voltage state
    #[inline]
    pub fn make_push_pull_output_in_state(&mut self, state: PinState) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_push_pull_output_in_state(state);
        self.mode = Dynamic::OutputPushPull;
    }
    /// Switch pin into open-drain output
    #[inline]
    pub fn make_open_drain_output(&mut self) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_open_drain_output();
        self.mode = Dynamic::OutputOpenDrain;
    }
    /// Switch pin into open-drain output with required voltage state
    #[inline]
    pub fn make_open_drain_output_in_state(&mut self, state: PinState) {
        // NOTE(unsafe), we have a mutable reference to the current pin
        Pin::<P, N, Unknown>::new().into_open_drain_output_in_state(state);
        self.mode = Dynamic::OutputOpenDrain;
    }

    /// Drives the pin high
    pub fn set_high(&mut self) -> Result<(), PinModeError> {
        if self.mode.is_output() {
            Pin::<P, N, Unknown>::new()._set_state(PinState::High);
            Ok(())
        } else {
            Err(PinModeError::IncorrectMode)
        }
    }

    /// Drives the pin low
    pub fn set_low(&mut self) -> Result<(), PinModeError> {
        if self.mode.is_output() {
            Pin::<P, N, Unknown>::new()._set_state(PinState::Low);
            Ok(())
        } else {
            Err(PinModeError::IncorrectMode)
        }
    }

    /// Is the input pin high?
    pub fn is_high(&self) -> Result<bool, PinModeError> {
        self.is_low().map(|b| !b)
    }

    /// Is the input pin low?
    pub fn is_low(&self) -> Result<bool, PinModeError> {
        if self.mode.is_input() {
            Ok(Pin::<P, N, Unknown>::new()._is_low())
        } else {
            Err(PinModeError::IncorrectMode)
        }
    }
}