kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Merge 0507a33203
into 4b37e6051e
commit
8dadb92881
|
@ -177,6 +177,64 @@ class InputEnc:
|
|||
return self._adj
|
||||
|
||||
|
||||
# Special mode where an encoder with a "press" pushbutton is the only control.
|
||||
# nxt and prev are Pin instances corresponding to encoder X and Y.
|
||||
# sel is a Pin for the encoder's pushbutton.
|
||||
# encoder is the division ratio.
|
||||
# Note that using a single click for adjust mode failed because the mode changed when
|
||||
# activating pushbuttons, checkboxes etc.
|
||||
class InputI2CEnc:
|
||||
def __init__(self, encoder):
|
||||
from gui.primitives import I2CEncoder
|
||||
|
||||
self._encoder = encoder # Encoder in use
|
||||
self._enc = I2CEncoder(encoder=encoder, callback=self.enc_cb)
|
||||
self._precision = False # Precision mode
|
||||
self._adj = False # Adjustment mode
|
||||
self._sel = Pushbutton(sel, suppress=True)
|
||||
self._sel.release_func(self.release) # Widgets are selected on release.
|
||||
self._sel.long_func(self.precision, (True,)) # Long press -> precision mode
|
||||
self._sel.double_func(self.adj_mode, (True,)) # Double press -> adjust mode
|
||||
|
||||
# Screen.adjust: adjust the value of a widget. In this case 1st button arg
|
||||
# is an int (discarded), val is the delta. (With button interface 1st arg
|
||||
# is the button, delta is +1 or -1).
|
||||
def enc_cb(self, position, delta): # Eencoder callback
|
||||
if self._adj:
|
||||
Screen.adjust(0, delta)
|
||||
else:
|
||||
Screen.ctrl_move(_NEXT if delta > 0 else _PREV)
|
||||
|
||||
def release(self):
|
||||
self.adj_mode(False) # Cancel adjust and precision
|
||||
Screen.sel_ctrl()
|
||||
|
||||
def precision(self, val): # Also called by Screen.ctrl_move to cancel mode
|
||||
if val:
|
||||
if not self._adj:
|
||||
self.adj_mode()
|
||||
self._precision = True
|
||||
else:
|
||||
self._precision = False
|
||||
Screen.redraw_co()
|
||||
|
||||
# If v is None, toggle adjustment mode. Bool sets or clears
|
||||
def adj_mode(self, v=None): # Set, clear or toggle adjustment mode
|
||||
self._adj = not self._adj if v is None else v
|
||||
if not self._adj:
|
||||
self._precision = False
|
||||
Screen.redraw_co() # Redraw curret object
|
||||
|
||||
def encoder(self):
|
||||
return self._encoder
|
||||
|
||||
def is_precision(self):
|
||||
return self._precision
|
||||
|
||||
def is_adjust(self):
|
||||
return self._adj
|
||||
|
||||
|
||||
# Wrapper for global ssd object providing framebuf compatible methods.
|
||||
# Must be subclassed: subclass provides input device and populates globals
|
||||
# display and ssd.
|
||||
|
@ -286,6 +344,10 @@ class Display(DisplayIP):
|
|||
global display, ssd
|
||||
ssd = objssd
|
||||
if incr is False: # Special encoder-only mode
|
||||
if not isinstance(encoder, (int, bool)):
|
||||
assert touch is False and nxt is None and sel is None and prev is None and decr is None, "Invalid args"
|
||||
ipdev = InputI2CEnc(encoder)
|
||||
else:
|
||||
ev = isinstance(encoder, int)
|
||||
assert ev and touch is False and decr is None and prev is not None, "Invalid args"
|
||||
ipdev = InputEnc(nxt, sel, prev, encoder)
|
||||
|
|
|
@ -28,6 +28,7 @@ _attrs = {
|
|||
"Pushbutton": "pushbutton",
|
||||
"ESP32Touch": "pushbutton",
|
||||
"Switch": "switch",
|
||||
"I2CEncoder": "i2cencoder",
|
||||
}
|
||||
|
||||
# Copied from uasyncio.__init__.py
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# 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-2024 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
|
||||
|
||||
# Now uses ThreadSafeFlag.clear()
|
||||
|
||||
import asyncio
|
||||
from machine import Pin
|
||||
|
||||
|
||||
class I2CEncoder:
|
||||
delay = 100 # Debounce/detent delay (ms)
|
||||
|
||||
def __init__(self, encoder, callback):
|
||||
self._v = 0 # Encoder value set by ISR
|
||||
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(div, callback))
|
||||
|
||||
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, div, cb):
|
||||
pv = 0 # Prior hardware value
|
||||
pcv = 0 # Prior divided value passed to callback
|
||||
while True:
|
||||
self._tsf.clear()
|
||||
await self._tsf.wait() # Wait for an edge
|
||||
await asyncio.sleep_ms(Encoder.delay) # Wait for motion/bounce 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 (cv - pcv) != 0: # dv is change in divided value.
|
||||
cb(cv, cv - pcv) # Run user CB in uasyncio context
|
||||
pcv = cv
|
|
@ -0,0 +1,61 @@
|
|||
# hardware_setup.py Customise for your hardware config
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa
|
||||
|
||||
# Supports:
|
||||
# Waveshare Pico LCD 1.14" 135*240(Pixel) based on ST7789V
|
||||
# https://www.waveshare.com/wiki/Pico-LCD-1.14
|
||||
# https://www.waveshare.com/pico-lcd-1.14.htm
|
||||
|
||||
from machine import Pin, SPI, I2C
|
||||
import i2cEncoderLibV2
|
||||
import gc
|
||||
from drivers.st7789.st7789_4bit import *
|
||||
SSD = ST7789
|
||||
|
||||
SPI_CHANNEL = 0
|
||||
SPI_SCK = 2
|
||||
SPI_MOSI = 3
|
||||
SPI_CS = 5
|
||||
SPI_DC = 4
|
||||
SPI_RST = 0
|
||||
SPI_BAUD = 30_000_000
|
||||
DISPLAY_BACKLIGHT = 1
|
||||
I2C_CHANNEL = 1
|
||||
I2C_SDA = 18
|
||||
I2C_SCL = 19
|
||||
I2C_INTERRUPT = 22
|
||||
I2C_ENCODER_ADDRESS = 0x50
|
||||
|
||||
mode = LANDSCAPE
|
||||
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
# Conservative low baudrate. Can go to 62.5MHz.
|
||||
spi = SPI(SPI_CHANNEL, SPI_BAUD, sck=Pin(SPI_SCK), mosi=Pin(SPI_MOSI), miso=None)
|
||||
pcs = Pin(SPI_CS, Pin.OUT, value=1)
|
||||
prst = Pin(SPI_RST, Pin.OUT, value=1)
|
||||
pbl = Pin(DISPLAY_BACKLIGHT, Pin.OUT, value=1)
|
||||
pdc = Pin(SPI_DC, Pin.OUT, value=0)
|
||||
|
||||
portrait = mode & PORTRAIT
|
||||
ssd = SSD(spi, height=240, width=320, dc=pdc, cs=pcs, rst=prst, disp_mode=mode, display=PI_PICO_LCD_2)
|
||||
|
||||
# Setup the Interrupt Pin from the encoder.
|
||||
INT_pin = Pin(I2C_INTERRUPT, Pin.IN, Pin.PULL_UP)
|
||||
|
||||
# Initialize the device.
|
||||
i2c = I2C(I2C_CHANNEL, scl=Pin(I2C_SCL), sda=Pin(I2C_SDA))
|
||||
|
||||
encconfig = (i2cEncoderLibV2.INT_DATA | i2cEncoderLibV2.WRAP_ENABLE
|
||||
| i2cEncoderLibV2.DIRE_RIGHT | i2cEncoderLibV2.IPUP_ENABLE
|
||||
| i2cEncoderLibV2.RMOD_X1 | i2cEncoderLibV2.RGB_ENCODER)
|
||||
|
||||
encoder = i2cEncoderLibV2.i2cEncoderLibV2(i2c, I2C_ENCODER_ADDRESS)
|
||||
encoder.reset()
|
||||
|
||||
# Create and export a Display instance
|
||||
from gui.core.ugui import Display
|
||||
|
||||
# I2cEncoder Rotary w/ Button only
|
||||
display = Display(ssd, None, None, None, False, None, encoder) # Encoder
|
Ładowanie…
Reference in New Issue