3
3
# Copyright (c) 2021 Peter Hinch
4
4
# Released under the MIT License (MIT) - see LICENSE file
5
5
6
- # This driver is intended for encoder-based control knobs. It is not
7
- # suitable for NC machine applications. Please see the docs.
6
+ # This driver is intended for encoder-based control knobs. It is
7
+ # unsuitable for NC machine applications. Please see the docs.
8
8
9
9
import uasyncio as asyncio
10
10
from machine import Pin
11
11
12
12
class Encoder :
13
- LATENCY = 50
13
+ delay = 100 # Pause (ms) for motion to stop
14
14
15
- def __init__ (self , pin_x , pin_y , v = 0 , vmin = None , vmax = None ,
15
+ def __init__ (self , pin_x , pin_y , v = 0 , vmin = None , vmax = None , div = 1 ,
16
16
callback = lambda a , b : None , args = ()):
17
17
self ._pin_x = pin_x
18
18
self ._pin_y = pin_y
19
- self ._v = v
19
+ self ._v = 0 # Hardware value always starts at 0
20
+ self ._cv = v # Current (divided) value
21
+ if ((vmin is not None ) and v < min ) or ((vmax is not None ) and v > vmax ):
22
+ raise ValueError ('Incompatible args: must have vmin <= v <= vmax' )
20
23
self ._tsf = asyncio .ThreadSafeFlag ()
24
+ trig = Pin .IRQ_RISING | Pin .IRQ_FALLING
21
25
try :
22
- xirq = pin_x .irq (trigger = Pin .IRQ_RISING | Pin .IRQ_FALLING , handler = self ._x_cb , hard = True )
23
- yirq = pin_y .irq (trigger = Pin .IRQ_RISING | Pin .IRQ_FALLING , handler = self ._y_cb , hard = True )
24
- except TypeError :
25
- xirq = pin_x .irq (trigger = Pin .IRQ_RISING | Pin .IRQ_FALLING , handler = self ._x_cb )
26
- yirq = pin_y .irq (trigger = Pin .IRQ_RISING | Pin .IRQ_FALLING , handler = self ._y_cb )
27
- asyncio .create_task (self ._run (vmin , vmax , callback , args ))
28
-
26
+ xirq = pin_x .irq (trigger = trig , handler = self ._x_cb , hard = True )
27
+ yirq = pin_y .irq (trigger = trig , handler = self ._y_cb , hard = True )
28
+ except TypeError : # hard arg is unsupported on some hosts
29
+ xirq = pin_x .irq (trigger = trig , handler = self ._x_cb )
30
+ yirq = pin_y .irq (trigger = trig , handler = self ._y_cb )
31
+ asyncio .create_task (self ._run (vmin , vmax , div , callback , args ))
29
32
30
33
# Hardware IRQ's
31
34
def _x_cb (self , pin ):
@@ -38,21 +41,32 @@ def _y_cb(self, pin):
38
41
self ._v += 1 if fwd else - 1
39
42
self ._tsf .set ()
40
43
41
- async def _run (self , vmin , vmax , cb , args ):
42
- pv = self ._v # Prior value
44
+ async def _run (self , vmin , vmax , div , cb , args ):
45
+ pv = self ._v # Prior hardware value
46
+ cv = self ._cv # Current divided value as passed to callback
47
+ pcv = cv # Prior divided value passed to callback
48
+ mod = 0
49
+ delay = self .delay
43
50
while True :
44
51
await self ._tsf .wait ()
45
- cv = self ._v # Current value
52
+ await asyncio .sleep_ms (delay ) # Wait for motion to stop
53
+ new = self ._v # Sample hardware (atomic read)
54
+ a = new - pv # Hardware change
55
+ # Ensure symmetrical bahaviour for + and - values
56
+ q , r = divmod (abs (a ), div )
57
+ if a < 0 :
58
+ r = - r
59
+ q = - q
60
+ pv = new - r # Hardware value when local value was updated
61
+ cv += q
46
62
if vmax is not None :
47
63
cv = min (cv , vmax )
48
64
if vmin is not None :
49
65
cv = max (cv , vmin )
50
- self ._v = cv
51
- #print(cv, pv)
52
- if cv != pv :
53
- cb (cv , cv - pv , * args ) # User CB in uasyncio context
54
- pv = cv
55
- await asyncio .sleep_ms (self .LATENCY )
66
+ self ._cv = cv # For value()
67
+ if cv != pcv :
68
+ cb (cv , cv - pcv , * args ) # User CB in uasyncio context
69
+ pcv = cv
56
70
57
71
def value (self ):
58
- return self ._v
72
+ return self ._cv
0 commit comments