lilygo t embed带编码器,研究一下驱动,以为这玩意挺简单,结果直接给我绕蒙了,算了,直接去github抄作业吧。。
第一款,精简款,直接转编码器,读书加减,比较方便,如果转太猛会报一个队列错误,不过不影响使用,用try包住即可,不包也不影响使用的,采用中断,不会阻塞,挺好。
库:
import machine
import utime as time
from machine import Pin
import micropython
class Rotary:
ROT_CW = 1
ROT_CCW = 2
SW_PRESS = 4
SW_RELEASE = 8
def __init__(self,dt,clk,sw):
self.dt_pin = Pin(dt, Pin.IN)
self.clk_pin = Pin(clk, Pin.IN)
self.sw_pin = Pin(sw, Pin.IN)
self.last_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.handlers = []
self.last_button_status = self.sw_pin.value()
def rotary_change(self, pin):
new_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
if new_status == self.last_status:
return
transition = (self.last_status << 2) | new_status
try:
if transition == 0b1110:
micropython.schedule(self.call_handlers, Rotary.ROT_CW)
elif transition == 0b1101:
micropython.schedule(self.call_handlers, Rotary.ROT_CCW)
except:
pass
self.last_status = new_status
def switch_detect(self,pin):
if self.last_button_status == self.sw_pin.value():
return
self.last_button_status = self.sw_pin.value()
if self.sw_pin.value():
micropython.schedule(self.call_handlers, Rotary.SW_RELEASE)
else:
micropython.schedule(self.call_handlers, Rotary.SW_PRESS)
def add_handler(self, handler):
self.handlers.append(handler)
def call_handlers(self, type):
for handler in self.handlers:
handler(type)
MAIN:
from rotary import Rotary
import utime as time
rotary = Rotary(2,1,0)
val = 0
def rotary_changed(change):
global val
if change == Rotary.ROT_CW:
val = val + 1
print(val)
elif change == Rotary.ROT_CCW:
val = val - 1
print(val)
elif change == Rotary.SW_PRESS:
print('PRESS')
elif change == Rotary.SW_RELEASE:
print('RELEASE')
rotary.add_handler(rotary_changed)
while True:
time.sleep(999)
第二款,是mpy源码工程师之一写的,大佬搞得东西就是不一样,很细,可以根据时间间隔判断走了多少步,也可以设置上下限之类的,感兴趣的自己去研究,不是很难:
# encoder.py Asynchronous driver for incremental quadrature encoder.
# Copyright (c) 2021-2022 Peter Hinch
# Released under the MIT License (MIT) - see LICENSE file
# Thanks are due to @ilium007 for identifying the issue of tracking detents,
# https://github.com/peterhinch/micropython-async/issues/82.
# Also to Mike Teachman (@miketeachman) for design discussions and testing
# against a state table design
# https://github.com/miketeachman/micropython-rotary/blob/master/rotary.py
import uasyncio as asyncio
from machine import Pin
class Encoder:
def __init__(self, pin_x, pin_y, v=0, div=1, vmin=None, vmax=None,
mod=None, callback=lambda a, b : None, args=(), delay=20):
self._pin_x = pin_x
self._pin_y = pin_y
self._x = pin_x()
self._y = pin_y()
self._v = v * div # Initialise hardware value
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()
trig = Pin.IRQ_RISING | Pin.IRQ_FALLING
try:
xirq = pin_x.irq(trigger=trig, handler=self._x_cb, hard=True)
yirq = pin_y.irq(trigger=trig, handler=self._y_cb, hard=True)
except TypeError: # hard arg is unsupported on some hosts
xirq = pin_x.irq(trigger=trig, handler=self._x_cb)
yirq = pin_y.irq(trigger=trig, handler=self._y_cb)
asyncio.create_task(self._run(vmin, vmax, div, mod, callback, args))
# 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):
if (x := pin_x()) != self._x:
self._x = x
self._v += 1 if x ^ self._pin_y() else -1
self._tsf.set()
def _y_cb(self, pin_y):
if (y := pin_y()) != self._y:
self._y = y
self._v -= 1 if y ^ self._pin_x() else -1
self._tsf.set()
async def _run(self, vmin, vmax, div, mod, cb, args):
pv = self._v # Prior hardware value
pcv = self._cv # 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:
await self._tsf.wait()
await asyncio.sleep_ms(delay) # Wait for motion to stop.
hv = self._v # Sample hardware (atomic read).
if hv == pv: # A change happened but was negated before
continue # this got scheduled. Nothing to do.
pv = hv
cv = round(hv / div) # cv is divided value.
if not (dv := cv - pcv): # dv is change in divided value.
continue # No change
lcv += dv # lcv: divided value with limits/mod applied
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
MAIN:
# encoder_test.py Test for asynchronous driver for incremental quadrature encoder.
# Copyright (c) 2021-2022 Peter Hinch
# Released under the MIT License (MIT) - see LICENSE file
from machine import Pin
import uasyncio as asyncio
import encoder
px = Pin(1, Pin.IN, Pin.PULL_UP)
py = Pin(2, Pin.IN, Pin.PULL_UP)
def cb(pos, delta):
print(pos, delta)
async def main():
while True:
await asyncio.sleep(1)
def test():
print('Running encoder test. Press ctrl-c to teminate.')
enc = encoder.Encoder(px, py, v=5, vmin=0, vmax=100, callback=cb)
try:
asyncio.run(main())
except KeyboardInterrupt:
print('Interrupted')
finally:
asyncio.new_event_loop()
test()