embtk_rotary_encoder/
lib.rs

1/*
2   Copyright 2019 Ilya Epifanov
3
4   Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5   http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6   http://opensource.org/licenses/MIT>, at your option. This file may not be
7   copied, modified, or distributed except according to those terms.
8*/
9/*!
10A helper for handling rotary encoder input, mainly for use on embedded platforms.
11
12```
13use embtk_rotary_encoder::RotaryEncoder;
14
15# fn main() {
16// The `u16` type will be accepted as a raw position output from your QEI peripheral
17let mut enc: RotaryEncoder<u16, _, _> =
18// `4i8` below is the number of raw divisions per full physical division.
19// `10u32` is the timeout in any kind of ticks you like.
20// You'll supply a timestamp every time you receive an even from a peripheral.
21    RotaryEncoder::new(4i8, 10u32);
22
23assert_eq!(enc.get_delta(65534u16, 1), 0); // we haven't moved full 4 divisions yet
24assert_eq!(enc.get_delta(65532u16, 2), -1); // full 4 divisions down => 1 logical division down
25
26assert_eq!(enc.get_delta(65530u16, 3), 0);
27assert_eq!(enc.get_delta(65528u16, 20), 0); // too late, read about timeouts below
28# }
29```
30
31A note about timeout:
32
33Sometimes you may lose an event from a peripheral or even a peripheral might be buggy.
34In this case you'll end up slightly off grid, i.e. you'll see the transition to a next
35division not when you feel the tactile feedback from an encoder but somewhere between the positions.
36
37To remedy that, there's a timeout which, on expiry, makes a current position of an encoder a
38reference for subsequent moves.
39
40*/
41#![no_std]
42
43use core::default::Default;
44
45use num_traits::Num;
46use num_traits::WrappingAdd;
47use num_traits::WrappingSub;
48use num_traits::Bounded;
49use num_traits::AsPrimitive;
50use num_traits::CheckedSub;
51use num_traits::Unsigned;
52use num_traits::Signed;
53
54pub struct RotaryEncoder<Pos, Tick, Delta> where
55    Pos: Num + WrappingAdd + WrappingSub + Bounded + Copy + PartialOrd + AsPrimitive<Delta> + Default,
56    Tick: Unsigned + Bounded + Copy + PartialOrd + CheckedSub + Default,
57    Delta: Signed + Copy + AsPrimitive<Pos>,
58{
59    last_active: Tick,
60    last_effective_raw_position: Pos,
61    last_real_raw_position: Pos,
62    reset_timeout: Tick,
63    div: Delta,
64}
65
66impl<Pos, Tick, Delta> RotaryEncoder<Pos, Tick, Delta> where
67    Pos: Num + WrappingAdd + WrappingSub + Bounded + Copy + PartialOrd + AsPrimitive<Delta> + Default,
68    Tick: Unsigned + Bounded + Copy + PartialOrd + CheckedSub + Default,
69    Delta: Signed + Copy + AsPrimitive<Pos>,
70{
71    pub fn new(div: Delta, reset_timeout: Tick) -> Self {
72        RotaryEncoder {
73            div,
74            last_active: Default::default(),
75            last_effective_raw_position: Default::default(),
76            last_real_raw_position: Default::default(),
77            reset_timeout,
78        }
79    }
80
81    pub fn get_delta(&mut self, raw_position: Pos, ts: Tick) -> Delta where
82    {
83        if (self.last_active + self.reset_timeout).checked_sub(&ts) == None {
84            self.last_effective_raw_position = self.last_real_raw_position;
85        }
86
87        let delta: Delta = raw_position.wrapping_sub(&self.last_effective_raw_position).as_();
88
89        let divisions = delta / self.div;
90        let remainder = delta % self.div;
91
92        self.last_effective_raw_position = self.last_effective_raw_position.wrapping_add(&(delta - remainder).as_());
93        if self.last_real_raw_position != raw_position {
94            self.last_active = ts;
95            self.last_real_raw_position = raw_position;
96        }
97        divisions
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use self::super::*;
104
105    #[test]
106    fn zero() {
107        let mut enc = RotaryEncoder::new(1i8, 10u32);
108        assert_eq!(enc.get_delta(0, 1), 0);
109    }
110
111    #[test]
112    fn increment() {
113        let mut enc = RotaryEncoder::new(1i8, 10u32);
114        assert_eq!(enc.get_delta(1, 1), 1);
115    }
116
117    #[test]
118    fn decrement() {
119        let mut enc = RotaryEncoder::new(1i8, 10u32);
120        assert_eq!(enc.get_delta(-1, 1), -1);
121    }
122
123    #[test]
124    fn rollover_up() {
125        let mut enc = RotaryEncoder::new(1i8, 10u32);
126
127        assert_eq!(enc.get_delta(127, 1), 127);
128        assert_eq!(enc.get_delta(-128, 1), 1);
129    }
130
131    #[test]
132    fn rollover_down() {
133        let mut enc = RotaryEncoder::new(1i8, 10u32);
134
135        assert_eq!(enc.get_delta(-128, 1), -128);
136        assert_eq!(enc.get_delta(127, 1), -1);
137    }
138
139    #[test]
140    fn all_the_way_up() {
141        let mut enc = RotaryEncoder::new(1i8, 10u32);
142
143        for p in 1..512 {
144            assert_eq!(enc.get_delta((p % 256) as i8, 1), 1);
145        }
146    }
147
148    #[test]
149    fn all_the_way_down() {
150        let mut enc = RotaryEncoder::new(1i8, 10u32);
151
152        for p in -1..-512 {
153            assert_eq!(enc.get_delta((p % 256) as i8, 1), 1);
154        }
155    }
156
157    #[test]
158    fn increment_4divs() {
159        let mut enc = RotaryEncoder::new(4i8, 10u32);
160
161        assert_eq!(enc.get_delta(1, 1), 0);
162        assert_eq!(enc.get_delta(2, 1), 0);
163        assert_eq!(enc.get_delta(3, 1), 0);
164        assert_eq!(enc.get_delta(4, 1), 1);
165    }
166
167    #[test]
168    fn decrement_4divs() {
169        let mut enc = RotaryEncoder::new(4i8, 10u32);
170        assert_eq!(enc.get_delta(-1, 1), 0);
171        assert_eq!(enc.get_delta(-2, 1), 0);
172        assert_eq!(enc.get_delta(-3, 1), 0);
173        assert_eq!(enc.get_delta(-4, 1), -1);
174    }
175
176    #[test]
177    fn rollover_up_4divs() {
178        let mut enc = RotaryEncoder::new(4i8, 10u32);
179
180        assert_eq!(enc.get_delta(124, 1), 31);
181        assert_eq!(enc.get_delta(127, 1), 0);
182        assert_eq!(enc.get_delta(-128, 1), 1);
183        assert_eq!(enc.get_delta(-127, 1), 0);
184        assert_eq!(enc.get_delta(-126, 1), 0);
185        assert_eq!(enc.get_delta(-125, 1), 0);
186        assert_eq!(enc.get_delta(-124, 1), 1);
187    }
188
189    #[test]
190    fn rollover_down_4divs() {
191        let mut enc = RotaryEncoder::new(4i8, 10u32);
192
193        assert_eq!(enc.get_delta(-128, 1), -32);
194        assert_eq!(enc.get_delta(127, 1), 0);
195        assert_eq!(enc.get_delta(126, 1), 0);
196        assert_eq!(enc.get_delta(125, 1), 0);
197        assert_eq!(enc.get_delta(124, 1), -1);
198    }
199
200    #[test]
201    fn all_the_way_up_4divs() {
202        let mut enc = RotaryEncoder::new(4i8, 10u32);
203
204        for p in 1..512 {
205            assert_eq!(enc.get_delta((p % 256) as i8, 1), if p % 4 == 0 { 1 } else { 0 });
206        }
207    }
208
209    #[test]
210    fn all_the_way_down_4divs() {
211        let mut enc = RotaryEncoder::new(4i8, 10u32);
212
213        for p in -1..-512 {
214            assert_eq!(enc.get_delta((p % 256) as i8, 1), if p % 4 == 0 { -1 } else { 0 });
215        }
216    }
217
218    #[test]
219    fn zero_unsigned() {
220        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
221        assert_eq!(enc.get_delta(0, 1), 0);
222    }
223
224    #[test]
225    fn increment_unsigned() {
226        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
227        assert_eq!(enc.get_delta(1, 1), 1);
228    }
229
230    #[test]
231    fn decrement_unsigned() {
232        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
233        assert_eq!(enc.get_delta(255, 1), -1);
234    }
235
236    #[test]
237    fn rollover_up_unsigned() {
238        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
239
240        assert_eq!(enc.get_delta(127, 1), 127);
241        assert_eq!(enc.get_delta(128, 1), 1);
242        assert_eq!(enc.get_delta(255, 1), 127);
243        assert_eq!(enc.get_delta(0, 1), 1);
244    }
245
246    #[test]
247    fn rollover_down_unsigned() {
248        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
249
250        assert_eq!(enc.get_delta(255, 1), -1);
251    }
252
253    #[test]
254    fn all_the_way_up_unsigned() {
255        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
256
257        for p in 1..512 {
258            assert_eq!(enc.get_delta((p % 256) as u8, 1), 1);
259        }
260    }
261
262    #[test]
263    fn all_the_way_down_unsigned() {
264        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(1i8, 10u32);
265
266        for p in -1..-512 {
267            assert_eq!(enc.get_delta((p % 256) as u8, 1), 1);
268        }
269    }
270
271    #[test]
272    fn increment_4divs_unsigned() {
273        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
274
275        assert_eq!(enc.get_delta(1, 1), 0);
276        assert_eq!(enc.get_delta(2, 1), 0);
277        assert_eq!(enc.get_delta(3, 1), 0);
278        assert_eq!(enc.get_delta(4, 1), 1);
279    }
280
281    #[test]
282    fn decrement_4divs_unsigned() {
283        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
284        assert_eq!(enc.get_delta(255, 1), 0);
285        assert_eq!(enc.get_delta(254, 1), 0);
286        assert_eq!(enc.get_delta(253, 1), 0);
287        assert_eq!(enc.get_delta(252, 1), -1);
288    }
289
290    #[test]
291    fn rollover_up_4divs_unsigned() {
292        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
293
294        assert_eq!(enc.get_delta(127, 1), 31);
295        assert_eq!(enc.get_delta(128, 1), 1);
296        assert_eq!(enc.get_delta(252, 1), 31);
297        assert_eq!(enc.get_delta(253, 1), 0);
298        assert_eq!(enc.get_delta(254, 1), 0);
299        assert_eq!(enc.get_delta(255, 1), 0);
300        assert_eq!(enc.get_delta(0, 1), 1);
301        assert_eq!(enc.get_delta(1, 1), 0);
302        assert_eq!(enc.get_delta(2, 1), 0);
303        assert_eq!(enc.get_delta(3, 1), 0);
304        assert_eq!(enc.get_delta(4, 1), 1);
305    }
306
307    #[test]
308    fn rollover_down_4divs_unsigned() {
309        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
310
311        assert_eq!(enc.get_delta(255, 1), 0);
312        assert_eq!(enc.get_delta(254, 1), 0);
313        assert_eq!(enc.get_delta(253, 1), 0);
314        assert_eq!(enc.get_delta(252, 1), -1);
315    }
316
317    #[test]
318    fn all_the_way_up_4divs_unsigned() {
319        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
320
321        for p in 1..512 {
322            assert_eq!(enc.get_delta((p % 256) as u8, 1), if p % 4 == 0 { 1 } else { 0 });
323        }
324    }
325
326    #[test]
327    fn all_the_way_down_4divs_unsigned() {
328        let mut enc: RotaryEncoder<u8, _, _> = RotaryEncoder::new(4i8, 10u32);
329
330        for p in -1..-512 {
331            assert_eq!(enc.get_delta((p % 256) as u8, 1), if p % 4 == 0 { -1 } else { 0 });
332        }
333    }
334}