Skip to content

Commit 5cc34b3

Browse files
committed
Encoder: Improve driver, document.
1 parent f8d1257 commit 5cc34b3

File tree

3 files changed

+41
-15
lines changed

3 files changed

+41
-15
lines changed

v3/docs/DRIVERS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ behaviour.
866866

867867
The `Encoder` can be instantiated in such a way that its effective resolution
868868
can be reduced. A virtual encoder with lower resolution can be useful in some
869-
applications.
869+
applications. In particular it can track the "clicks" of a mechanical detent.
870870

871871
The driver allows limits to be assigned to the virtual encoder's value so that
872872
a dial running from (say) 0 to 100 may be implemented. If limits are used,
@@ -908,10 +908,10 @@ Constructor arguments:
908908
receives two integer args, `v` being the virtual encoder's current value and
909909
`delta` being the signed difference between the current value and the previous
910910
one. Further args may be appended by the following.
911-
9. `args=()` An optional tuple of positionl args for the callback.
911+
9. `args=()` An optional tuple of positional args for the callback.
912912
10. `delay=100` After motion is detected the driver waits for `delay` ms before
913-
reading the current position. A delay can be used to limit the rate at which
914-
the callback is invoked. This is a minimal approach. See
913+
reading the current position. A delay limits the rate at which the callback is
914+
invoked and improves debouncing. This is a minimal approach. See
915915
[this script](https://github.com/peterhinch/micropython-async/blob/master/v3/primitives/tests/encoder_stop.py)
916916
for a way to create a callback which runs only when the encoder stops moving.
917917

v3/docs/TUTORIAL.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,13 +1163,14 @@ async def foo(tsf): # Periodically set the ThreadSafeFlag
11631163
await asyncio.sleep(1)
11641164
tsf.set()
11651165

1166-
def ready(tsf, poller):
1167-
poller.register(tsf, POLLIN)
1166+
def ready(tsf, poller):
1167+
r = (tsf, POLLIN)
1168+
poller.register(*r)
11681169

1169-
def is_rdy():
1170-
return len([t for t in poller.ipoll(0) if t[0] is tsf]) > 0
1170+
def is_rdy():
1171+
return r in poller.ipoll(0)
11711172

1172-
return is_rdy
1173+
return is_rdy
11731174

11741175
async def test():
11751176
tsf = asyncio.ThreadSafeFlag()

v3/primitives/encoder.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# encoder.py Asynchronous driver for incremental quadrature encoder.
22

3-
# Copyright (c) 2021-2022 Peter Hinch
3+
# Copyright (c) 2021-2023 Peter Hinch
44
# Released under the MIT License (MIT) - see LICENSE file
55

6-
# For an explanation of the design please see
6+
# For an explanation of the design please see
77
# [ENCODERS.md](https://github.com/peterhinch/micropython-samples/blob/master/encoders/ENCODERS.md)
88

99
# Thanks are due to the following collaborators:
@@ -19,11 +19,33 @@
1919

2020
import uasyncio as asyncio
2121
from machine import Pin
22+
from select import poll, POLLIN
2223

23-
class Encoder:
2424

25-
def __init__(self, pin_x, pin_y, v=0, div=1, vmin=None, vmax=None,
26-
mod=None, callback=lambda a, b : None, args=(), delay=100):
25+
def ready(tsf, poller):
26+
r = (tsf, POLLIN)
27+
poller.register(*r)
28+
29+
def is_rdy():
30+
return r in poller.ipoll(0)
31+
32+
return is_rdy
33+
34+
35+
class Encoder:
36+
def __init__(
37+
self,
38+
pin_x,
39+
pin_y,
40+
v=0,
41+
div=1,
42+
vmin=None,
43+
vmax=None,
44+
mod=None,
45+
callback=lambda a, b: None,
46+
args=(),
47+
delay=100,
48+
):
2749
self._pin_x = pin_x
2850
self._pin_y = pin_y
2951
self._x = pin_x()
@@ -34,8 +56,9 @@ def __init__(self, pin_x, pin_y, v=0, div=1, vmin=None, vmax=None,
3456
self._trig = asyncio.Event()
3557

3658
if ((vmin is not None) and v < vmin) or ((vmax is not None) and v > vmax):
37-
raise ValueError('Incompatible args: must have vmin <= v <= vmax')
59+
raise ValueError("Incompatible args: must have vmin <= v <= vmax")
3860
self._tsf = asyncio.ThreadSafeFlag()
61+
self._tsf_ready = ready(self._tsf, poll()) # Create a ready function
3962
trig = Pin.IRQ_RISING | Pin.IRQ_FALLING
4063
try:
4164
xirq = pin_x.irq(trigger=trig, handler=self._x_cb, hard=True)
@@ -67,6 +90,8 @@ async def _run(self, vmin, vmax, div, mod, cb, args):
6790
plcv = pcv # Previous value after limits applied
6891
delay = self.delay
6992
while True:
93+
if delay > 0 and self._tsf_ready(): # Ensure ThreadSafeFlag is clear
94+
await self._tsf.wait()
7095
await self._tsf.wait()
7196
await asyncio.sleep_ms(delay) # Wait for motion to stop.
7297
hv = self._v # Sample hardware (atomic read).

0 commit comments

Comments
 (0)