Skip to content

Commit 60547a7

Browse files
committed
encoder.py: improve tracking of detents.
1 parent 5c7da15 commit 60547a7

File tree

2 files changed

+33
-35
lines changed

2 files changed

+33
-35
lines changed

v3/docs/DRIVERS.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -442,21 +442,21 @@ Constructor arguments:
442442
as `Pin.IN` and have pullups.
443443
2. `pin_y` Ditto.
444444
3. `v=0` Initial value.
445-
4. `vmin=None` By default the `value` of the encoder can vary without limit.
446-
Optionally maximum and/or minimum limits can be set.
447-
5. `vmax=None` As above. If `vmin` and/or `vmax` are specified, a `ValueError`
448-
will be thrown if the initial value `v` does not conform with the limits.
449-
6. `div=1` A value > 1 causes the motion rate of the encoder to be divided
445+
4. `div=1` A value > 1 causes the motion rate of the encoder to be divided
450446
down, to produce a virtual encoder with lower resolution. This can enable
451447
tracking of mechanical detents - typical values are then 4 or 2 pulses per
452448
click.
453-
7. `callback=lambda a, b : None` Optional callback function. The callback
449+
5. `vmin=None` By default the `value` of the encoder can vary without limit.
450+
Optionally maximum and/or minimum limits can be set.
451+
6. `vmax=None` As above. If `vmin` and/or `vmax` are specified, a `ValueError`
452+
will be thrown if the initial value `v` does not conform with the limits.
453+
7. `mod=None` An integer `N > 0` causes the divided value to be reduced modulo
454+
`N` - useful for controlling rotary devices.
455+
8. `callback=lambda a, b : None` Optional callback function. The callback
454456
receives two integer args, `v` being the virtual encoder's current value and
455457
`delta` being the signed difference between the current value and the previous
456458
one. Further args may be appended by the following.
457-
8. `args=()` An optional tuple of positionl args for the callback.
458-
9. `mod=0` An integer `N > 0` causes the divided value to be reduced modulo
459-
`N` - useful for controlling rotary devices.
459+
9. `args=()` An optional tuple of positionl args for the callback.
460460

461461
Synchronous method:
462462
* `value` No args. Returns an integer being the virtual encoder's current

v3/primitives/encoder.py

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
from machine import Pin
88

99
class Encoder:
10-
delay = 100 # Pause (ms) for motion to stop
10+
delay = 100 # Pause (ms) for motion to stop/limit callback frequency
1111

12-
def __init__(self, pin_x, pin_y, v=0, vmin=None, vmax=None, div=1,
13-
callback=lambda a, b : None, args=(), mod=0):
12+
def __init__(self, pin_x, pin_y, v=0, div=1, vmin=None, vmax=None,
13+
mod=None, callback=lambda a, b : None, args=()):
1414
self._pin_x = pin_x
1515
self._pin_y = pin_y
1616
self._x = pin_x()
1717
self._y = pin_y()
18-
self._v = 0 # Hardware value always starts at 0
18+
self._v = v * div # Initialise hardware value
1919
self._cv = v # Current (divided) value
2020
if ((vmin is not None) and v < vmin) or ((vmax is not None) and v > vmax):
2121
raise ValueError('Incompatible args: must have vmin <= v <= vmax')
@@ -44,33 +44,31 @@ def _y_cb(self, pin_y):
4444
self._v -= 1 if y ^ self._pin_x() else -1
4545
self._tsf.set()
4646

47-
async def _run(self, vmin, vmax, div, modulo, cb, args):
47+
async def _run(self, vmin, vmax, div, mod, cb, args):
4848
pv = self._v # Prior hardware value
49-
cv = self._cv # Current divided value as passed to callback
50-
pcv = cv # Prior divided value passed to callback
49+
pcv = self._cv # Prior divided value passed to callback
50+
lcv = pcv # Current value after limits applied
51+
plcv = pcv # Previous value after limits applied
5152
delay = self.delay
5253
while True:
5354
await self._tsf.wait()
54-
await asyncio.sleep_ms(delay) # Wait for motion to stop
55-
new = self._v # Sample hardware (atomic read)
56-
a = new - pv # Hardware change
57-
# Ensure symmetrical bahaviour for + and - values
58-
q, r = divmod(abs(a), div)
59-
if a < 0:
60-
r = -r
61-
q = -q
62-
pv = new - r # Hardware value when local value was updated
63-
cv += q
64-
if vmax is not None:
65-
cv = min(cv, vmax)
66-
if vmin is not None:
67-
cv = max(cv, vmin)
68-
if modulo:
69-
cv %= modulo
70-
self._cv = cv # For value()
71-
if cv != pcv:
72-
cb(cv, cv - pcv, *args) # User CB in uasyncio context
55+
await asyncio.sleep_ms(delay) # Wait for motion to stop.
56+
hv = self._v # Sample hardware (atomic read).
57+
if hv == pv: # A change happened but was negated before
58+
continue # this got scheduled. Nothing to do.
59+
pv = hv
60+
cv = round(hv / div) # cv is divided value.
61+
if not (dv := cv - pcv): # dv is change in divided value.
62+
continue # No change
63+
lcv += dv # lcv: divided value with limits/mod applied
64+
lcv = lcv if vmax is None else min(vmax, lcv)
65+
lcv = lcv if vmin is None else max(vmin, lcv)
66+
lcv = lcv if mod is None else lcv % mod
67+
self._cv = lcv # update ._cv for .value() before CB.
68+
if lcv != plcv:
69+
cb(lcv, lcv - plcv, *args) # Run user CB in uasyncio context
7370
pcv = cv
71+
plcv = lcv
7472

7573
def value(self):
7674
return self._cv

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy