kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Minimal encoder.py
rodzic
5fea5d948c
commit
95a8d3abcb
|
@ -1,4 +1,6 @@
|
||||||
# encoder.py Asynchronous driver for incremental quadrature encoder.
|
# encoder.py Asynchronous driver for incremental quadrature encoder.
|
||||||
|
# This is minimised for micro-gui. Derived from
|
||||||
|
# https://github.com/peterhinch/micropython-async/blob/master/v3/primitives/encoder.py
|
||||||
|
|
||||||
# Copyright (c) 2021-2023 Peter Hinch
|
# Copyright (c) 2021-2023 Peter Hinch
|
||||||
# Released under the MIT License (MIT) - see LICENSE file
|
# Released under the MIT License (MIT) - see LICENSE file
|
||||||
|
@ -24,29 +26,14 @@ def ready(tsf, poller):
|
||||||
|
|
||||||
|
|
||||||
class Encoder:
|
class Encoder:
|
||||||
def __init__(
|
delay = 100 # Debounce/detent delay (ms)
|
||||||
self,
|
|
||||||
pin_x,
|
def __init__(self, pin_x, pin_y, div, callback):
|
||||||
pin_y,
|
|
||||||
v=0,
|
|
||||||
div=1,
|
|
||||||
vmin=None,
|
|
||||||
vmax=None,
|
|
||||||
mod=None,
|
|
||||||
callback=lambda a, b: None,
|
|
||||||
args=(),
|
|
||||||
delay=100,
|
|
||||||
):
|
|
||||||
self._pin_x = pin_x
|
self._pin_x = pin_x
|
||||||
self._pin_y = pin_y
|
self._pin_y = pin_y
|
||||||
self._x = pin_x()
|
self._x = pin_x()
|
||||||
self._y = pin_y()
|
self._y = pin_y()
|
||||||
self._v = v * div # Initialise hardware value
|
self._v = 0 # Encoder value set by ISR
|
||||||
self._cv = v # Current (divided) value
|
|
||||||
self.delay = delay # Pause (ms) for motion to stop/limit callback frequency
|
|
||||||
|
|
||||||
if ((vmin is not None) and v < vmin) or ((vmax is not None) and v > vmax):
|
|
||||||
raise ValueError("Incompatible args: must have vmin <= v <= vmax")
|
|
||||||
self._tsf = asyncio.ThreadSafeFlag()
|
self._tsf = asyncio.ThreadSafeFlag()
|
||||||
self._tsf_ready = ready(self._tsf, poll()) # Create a ready function
|
self._tsf_ready = ready(self._tsf, poll()) # Create a ready function
|
||||||
trig = Pin.IRQ_RISING | Pin.IRQ_FALLING
|
trig = Pin.IRQ_RISING | Pin.IRQ_FALLING
|
||||||
|
@ -56,11 +43,8 @@ class Encoder:
|
||||||
except TypeError: # hard arg is unsupported on some hosts
|
except TypeError: # hard arg is unsupported on some hosts
|
||||||
xirq = pin_x.irq(trigger=trig, handler=self._x_cb)
|
xirq = pin_x.irq(trigger=trig, handler=self._x_cb)
|
||||||
yirq = pin_y.irq(trigger=trig, handler=self._y_cb)
|
yirq = pin_y.irq(trigger=trig, handler=self._y_cb)
|
||||||
asyncio.create_task(self._run(vmin, vmax, div, mod, callback, args))
|
asyncio.create_task(self._run(div, callback))
|
||||||
|
|
||||||
# Hardware IRQ's. Duration 36μs on Pyboard 1 ~50μs on ESP32.
|
|
||||||
# IRQ latency: 2nd edge may have occured by the time ISR runs, in
|
|
||||||
# which case there is no movement.
|
|
||||||
def _x_cb(self, pin_x):
|
def _x_cb(self, pin_x):
|
||||||
if (x := pin_x()) != self._x:
|
if (x := pin_x()) != self._x:
|
||||||
self._x = x
|
self._x = x
|
||||||
|
@ -73,33 +57,19 @@ class Encoder:
|
||||||
self._v -= 1 if y ^ self._pin_x() else -1
|
self._v -= 1 if y ^ self._pin_x() else -1
|
||||||
self._tsf.set()
|
self._tsf.set()
|
||||||
|
|
||||||
async def _run(self, vmin, vmax, div, mod, cb, args):
|
async def _run(self, div, cb):
|
||||||
pv = self._v # Prior hardware value
|
pv = 0 # Prior hardware value
|
||||||
pcv = self._cv # Prior divided value passed to callback
|
pcv = 0 # Prior divided value passed to callback
|
||||||
lcv = pcv # Current value after limits applied
|
|
||||||
plcv = pcv # Previous value after limits applied
|
|
||||||
delay = self.delay
|
|
||||||
while True:
|
while True:
|
||||||
if self._tsf_ready(): # Ensure ThreadSafeFlag is clear
|
if self._tsf_ready(): # Ensure ThreadSafeFlag is clear
|
||||||
await self._tsf.wait()
|
await self._tsf.wait()
|
||||||
await self._tsf.wait() # Wait for an edge
|
await self._tsf.wait() # Wait for an edge
|
||||||
await asyncio.sleep_ms(delay) # Wait for motion/bounce to stop.
|
await asyncio.sleep_ms(Encoder.delay) # Wait for motion/bounce to stop.
|
||||||
hv = self._v # Sample hardware (atomic read).
|
hv = self._v # Sample hardware (atomic read).
|
||||||
if hv == pv: # A change happened but was negated before
|
if hv == pv: # A change happened but was negated before
|
||||||
continue # this got scheduled. Nothing to do.
|
continue # this got scheduled. Nothing to do.
|
||||||
pv = hv
|
pv = hv
|
||||||
cv = round(hv / div) # cv is divided value.
|
cv = round(hv / div) # cv is divided value.
|
||||||
if not (dv := cv - pcv): # dv is change in divided value.
|
if (cv - pcv) != 0: # dv is change in divided value.
|
||||||
continue # No change
|
cb(cv, cv - pcv) # Run user CB in uasyncio context
|
||||||
lcv += dv # lcv: divided value with limits/mod applied
|
pcv = cv
|
||||||
lcv = lcv if vmax is None else min(vmax, lcv)
|
|
||||||
lcv = lcv if vmin is None else max(vmin, lcv)
|
|
||||||
lcv = lcv if mod is None else lcv % mod
|
|
||||||
self._cv = lcv # update ._cv for .value() before CB.
|
|
||||||
if lcv != plcv:
|
|
||||||
cb(lcv, lcv - plcv, *args) # Run user CB in uasyncio context
|
|
||||||
pcv = cv
|
|
||||||
plcv = lcv
|
|
||||||
|
|
||||||
def value(self):
|
|
||||||
return self._cv
|
|
||||||
|
|
Ładowanie…
Reference in New Issue