kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Some doc for encoder mode. Remove unwanted code/comments from demos.
rodzic
b8d8bdb0a3
commit
3a64bb5691
17
README.md
17
README.md
|
@ -19,6 +19,9 @@ to a wide range of displays. It is also portable between hosts.
|
||||||
![Image](./images/ttgo.JPG) TTGO T-Display. Add a joystick switch and an SIL
|
![Image](./images/ttgo.JPG) TTGO T-Display. Add a joystick switch and an SIL
|
||||||
resistor for a simple, inexpensive, WiFi capable system.
|
resistor for a simple, inexpensive, WiFi capable system.
|
||||||
|
|
||||||
|
An alternative interface consists of two pushbuttons and an encoder such as
|
||||||
|
[this one](https://www.adafruit.com/product/377).
|
||||||
|
|
||||||
# Rationale
|
# Rationale
|
||||||
|
|
||||||
Touch GUI's have many advantages, however they have drawbacks, principally cost
|
Touch GUI's have many advantages, however they have drawbacks, principally cost
|
||||||
|
@ -115,7 +118,7 @@ of some display drivers.
|
||||||
22.3.1 [Class Curve](./README.md#2231-class-curve)
|
22.3.1 [Class Curve](./README.md#2231-class-curve)
|
||||||
22.3.2 [Class PolarCurve](./README.md#2232-class-polarcurve)
|
22.3.2 [Class PolarCurve](./README.md#2232-class-polarcurve)
|
||||||
22.4 [Class TSequence](./README.md#224-class-tsequence) Plotting realtime, time sequential data.
|
22.4 [Class TSequence](./README.md#224-class-tsequence) Plotting realtime, time sequential data.
|
||||||
[Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, use of graphics primitives
|
[Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, encoder interface, use of graphics primitives
|
||||||
|
|
||||||
# 1. Basic concepts
|
# 1. Basic concepts
|
||||||
|
|
||||||
|
@ -385,6 +388,8 @@ files in `gui/core` are:
|
||||||
The `gui/primitives` directory contains the following files:
|
The `gui/primitives` directory contains the following files:
|
||||||
* `switch.py` Interface to physical pushbuttons.
|
* `switch.py` Interface to physical pushbuttons.
|
||||||
* `delay_ms.py` A software triggerable timer.
|
* `delay_ms.py` A software triggerable timer.
|
||||||
|
* `encoder.py` Driver for a quadrature encoder. This offers an alternative
|
||||||
|
interface - see [Appendix 1](./README.md#appendix-1-application-design).
|
||||||
|
|
||||||
The `gui/demos` directory contains a variety of demos and tests described
|
The `gui/demos` directory contains a variety of demos and tests described
|
||||||
below.
|
below.
|
||||||
|
@ -2202,6 +2207,16 @@ The apparently obvious solution of designing a vertical `Scale` is tricky owing
|
||||||
to the fact that the length of the internal text can be substantial and
|
to the fact that the length of the internal text can be substantial and
|
||||||
variable.
|
variable.
|
||||||
|
|
||||||
|
## Encoder interface
|
||||||
|
|
||||||
|
This alternative interface comprises just two buttons `Next` and `Prev`.
|
||||||
|
Selection and Increase/Decrease is handled by an encoder such as
|
||||||
|
[this one](https://www.adafruit.com/product/377). Selection occurs when the
|
||||||
|
knob is pressed, and movement when it is rotated. This is more intuitive,
|
||||||
|
particularly with horizontally oriented controls.
|
||||||
|
|
||||||
|
TODO wiring details.
|
||||||
|
|
||||||
## Screen layout
|
## Screen layout
|
||||||
|
|
||||||
Widgets are positioned using absolute `row` and `col` coordinates. These may
|
Widgets are positioned using absolute `row` and `col` coordinates. These may
|
||||||
|
|
101
gui/core/ugui.py
101
gui/core/ugui.py
|
@ -38,15 +38,15 @@ _LAST = const(3)
|
||||||
# Wrapper for ssd providing buttons and framebuf compatible methods
|
# Wrapper for ssd providing buttons and framebuf compatible methods
|
||||||
class Display:
|
class Display:
|
||||||
|
|
||||||
def __init__(self, objssd, nxt, sel, prev=None, up=None, down=None):
|
def __init__(self, objssd, nxt, sel, prev=None, up=None, down=None, encoder=False):
|
||||||
global display, ssd
|
global display, ssd
|
||||||
self._next = Switch(nxt)
|
self._next = Switch(nxt)
|
||||||
self._sel = Switch(sel)
|
self._sel = Switch(sel)
|
||||||
self._last = None # Last switch pressed.
|
self._last = None # Last switch pressed.
|
||||||
# Mandatory buttons
|
# Mandatory buttons
|
||||||
# Call current screen bound method
|
# Call current screen bound method
|
||||||
self._next.close_func(self._closure, (self._next, Screen.next_ctrl))
|
self._next.close_func(self._closure, (self._next, Screen.ctrl_move, _NEXT))
|
||||||
self._sel.close_func(self._closure, (self._sel, Screen.sel_ctrl))
|
self._sel.close_func(self._closure, (self._sel, Screen.sel_ctrl, 0))
|
||||||
self._sel.open_func(Screen.unsel)
|
self._sel.open_func(Screen.unsel)
|
||||||
|
|
||||||
self.height = objssd.height
|
self.height = objssd.height
|
||||||
|
@ -56,26 +56,30 @@ class Display:
|
||||||
self._prev = None
|
self._prev = None
|
||||||
if prev is not None:
|
if prev is not None:
|
||||||
self._prev = Switch(prev)
|
self._prev = Switch(prev)
|
||||||
self._prev.close_func(self._closure, (self._prev, Screen.prev_ctrl))
|
self._prev.close_func(self._closure, (self._prev, Screen.ctrl_move, _PREV))
|
||||||
# Up and down methods get the button as an arg.
|
if encoder:
|
||||||
self._up = None
|
if up is None or down is None:
|
||||||
if up is not None:
|
raise ValueError('Must specify pins for encoder.')
|
||||||
self._up = Switch(up)
|
from gui.primitives.encoder import Encoder
|
||||||
self._up.close_func(self._closure, (self._up, self.do_up))
|
self._enc = Encoder(up, down, div=encoder, callback=Screen.adjust)
|
||||||
self._down = None
|
else:
|
||||||
if down is not None:
|
# Up and down methods get the button as an arg.
|
||||||
self._down = Switch(down)
|
if up is not None:
|
||||||
self._down.close_func(self._closure, (self._down, self.do_down))
|
sup = Switch(up)
|
||||||
|
sup.close_func(self._closure, (sup, Screen.adjust, 1))
|
||||||
|
if down is not None:
|
||||||
|
sdown = Switch(down)
|
||||||
|
sdown.close_func(self._closure, (sdown, Screen.adjust, -1))
|
||||||
self._is_grey = False # Not greyed-out
|
self._is_grey = False # Not greyed-out
|
||||||
display = self # Populate globals
|
display = self # Populate globals
|
||||||
ssd = objssd
|
ssd = objssd
|
||||||
|
|
||||||
# Reject button presses where a button is already pressed.
|
# Reject button presses where a button is already pressed.
|
||||||
# Execute if initialising, if same switch re-pressed or if last switch released
|
# Execute if initialising, if same switch re-pressed or if last switch released
|
||||||
def _closure(self, switch, func):
|
def _closure(self, switch, func, arg):
|
||||||
if (self._last is None) or (self._last == switch) or self._last():
|
if (self._last is None) or (self._last == switch) or self._last():
|
||||||
self._last = switch
|
self._last = switch
|
||||||
func()
|
func(switch, arg)
|
||||||
|
|
||||||
def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False):
|
def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False):
|
||||||
sl = writer.stringlen(text)
|
sl = writer.stringlen(text)
|
||||||
|
@ -94,12 +98,6 @@ class Display:
|
||||||
writer.printstring(txt, invert)
|
writer.printstring(txt, invert)
|
||||||
writer.setcolor() # Restore defaults
|
writer.setcolor() # Restore defaults
|
||||||
|
|
||||||
def do_up(self):
|
|
||||||
Screen.up_ctrl(self._up)
|
|
||||||
|
|
||||||
def do_down(self):
|
|
||||||
Screen.down_ctrl(self._down)
|
|
||||||
|
|
||||||
# Greying out has only one option given limitation of 4-bit display driver
|
# Greying out has only one option given limitation of 4-bit display driver
|
||||||
# It would be possible to do better with RGB565 but would need inverse transformation
|
# It would be possible to do better with RGB565 but would need inverse transformation
|
||||||
# to (r, g, b), scale and re-convert to integer.
|
# to (r, g, b), scale and re-convert to integer.
|
||||||
|
@ -204,35 +202,27 @@ class Screen:
|
||||||
is_shutdown = Event()
|
is_shutdown = Event()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def next_ctrl(cls):
|
def ctrl_move(cls, _, v):
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
cls.current_screen.move(_NEXT)
|
cls.current_screen.move(v)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prev_ctrl(cls):
|
def sel_ctrl(cls, b, _):
|
||||||
if cls.current_screen is not None:
|
|
||||||
cls.current_screen.move(_PREV)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def sel_ctrl(cls):
|
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
cls.current_screen.do_sel()
|
cls.current_screen.do_sel()
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unsel(cls):
|
def unsel(cls):
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
cls.current_screen.unsel_i()
|
cls.current_screen.unsel_i()
|
||||||
|
|
||||||
|
# Adjust the value of a widget. If an encoder is used, button arg
|
||||||
|
# is an int (discarded), val is the delta. If using buttons, 1st
|
||||||
|
# arg is the button, delta is +1 or -1
|
||||||
@classmethod
|
@classmethod
|
||||||
def up_ctrl(cls, button):
|
def adjust(cls, button, val):
|
||||||
if cls.current_screen is not None:
|
if cls.current_screen is not None:
|
||||||
cls.current_screen.do_up(button)
|
cls.current_screen.do_adj(button, val)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def down_ctrl(cls, button):
|
|
||||||
if cls.current_screen is not None:
|
|
||||||
cls.current_screen.do_down(button)
|
|
||||||
|
|
||||||
# Move currency to a specific widget (e.g. ButtonList)
|
# Move currency to a specific widget (e.g. ButtonList)
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -440,19 +430,12 @@ class Screen:
|
||||||
if co is not None:
|
if co is not None:
|
||||||
co.unsel()
|
co.unsel()
|
||||||
|
|
||||||
def do_up(self, button):
|
def do_adj(self, button, val):
|
||||||
co = self.get_obj()
|
co = self.get_obj()
|
||||||
if co is not None and hasattr(co, 'do_up'):
|
if co is not None and hasattr(co, 'do_adj'):
|
||||||
co.do_up(button) # Widget handles up/down
|
co.do_adj(button, val) # Widget can handle up/down
|
||||||
else:
|
else:
|
||||||
Screen.current_screen.move(_FIRST)
|
Screen.current_screen.move(_FIRST if val < 0 else _LAST)
|
||||||
|
|
||||||
def do_down(self, button):
|
|
||||||
co = self.get_obj()
|
|
||||||
if co is not None and hasattr(co, 'do_down'):
|
|
||||||
co.do_down(button)
|
|
||||||
else:
|
|
||||||
Screen.current_screen.move(_LAST)
|
|
||||||
|
|
||||||
# Methods optionally implemented in subclass
|
# Methods optionally implemented in subclass
|
||||||
def on_open(self):
|
def on_open(self):
|
||||||
|
@ -710,21 +693,17 @@ class LinearIO(Widget):
|
||||||
# but subclass can be defeat this with WHITE or another color
|
# but subclass can be defeat this with WHITE or another color
|
||||||
self.prcolor = YELLOW if prcolor is None else prcolor
|
self.prcolor = YELLOW if prcolor is None else prcolor
|
||||||
|
|
||||||
def do_up(self, button):
|
# Adjust widget's value. Args: button pressed, amount of increment
|
||||||
asyncio.create_task(self.btnhan(button, 1))
|
def do_adj(self, button, val):
|
||||||
|
encoder = isinstance(button, int)
|
||||||
def do_down(self, button):
|
d = self.min_delta * 0.1 if self.precision else self.min_delta
|
||||||
asyncio.create_task(self.btnhan(button, -1))
|
self.value(self.value() + val * d)
|
||||||
|
if not encoder:
|
||||||
|
asyncio.create_task(self.btnhan(button, val, d))
|
||||||
|
|
||||||
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
|
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
|
||||||
async def btnhan(self, button, up):
|
async def btnhan(self, button, up, d):
|
||||||
if self.precision:
|
maxd = self.max_delta if self.precision else d * 4 # Why move fast in precision mode?
|
||||||
d = self.min_delta * 0.1
|
|
||||||
maxd = self.max_delta
|
|
||||||
else:
|
|
||||||
d = self.min_delta
|
|
||||||
maxd = d * 4 # Why move fast in precision mode?
|
|
||||||
self.value(self.value() + up * d)
|
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
while not button():
|
while not button():
|
||||||
await asyncio.sleep_ms(0) # Quit fast on button release
|
await asyncio.sleep_ms(0) # Quit fast on button release
|
||||||
|
|
|
@ -47,13 +47,11 @@ class BaseScreen(Screen):
|
||||||
self.vslider = Slider(wri, 2, 2, callback=self.slider_cb,
|
self.vslider = Slider(wri, 2, 2, callback=self.slider_cb,
|
||||||
bdcolor=RED, slotcolor=BLUE,
|
bdcolor=RED, slotcolor=BLUE,
|
||||||
legends=('0.0', '0.5', '1.0'), value=0.5)
|
legends=('0.0', '0.5', '1.0'), value=0.5)
|
||||||
#Label(wri, 2, self.vslider.mcol, 'FF')
|
|
||||||
col = 80
|
col = 80
|
||||||
row = 15
|
row = 15
|
||||||
self.hslider = HorizSlider(wri, row, col, callback=self.slider_cb,
|
self.hslider = HorizSlider(wri, row, col, callback=self.slider_cb,
|
||||||
bdcolor=GREEN, slotcolor=BLUE,
|
bdcolor=GREEN, slotcolor=BLUE,
|
||||||
legends=('0.0', '0.5', '1.0'), value=0.7)
|
legends=('0.0', '0.5', '1.0'), value=0.7)
|
||||||
Label(wri, self.hslider.mrow, self.hslider.mcol, 'FF')
|
|
||||||
row += 30
|
row += 30
|
||||||
self.scale = Scale(wri, row, col, width = 150, tickcb = tickcb,
|
self.scale = Scale(wri, row, col, width = 150, tickcb = tickcb,
|
||||||
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN,
|
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN,
|
||||||
|
|
|
@ -61,11 +61,8 @@ class FooScreen(Screen):
|
||||||
|
|
||||||
m0 = Meter(wri, 10, 240, divisions = 4, ptcolor=YELLOW, height=80, width=15,
|
m0 = Meter(wri, 10, 240, divisions = 4, ptcolor=YELLOW, height=80, width=15,
|
||||||
label='Meter example', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
label='Meter example', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||||
#Label(wri, 2, m0.mcol, 'FF')
|
|
||||||
# Instantiate displayable objects. bgcolor forces complete redraw.
|
# Instantiate displayable objects. bgcolor forces complete redraw.
|
||||||
dial = Dial(wri, 2, 2, height = 75, ticks = 12, bgcolor=BLACK, bdcolor=None, label=120) # Border in fg color
|
dial = Dial(wri, 2, 2, height = 75, ticks = 12, bgcolor=BLACK, bdcolor=None, label=120) # Border in fg color
|
||||||
#Label(wri, dial.mrow, 2, 'FF')
|
|
||||||
#Label(wri, dial.mrow, dial.mcol, 'FF')
|
|
||||||
scale = Scale(wri, 2, 100, width = 124, tickcb = tickcb,
|
scale = Scale(wri, 2, 100, width = 124, tickcb = tickcb,
|
||||||
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN)
|
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# encoder.py Asynchronous driver for incremental quadrature encoder.
|
||||||
|
|
||||||
|
# Copyright (c) 2021 Peter Hinch
|
||||||
|
# Released under the MIT License (MIT) - see LICENSE file
|
||||||
|
|
||||||
|
# This driver is intended for encoder-based control knobs. It is
|
||||||
|
# unsuitable for NC machine applications. Please see the docs.
|
||||||
|
|
||||||
|
import uasyncio as asyncio
|
||||||
|
from machine import Pin
|
||||||
|
|
||||||
|
class Encoder:
|
||||||
|
delay = 100 # Pause (ms) for motion to stop
|
||||||
|
|
||||||
|
def __init__(self, pin_x, pin_y, v=0, vmin=None, vmax=None, div=1,
|
||||||
|
callback=lambda a, b : None, args=()):
|
||||||
|
self._pin_x = pin_x
|
||||||
|
self._pin_y = pin_y
|
||||||
|
self._v = 0 # Hardware value always starts at 0
|
||||||
|
self._cv = v # Current (divided) value
|
||||||
|
if ((vmin is not None) and v < min) 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, callback, args))
|
||||||
|
|
||||||
|
# Hardware IRQ's
|
||||||
|
def _x_cb(self, pin):
|
||||||
|
fwd = pin() ^ self._pin_y()
|
||||||
|
self._v += 1 if fwd else -1
|
||||||
|
self._tsf.set()
|
||||||
|
|
||||||
|
def _y_cb(self, pin):
|
||||||
|
fwd = pin() ^ self._pin_x() ^ 1
|
||||||
|
self._v += 1 if fwd else -1
|
||||||
|
self._tsf.set()
|
||||||
|
|
||||||
|
async def _run(self, vmin, vmax, div, cb, args):
|
||||||
|
pv = self._v # Prior hardware value
|
||||||
|
cv = self._cv # Current divided value as passed to callback
|
||||||
|
pcv = cv # Prior divided value passed to callback
|
||||||
|
mod = 0
|
||||||
|
delay = self.delay
|
||||||
|
while True:
|
||||||
|
await self._tsf.wait()
|
||||||
|
await asyncio.sleep_ms(delay) # Wait for motion to stop
|
||||||
|
new = self._v # Sample hardware (atomic read)
|
||||||
|
a = new - pv # Hardware change
|
||||||
|
# Ensure symmetrical bahaviour for + and - values
|
||||||
|
q, r = divmod(abs(a), div)
|
||||||
|
if a < 0:
|
||||||
|
r = -r
|
||||||
|
q = -q
|
||||||
|
pv = new - r # Hardware value when local value was updated
|
||||||
|
cv += q
|
||||||
|
if vmax is not None:
|
||||||
|
cv = min(cv, vmax)
|
||||||
|
if vmin is not None:
|
||||||
|
cv = max(cv, vmin)
|
||||||
|
self._cv = cv # For value()
|
||||||
|
if cv != pcv:
|
||||||
|
cb(cv, cv - pcv, *args) # User CB in uasyncio context
|
||||||
|
pcv = cv
|
||||||
|
|
||||||
|
def value(self):
|
||||||
|
return self._cv
|
|
@ -78,18 +78,17 @@ class Listbox(Widget):
|
||||||
self.value(v)
|
self.value(v)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def do_up(self, _):
|
def do_adj(self, _, val):
|
||||||
if v := self._value:
|
v = self._value
|
||||||
self.value(v - 1)
|
if val > 0:
|
||||||
|
if v:
|
||||||
|
self.value(v - 1)
|
||||||
|
elif val < 0:
|
||||||
|
if v < len(self.elements) - 1:
|
||||||
|
self.value(v + 1)
|
||||||
if (self.also & Listbox.ON_MOVE): # Treat as if select pressed
|
if (self.also & Listbox.ON_MOVE): # Treat as if select pressed
|
||||||
self.do_sel()
|
self.do_sel()
|
||||||
|
|
||||||
def do_down(self, _):
|
|
||||||
if (v := self._value) < len(self.elements) - 1:
|
|
||||||
self.value(v + 1)
|
|
||||||
if (self.also & Listbox.ON_MOVE):
|
|
||||||
self.do_sel()
|
|
||||||
|
|
||||||
# Callback runs if select is pressed. Also (if ON_LEAVE) if user changes
|
# Callback runs if select is pressed. Also (if ON_LEAVE) if user changes
|
||||||
# list currency and then moves off the control. Otherwise if we have a
|
# list currency and then moves off the control. Otherwise if we have a
|
||||||
# callback that refreshes another control, that second control does not
|
# callback that refreshes another control, that second control does not
|
||||||
|
|
|
@ -23,6 +23,7 @@ dolittle = lambda *_ : None
|
||||||
|
|
||||||
# Start value is 1.0. User applies scaling to value and ticks callback.
|
# Start value is 1.0. User applies scaling to value and ticks callback.
|
||||||
class ScaleLog(LinearIO):
|
class ScaleLog(LinearIO):
|
||||||
|
encoder_rate = 5
|
||||||
def __init__(self, writer, row, col, *,
|
def __init__(self, writer, row, col, *,
|
||||||
decades=5, height=0, width=160,
|
decades=5, height=0, width=160,
|
||||||
bdcolor=None, fgcolor=None, bgcolor=None,
|
bdcolor=None, fgcolor=None, bgcolor=None,
|
||||||
|
@ -141,6 +142,14 @@ class ScaleLog(LinearIO):
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
|
|
||||||
|
# Adjust widget's value. Args: button pressed, amount of increment
|
||||||
|
def do_adj(self, button, val):
|
||||||
|
if isinstance(button, int): # Using an encoder
|
||||||
|
delta = self.delta * self.encoder_rate * 0.1 if self.precision else self.delta * self.encoder_rate
|
||||||
|
self.value(self.value() * (1 + delta)**val)
|
||||||
|
else:
|
||||||
|
asyncio.create_task(self.btnhan(button, val, d))
|
||||||
|
|
||||||
async def btnhan(self, button, up):
|
async def btnhan(self, button, up):
|
||||||
up = up == 1
|
up = up == 1
|
||||||
if self.precision:
|
if self.precision:
|
||||||
|
|
|
@ -1,122 +1,54 @@
|
||||||
# color_setup.py Customise for your hardware config
|
# ili9341_pico.py Customise for your hardware config
|
||||||
|
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa
|
# Copyright (c) 2021 Peter Hinch
|
||||||
|
|
||||||
# Supports:
|
# As written, supports:
|
||||||
# TTGO T-Display 1.14" 135*240(Pixel) based on ST7789V
|
# ili9341 240x320 displays on Pi Pico
|
||||||
# http://www.lilygo.cn/claprod_view.aspx?TypeId=62&Id=1274
|
# Edit the driver import for other displays.
|
||||||
# http://www.lilygo.cn/prod_view.aspx?TypeId=50044&Id=1126
|
|
||||||
# https://github.com/Xinyuan-LilyGO/TTGO-T-Display
|
|
||||||
# https://github.com/Xinyuan-LilyGO/TTGO-T-Display/blob/master/image/pinmap.jpg
|
|
||||||
# https://github.com/Xinyuan-LilyGO/TTGO-T-Display/blob/master/schematic/ESP32-TFT(6-26).pdf
|
|
||||||
|
|
||||||
# WIRING (TTGO T-Display pin numbers and names).
|
|
||||||
# Pinout of TFT Driver
|
|
||||||
# ST7789 ESP32
|
|
||||||
# TFT_MISO N/A
|
|
||||||
TFT_MOSI = 19 # (SDA on schematic pdf) SPI interface output/input pin.
|
|
||||||
TFT_SCLK = 18 # This pin is used to be serial interface clock.
|
|
||||||
TFT_CS = 5 # Chip selection pin, low enable, high disable.
|
|
||||||
TFT_DC = 16 # Display data/command selection pin in 4-line serial interface.
|
|
||||||
TFT_RST = 23 # This signal will reset the device,Signal is active low.
|
|
||||||
TFT_BL = 4 # (LEDK on schematic pdf) Display backlight control pin
|
|
||||||
|
|
||||||
ADC_IN = 34 # Measuring battery or USB voltage, see comment below
|
|
||||||
ADC_EN = 14 # (PWR_EN on schematic pdf) is the ADC detection enable port
|
|
||||||
|
|
||||||
BUTTON1 = 35 # right of the USB connector
|
|
||||||
BUTTON2 = 0 # left of the USB connector
|
|
||||||
|
|
||||||
# ESP32 pins, free for use in user applications
|
|
||||||
#I2C_SDA = 21 # hardware ID 0
|
|
||||||
#I2C_SCL = 22
|
|
||||||
|
|
||||||
#UART2TXD = 17
|
|
||||||
|
|
||||||
#GPIO2 = 2
|
|
||||||
#GPIO15 = 15
|
|
||||||
#GPIO13 = 13
|
|
||||||
#GPIO12 = 12
|
|
||||||
|
|
||||||
#GPIO37 = 37
|
|
||||||
#GPIO38 = 38
|
|
||||||
#UART1TXD = 4
|
|
||||||
#UART1RXD = 5
|
|
||||||
#GPIO18 = 18
|
|
||||||
#GPIO19 = 19
|
|
||||||
#GPIO17 = 17
|
|
||||||
|
|
||||||
#DAC1 = 25
|
|
||||||
#DAC2 = 26
|
|
||||||
|
|
||||||
# Input only pins
|
|
||||||
#GPIO36 = 36 # input only
|
|
||||||
#GPIO39 = 39 # input only
|
|
||||||
|
|
||||||
# Demo of initialisation procedure designed to minimise risk of memory fail
|
# Demo of initialisation procedure designed to minimise risk of memory fail
|
||||||
# when instantiating the frame buffer. The aim is to do this as early as
|
# when instantiating the frame buffer. The aim is to do this as early as
|
||||||
# possible before importing other modules.
|
# possible before importing other modules.
|
||||||
|
|
||||||
from machine import Pin, SPI, ADC, freq
|
# WIRING
|
||||||
|
# Pico Display
|
||||||
|
# GPIO Pin
|
||||||
|
# 3v3 36 Vin
|
||||||
|
# IO6 9 CLK Hardware SPI0
|
||||||
|
# IO7 10 DATA (AKA SI MOSI)
|
||||||
|
# IO8 11 DC
|
||||||
|
# IO9 12 Rst
|
||||||
|
# Gnd 13 Gnd
|
||||||
|
# IO10 14 CS
|
||||||
|
|
||||||
|
# Pushbuttons are wired between the pin and Gnd
|
||||||
|
# Pico pin Meaning
|
||||||
|
# 16 Operate current control
|
||||||
|
# 17 Decrease value of current control
|
||||||
|
# 18 Select previous control
|
||||||
|
# 19 Select next control
|
||||||
|
# 20 Increase value of current control
|
||||||
|
|
||||||
|
from machine import Pin, SPI, freq
|
||||||
import gc
|
import gc
|
||||||
|
|
||||||
from drivers.st7789.st7789_4bit import *
|
from drivers.ili93xx.ili9341 import ILI9341 as SSD
|
||||||
SSD = ST7789
|
freq(250_000_000) # RP2 overclock
|
||||||
|
# Create and export an SSD instance
|
||||||
pdc = Pin(TFT_DC, Pin.OUT, value=0) # Arbitrary pins
|
pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins
|
||||||
pcs = Pin(TFT_CS, Pin.OUT, value=1)
|
prst = Pin(9, Pin.OUT, value=1)
|
||||||
prst = Pin(TFT_RST, Pin.OUT, value=1)
|
pcs = Pin(10, Pin.OUT, value=1)
|
||||||
pbl = Pin(TFT_BL, Pin.OUT, value=1)
|
spi = SPI(0, baudrate=30_000_000)
|
||||||
|
|
||||||
gc.collect() # Precaution before instantiating framebuf
|
gc.collect() # Precaution before instantiating framebuf
|
||||||
# Conservative low baudrate. Can go to 62.5MHz.
|
ssd = SSD(spi, pcs, pdc, prst, usd=True)
|
||||||
spi = SPI(1, 30_000_000, sck=Pin(TFT_SCLK), mosi=Pin(TFT_MOSI))
|
|
||||||
freq(160_000_000)
|
|
||||||
|
|
||||||
''' TTGO
|
|
||||||
v +----------------+
|
|
||||||
40 | | |
|
|
||||||
^ | +------+ | pin 36
|
|
||||||
| | | | |
|
|
||||||
| | | | |
|
|
||||||
240 | | | | |
|
|
||||||
| | | | |
|
|
||||||
| | | | |
|
|
||||||
v | +------+ |
|
|
||||||
40 | | | Reset button
|
|
||||||
^ +----------------+
|
|
||||||
>----<------>----<
|
|
||||||
52 135 xx
|
|
||||||
BUTTON2 BUTTON1
|
|
||||||
'''
|
|
||||||
# Right way up landscape: defined as top left adjacent to pin 36
|
|
||||||
ssd = SSD(spi, height=135, width=240, dc=pdc, cs=pcs, rst=prst, disp_mode=LANDSCAPE, display=TDISPLAY)
|
|
||||||
# Normal portrait display: consistent with TTGO logo at top
|
|
||||||
# ssd = SSD(spi, height=240, width=135, dc=pdc, cs=pcs, rst=prst, disp_mode=PORTRAIT, display=TDISPLAY)
|
|
||||||
|
|
||||||
from gui.core.ugui import Display
|
from gui.core.ugui import Display
|
||||||
# Create and export a Display instance
|
# Create and export a Display instance
|
||||||
# Define control buttons
|
# Define control buttons
|
||||||
nxt = Pin(32, Pin.IN, Pin.PULL_UP) # Move to next control
|
nxt = Pin(19, Pin.IN, Pin.PULL_UP) # Move to next control
|
||||||
sel = Pin(36, Pin.IN, Pin.PULL_UP) # Operate current control
|
sel = Pin(16, Pin.IN, Pin.PULL_UP) # Operate current control
|
||||||
prev = Pin(38, Pin.IN, Pin.PULL_UP) # Move to previous control
|
prev = Pin(18, Pin.IN, Pin.PULL_UP) # Move to previous control
|
||||||
increase = Pin(37, Pin.IN, Pin.PULL_UP) # Increase control's value
|
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||||
decrease = Pin(39, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
decrease = Pin(17, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
||||||
display = Display(ssd, nxt, sel, prev, increase, decrease)
|
display = Display(ssd, nxt, sel, prev, increase, decrease, 5) # Encoder
|
||||||
|
|
||||||
# optional
|
|
||||||
# b1 = Pin(BUTTON1, Pin.IN)
|
|
||||||
# b2 = Pin(BUTTON2, Pin.IN)
|
|
||||||
# adc_en = Pin(ADC_EN, Pin.OUT, value=1)
|
|
||||||
# adc_in = ADC(Pin(ADC_IN))
|
|
||||||
# adc_en.value(0)
|
|
||||||
'''
|
|
||||||
Set ADC_EN to "1" and read voltage in BAT_ADC,
|
|
||||||
if this voltage more than 4.3 V device have powered from USB.
|
|
||||||
If less then 4.3 V - device have power from battery.
|
|
||||||
To save battery you can set ADC_EN to "0" and in this case the USB converter
|
|
||||||
will be power off and do not use your battery.
|
|
||||||
When you need to measure battery voltage first set ADC_EN to "1",
|
|
||||||
measure voltage and then set ADC_EN back to "0" for save battery.
|
|
||||||
'''
|
|
||||||
|
|
|
@ -101,9 +101,14 @@ from gui.core.ugui import Display
|
||||||
nxt = Pin(32, Pin.IN, Pin.PULL_UP) # Move to next control
|
nxt = Pin(32, Pin.IN, Pin.PULL_UP) # Move to next control
|
||||||
sel = Pin(36, Pin.IN, Pin.PULL_UP) # Operate current control
|
sel = Pin(36, Pin.IN, Pin.PULL_UP) # Operate current control
|
||||||
prev = Pin(38, Pin.IN, Pin.PULL_UP) # Move to previous control
|
prev = Pin(38, Pin.IN, Pin.PULL_UP) # Move to previous control
|
||||||
increase = Pin(37, Pin.IN, Pin.PULL_UP) # Increase control's value
|
encoder = 5 # Divide by 5
|
||||||
decrease = Pin(39, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
if encoder:
|
||||||
display = Display(ssd, nxt, sel, prev, increase, decrease)
|
increase = Pin(25, Pin.IN, Pin.PULL_UP) # Encoder x and y pins
|
||||||
|
decrease = Pin(33, Pin.IN, Pin.PULL_UP)
|
||||||
|
else:
|
||||||
|
increase = Pin(37, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||||
|
decrease = Pin(39, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
||||||
|
display = Display(ssd, nxt, sel, prev, increase, decrease, encoder)
|
||||||
|
|
||||||
# optional
|
# optional
|
||||||
# b1 = Pin(BUTTON1, Pin.IN)
|
# b1 = Pin(BUTTON1, Pin.IN)
|
||||||
|
|
Ładowanie…
Reference in New Issue