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
148
149
150
151
152
#![doc(html_root_url = "https://docs.rs/rotary-encoder-hal/0.3.0")]
//! # rotary-encoder-hal
//!
//! A platform agnostic rotary encoder library
//!
//! Built using [`embedded-hal`] traits
//!
//! [`embedded-hal`]: https://docs.rs/embedded-hal/0.2

#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]

use either::Either;

#[cfg(not(feature = "embedded-hal-alpha"))]
use embedded_hal::digital::v2::InputPin;

#[cfg(feature = "embedded-hal-alpha")]
use embedded_hal_alpha::digital::InputPin;

/// Holds current/old state and both [`InputPin`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/digital/v2/trait.InputPin.html)
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Rotary<A, B> {
    pin_a: A,
    pin_b: B,
    state: u8,

    #[cfg(feature = "table-decoder")]
    prev_next: u8,
    #[cfg(feature = "table-decoder")]
    store: u16,
}

/// The encoder direction is either `Clockwise`, `CounterClockwise`, or `None`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Direction {
    /// A clockwise turn
    Clockwise,
    /// A counterclockwise turn
    CounterClockwise,
    /// No change
    None,
}

#[cfg(not(feature = "table-decoder"))]
impl From<u8> for Direction {
    fn from(s: u8) -> Self {
        match s {
            0b0001 | 0b0111 | 0b1000 | 0b1110 => Direction::Clockwise,
            0b0010 | 0b0100 | 0b1011 | 0b1101 => Direction::CounterClockwise,
            _ => Direction::None,
        }
    }
}

#[cfg(feature = "table-decoder")]
impl From<u16> for Direction {
    fn from(s: u16) -> Self {
        match s & 0x00ff {
            0x17 => Direction::Clockwise,
            0x2b => Direction::CounterClockwise,
            _ => Direction::None,
        }
    }
}

impl<A, B> Rotary<A, B>
where
    A: InputPin,
    B: InputPin,
{
    /// Accepts two [`InputPin`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/digital/v2/trait.InputPin.html)s, these will be read on every `update()`
    pub fn new(pin_a: A, pin_b: B) -> Self {
        Self {
            pin_a,
            pin_b,
            state: 0u8,

            #[cfg(feature = "table-decoder")]
            prev_next: 0u8,
            #[cfg(feature = "table-decoder")]
            store: 0u16,
        }
    }

    #[cfg(not(feature = "table-decoder"))]
    /// Call `update` to evaluate the next state of the encoder, propagates errors from `InputPin` read
    pub fn update(&mut self) -> Result<Direction, Either<A::Error, B::Error>> {
        // use mask to get previous state value
        let mut s = self.state & 0b11;

        #[cfg(not(feature = "embedded-hal-alpha"))]
        let (a_is_low, b_is_low) = (self.pin_a.is_low(), self.pin_b.is_low());
        #[cfg(feature = "embedded-hal-alpha")]
        let (a_is_low, b_is_low) = (self.pin_a.try_is_low(), self.pin_b.try_is_low());

        // move in the new state
        if a_is_low.map_err(Either::Left)? {
            s |= 0b100;
        }
        if b_is_low.map_err(Either::Right)? {
            s |= 0b1000;
        }
        // move new state in
        self.state = s >> 2;
        Ok(s.into())
    }

    /// Returns a reference to the first pin. Can be used to clear interrupt.
    pub fn pin_a(&mut self) -> &mut A {
        &mut self.pin_a
    }

    /// Returns a reference to the second pin. Can be used to clear interrupt.
    pub fn pin_b(&mut self) -> &mut B {
        &mut self.pin_b
    }

    #[cfg(feature = "table-decoder")]
    /// Call `update` to evaluate the next state of the encoder, propagates errors from `InputPin` read
    pub fn update(&mut self) -> Result<Direction, Either<A::Error, B::Error>> {
        #[cfg(not(feature = "embedded-hal-alpha"))]
        let (a_is_high, b_is_high) = (self.pin_a.is_high(), self.pin_b.is_low());
        #[cfg(feature = "embedded-hal-alpha")]
        let (a_is_high, b_is_high) = (self.pin_a.try_is_high(), self.pin_b.try_is_low());

        // Implemented after https://www.best-microcontroller-projects.com/rotary-encoder.html
        self.prev_next <<= 2;
        if a_is_high.map_err(Either::Left)? {
            self.prev_next |= 0x02;
        }
        if b_is_high.map_err(Either::Right)? {
            self.prev_next |= 0x01;
        }
        self.prev_next &= 0x0f;

        match self.prev_next {
            /*Invalid cases 0 | 3 | 5 | 6 | 9 | 10 | 12 | 15=>, */
            /*valid cases*/
            1 | 2 | 4 | 7 | 8 | 11 | 13 | 14 => {
                self.store <<= 4;
                self.store |= self.prev_next as u16;

                Ok(self.store.into())
            }
            _ => Ok(Direction::None),
        }
    }
}