rotary_encoder_embedded/
angular_velocity.rs

1use embedded_hal::digital::InputPin;
2
3use crate::Direction;
4use crate::RotaryEncoder;
5
6/// Default angular velocity increasing factor
7const DEFAULT_VELOCITY_INC_FACTOR: f32 = 0.2;
8/// Default angular velocity decreasing factor
9const DEFAULT_VELOCITY_DEC_FACTOR: f32 = 0.01;
10/// Angular velocity action window duration in milliseconds
11const DEFAULT_VELOCITY_ACTION_MS: u64 = 25;
12/// Velocity type, the value is between 0.0 and 1.0
13pub type Velocity = f32;
14
15// For debouncing of pins, use 0x0f (b00001111) and 0x0c (b00001100) etc.
16const PIN_MASK: u8 = 0x03;
17const PIN_EDGE: u8 = 0x02;
18
19/// AngularVelocityMode
20/// Uses the full-step table with additional angular-velocity measurement
21pub struct AngularVelocityMode {
22    /// The pin state
23    pin_state: [u8; 2],
24    /// The instantaneous velocity
25    velocity: Velocity,
26    /// The increasing factor
27    velocity_inc_factor: f32,
28    /// The decreasing factor
29    velocity_dec_factor: f32,
30    /// The action window
31    velocity_action_ms: u64,
32    /// The last timestamp in mS
33    previous_time_millis: u64,
34}
35
36impl<DT, CLK> RotaryEncoder<AngularVelocityMode, DT, CLK>
37where
38    DT: InputPin,
39    CLK: InputPin,
40{
41    /// Set the velocity_inc_factor. How quickly the velocity increases to 1.0.
42    pub fn set_velocity_inc_factor(&mut self, inc_factor: f32) {
43        self.mode.velocity_inc_factor = inc_factor;
44    }
45
46    /// Set the velocity_dec_factor. How quickly the velocity decreases or cools-down
47    pub fn set_velocity_dec_factor(&mut self, dec_factor: f32) {
48        self.mode.velocity_dec_factor = dec_factor;
49    }
50
51    /// Set the velocity_action_ms. The window of duration (milliseconds) that the velocity will increase
52    pub fn set_velocity_action_ms(&mut self, action_ms: u64) {
53        self.mode.velocity_action_ms = action_ms;
54    }
55
56    /// This function should be called periodically, either via a timer or the main loop.
57    /// This function will reduce the angular velocity over time, the amount is configurable via the constructor
58    pub fn decay_velocity(&mut self) {
59        self.mode.velocity -= self.mode.velocity_dec_factor;
60        if self.mode.velocity < 0.0 {
61            self.mode.velocity = 0.0;
62        }
63    }
64
65    /// Update the state machine of the RotaryEncoder. This should be called ideally from an interrupt vector
66    /// when either the DT or CLK pins state changes. This function will update the RotaryEncoder's
67    /// Direction and current Angular Velocity.
68    /// * `current_time` - Current timestamp in ms (strictly monotonously increasing)
69    pub fn update(&mut self, current_time_millis: u64) -> Direction {
70        self.mode.update(
71            self.pin_dt.is_high().unwrap_or_default(),
72            self.pin_clk.is_high().unwrap_or_default(),
73            current_time_millis,
74        )
75    }
76
77    /// Returns the current angular velocity of the RotaryEncoder
78    /// The Angular Velocity is a value between 0.0 and 1.0
79    /// This is useful for incrementing/decrementing a value in an exponential fashion
80    pub fn velocity(&self) -> Velocity {
81        self.mode.velocity
82    }
83}
84
85impl AngularVelocityMode {
86    /// Initialises the AngularVelocityMode
87    pub fn new() -> Self {
88        Self {
89            pin_state: [0xFF, 2],
90            velocity: 0.0,
91            previous_time_millis: 0,
92            velocity_action_ms: DEFAULT_VELOCITY_ACTION_MS,
93            velocity_dec_factor: DEFAULT_VELOCITY_DEC_FACTOR,
94            velocity_inc_factor: DEFAULT_VELOCITY_INC_FACTOR,
95        }
96    }
97
98    /// Update to determine the direction
99    pub fn update(
100        &mut self,
101        dt_state: bool,
102        clk_state: bool,
103        current_time_millis: u64,
104    ) -> Direction {
105        self.pin_state[0] = (self.pin_state[0] << 1) | dt_state as u8;
106        self.pin_state[1] = (self.pin_state[1] << 1) | clk_state as u8;
107
108        let a = self.pin_state[0] & PIN_MASK;
109        let b = self.pin_state[1] & PIN_MASK;
110
111        let mut dir: Direction = Direction::None;
112
113        if a == PIN_EDGE && b == 0x00 {
114            dir = Direction::Anticlockwise;
115        } else if b == PIN_EDGE && a == 0x00 {
116            dir = Direction::Clockwise;
117        }
118
119        if dir != Direction::None {
120            if current_time_millis - self.previous_time_millis < self.velocity_action_ms
121                && self.velocity < 1.0
122            {
123                self.velocity += self.velocity_inc_factor;
124                if self.velocity > 1.0 {
125                    self.velocity = 1.0;
126                }
127            }
128        } else {
129            self.previous_time_millis = current_time_millis;
130        }
131
132        dir
133    }
134}
135
136impl Default for AngularVelocityMode {
137    fn default() -> Self {
138        Self::new()
139    }
140}
141
142impl<DT, CLK, MODE> RotaryEncoder<MODE, DT, CLK>
143where
144    DT: InputPin,
145    CLK: InputPin,
146{
147    /// Configure `RotaryEncoder` to use the AngularVelocityMode API
148    pub fn into_angular_velocity_mode(self) -> RotaryEncoder<AngularVelocityMode, DT, CLK> {
149        RotaryEncoder {
150            pin_dt: self.pin_dt,
151            pin_clk: self.pin_clk,
152            mode: AngularVelocityMode::new(),
153        }
154    }
155}