kopia lustrzana https://github.com/peterhinch/micropython-micro-gui
Add limited ePaper support.
rodzic
43c19b7b48
commit
e510739daf
41
README.md
41
README.md
|
@ -60,6 +60,8 @@ target and a C device driver (unless you can acquire a suitable binary).
|
|||
|
||||
# Project status
|
||||
|
||||
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
|
||||
|
||||
July 2022: Add ESP32 touch pad support.
|
||||
|
||||
June 2022: Add [QRMap](./README.md#620-qrmap-widget) and
|
||||
|
@ -147,6 +149,7 @@ development so check for updates.
|
|||
7.4 [Class TSequence](./README.md#74-class-tsequence) Plotting realtime, time sequential data.
|
||||
8. [ESP32 touch pads](./README.md#8-esp32-touch-pads) Replacing buttons with touch pads.
|
||||
9. [Realtime applications](./README.md#9-realtime-applications) Accommodating tasks requiring fast RT performance.
|
||||
9.1 [ePaper refresh](./README.md#91-epaper-refresh) Using these techniques to provide a full refresh.
|
||||
|
||||
[Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, encoder interface, use of graphics primitives
|
||||
[Appendix 2 Freezing bytecode](./README.md#appendix-2-freezing-bytecode) Optional way to save RAM.
|
||||
|
@ -501,8 +504,12 @@ some builds.
|
|||
|
||||
Supported displays are as per
|
||||
[the nano-gui list](https://github.com/peterhinch/micropython-nano-gui/blob/master/README.md#12-description).
|
||||
In practice usage with ePaper displays is questionable because of their slow
|
||||
refresh times. I haven't tested these, or the Sharp displays.
|
||||
In general ePaper and Sharp displays are unlikely to yield good results because
|
||||
of slow and visually intrusive refreshing. However there is an exception: the
|
||||
[Waveshare pico_epaper_42](https://www.waveshare.com/pico-epaper-4.2.htm). This
|
||||
supports partial updates which work remarkably well with minimal ghosting. Note
|
||||
that it can be used with hosts other than the Pico via the supplied cable. See
|
||||
[ePaper refresh](./README.md#91-epaper-refresh).
|
||||
|
||||
Display drivers are documented [here](https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md).
|
||||
|
||||
|
@ -565,6 +572,11 @@ minimal and aim to demonstrate a single technique.
|
|||
* `adjust_vec.py` A pair of `Adjuster`s vary a vector.
|
||||
* `bitmap.py` Demo of the `BitMap` widget showing a changing image.
|
||||
* `qrcode.py` Display a QR code. Requires the uQR module.
|
||||
* `calendar.py` Demo of grid widget.
|
||||
* `epaper.py` Warts-and-all demo for an ePaper display. Currently the only
|
||||
supported display is the
|
||||
[Waveshare pico_epaper_42](https://www.waveshare.com/pico-epaper-4.2.htm)
|
||||
with Pico or other host.
|
||||
|
||||
### 1.11.2 Test scripts
|
||||
|
||||
|
@ -784,8 +796,8 @@ display such as an OLED. On a Sharp display it indicates reflection.
|
|||
|
||||
There is an issue regarding ePaper displays discussed
|
||||
[here](https://github.com/peterhinch/micropython-nano-gui/blob/master/README.md#312-monochrome-displays).
|
||||
I don't consider ePaper displays suitable for I/O because of their slow refresh
|
||||
time.
|
||||
The driver for the [Waveshare pico_epaper_42](https://www.waveshare.com/pico-epaper-4.2.htm)
|
||||
renders colored objects as black on white.
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
|
@ -3030,6 +3042,27 @@ another from occurring.
|
|||
```
|
||||
The demo `gui/demos/audio.py` provides example usage.
|
||||
|
||||
## 9.1 ePaper refresh
|
||||
|
||||
The [Waveshare pico_epaper_42](https://www.waveshare.com/pico-epaper-4.2.htm)
|
||||
is currently the only fully supported ePaper display, with a hardware_setup.py
|
||||
copied or adapted from `setup_examples/pico_epaper_42_pico.py`. After an
|
||||
initial refresh the driver is put into partial mode to provide reasonably
|
||||
quick and visually satisfactory response to button events. However ghosting may
|
||||
accumulate after long periods of running, and an application may occasionally
|
||||
need to perform a full refresh. This requires the "done" interlock described
|
||||
above.
|
||||
|
||||
```python
|
||||
async def full_refresh():
|
||||
Screen.rfsh_done.clear() # Enable completion flag
|
||||
await Screen.rfsh_done.wait() # Wait for a refresh to end
|
||||
ssd.set_full()
|
||||
Screen.rfsh_done.clear() # Enable completion flag
|
||||
await Screen.rfsh_done.wait() # Wait for a single full refresh to end
|
||||
ssd.set_partial()
|
||||
```
|
||||
|
||||
###### [Contents](./README.md#0-contents)
|
||||
|
||||
# Appendix 1 Application design
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
# pico_epaper_42.py A 1-bit monochrome display driver for the Waveshare Pico
|
||||
# ePaper 4.2" display. This version fixes bugs and supports partial updates.
|
||||
# https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/epaper/pico_epaper_42.py
|
||||
|
||||
# Adapted from the Waveshare driver by Peter Hinch Sept 2022-March 2023.
|
||||
# https://www.waveshare.com/pico-epaper-4.2.htm
|
||||
# UC8176 manual https://www.waveshare.com/w/upload/8/88/UC8176.pdf
|
||||
# Waveshare's copy of this driver.
|
||||
# https://github.com/waveshare/Pico_ePaper_Code/blob/main/pythonNanoGui/drivers/ePaper4in2.py
|
||||
|
||||
# *****************************************************************************
|
||||
# * | File : Pico_ePaper-3.7.py
|
||||
# * | Author : Waveshare team
|
||||
# * | Function : Electronic paper driver
|
||||
# * | Info :
|
||||
# *----------------
|
||||
# * | This version: V1.0
|
||||
# * | Date : 2021-06-01
|
||||
# # | Info : python demo
|
||||
# -----------------------------------------------------------------------------
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documnetation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# If .set_partial() is called, subsequent updates will be partial. To restore normal
|
||||
# updates, issue .set_full()
|
||||
|
||||
from machine import Pin, SPI
|
||||
import framebuf
|
||||
import time
|
||||
import uasyncio as asyncio
|
||||
from drivers.boolpalette import BoolPalette
|
||||
|
||||
# Display resolution
|
||||
_EPD_WIDTH = const(400)
|
||||
_BWIDTH = _EPD_WIDTH // 8
|
||||
_EPD_HEIGHT = const(300)
|
||||
|
||||
RST_PIN = 12
|
||||
DC_PIN = 8
|
||||
CS_PIN = 9
|
||||
BUSY_PIN = 13
|
||||
|
||||
EPD_lut_vcom0 = b"\x00\x08\x08\x00\x00\x02\x00\x0F\x0F\x00\x00\x01\x00\x08\x08\x00\
|
||||
\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
EPD_lut_ww = b"\x50\x08\x08\x00\x00\x02\x90\x0F\x0F\x00\x00\x01\xA0\x08\x08\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
EPD_lut_bw = b"\x50\x08\x08\x00\x00\x02\x90\x0F\x0F\x00\x00\x01\xA0\x08\x08\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
EPD_lut_wb = b"\xA0\x08\x08\x00\x00\x02\x90\x0F\x0F\x00\x00\x01\x50\x08\x08\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
EPD_lut_bb = b"\x20\x08\x08\x00\x00\x02\x90\x0F\x0F\x00\x00\x01\x10\x08\x08\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
# ******************************partial screen update LUT********************************* #
|
||||
|
||||
EPD_partial_lut_vcom1 = b"\x00\x19\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
EPD_partial_lut_ww1 = b"\x00\x19\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00"
|
||||
|
||||
EPD_partial_lut_bw1 =b"\x80\x19\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00"
|
||||
|
||||
EPD_partial_lut_wb1 = b"\x40\x19\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00"
|
||||
|
||||
|
||||
EPD_partial_lut_bb1 = b"\x00\x19\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00"
|
||||
|
||||
class EPD(framebuf.FrameBuffer):
|
||||
# A monochrome approach should be used for coding this. The rgb method ensures
|
||||
# nothing breaks if users specify colors.
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return int((r > 127) or (g > 127) or (b > 127))
|
||||
|
||||
def __init__(self, spi=None, cs=None, dc=None, rst=None, busy=None, asyn=False):
|
||||
self.reset_pin = Pin(RST_PIN, Pin.OUT) if rst is None else rst
|
||||
self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP) if busy is None else busy
|
||||
self.cs_pin = Pin(CS_PIN, Pin.OUT) if cs is None else cs
|
||||
self.dc_pin = Pin(DC_PIN, Pin.OUT) if dc is None else dc
|
||||
self.spi = SPI(1, sck = Pin(10), mosi = Pin(11), miso = Pin(28)) if spi is None else spi
|
||||
self.spi.init(baudrate = 4_000_000)
|
||||
self._asyn = asyn
|
||||
self._busy = False # Set immediately on .show(). Cleared when busy pin is logically false (physically 1).
|
||||
self._updated = asyncio.Event()
|
||||
|
||||
self.width = _EPD_WIDTH
|
||||
self.height = _EPD_HEIGHT
|
||||
self.buf = bytearray(_EPD_HEIGHT * _BWIDTH)
|
||||
self.mvb = memoryview(self.buf)
|
||||
mode = framebuf.MONO_HLSB
|
||||
self.palette = BoolPalette(mode)
|
||||
super().__init__(self.buf, _EPD_WIDTH, _EPD_HEIGHT, mode)
|
||||
self.init()
|
||||
time.sleep_ms(500)
|
||||
|
||||
# Hardware reset
|
||||
def reset(self):
|
||||
for v in (1, 0, 1):
|
||||
self.reset_pin(v)
|
||||
time.sleep_ms(20)
|
||||
|
||||
def send_command(self, command):
|
||||
self.dc_pin(0)
|
||||
self.cs_pin(0)
|
||||
self.spi.write(command)
|
||||
self.cs_pin(1)
|
||||
|
||||
def send_bytes(self, data):
|
||||
self.dc_pin(1)
|
||||
self.cs_pin(0)
|
||||
self.spi.write(data)
|
||||
self.cs_pin(1)
|
||||
|
||||
def display_on(self):
|
||||
self.send_command(b"\x12")
|
||||
time.sleep_ms(100)
|
||||
self.wait_until_ready()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self.send_command(b"\x01") # POWER SETTING
|
||||
self.send_bytes(b"\x03")
|
||||
self.send_bytes(b"\x00")
|
||||
self.send_bytes(b"\x2b")
|
||||
self.send_bytes(b"\x2b")
|
||||
|
||||
self.send_command(b"\x06") # boost soft start
|
||||
self.send_bytes(b"\x17") # A
|
||||
self.send_bytes(b"\x17") # B
|
||||
self.send_bytes(b"\x17") # C
|
||||
|
||||
self.send_command(b"\x04") # POWER_ON
|
||||
self.wait_until_ready()
|
||||
|
||||
self.send_command(b"\x00") # panel setting
|
||||
self.send_bytes(b"\xbf") # KW-BF KWR-AF BWROTP 0f BWOTP 1f
|
||||
self.send_bytes(b"\x0d")
|
||||
|
||||
self.send_command(b"\x30") # PLL setting
|
||||
self.send_bytes(b"\x3C") # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
|
||||
|
||||
self.send_command(b"\x61") # resolution setting
|
||||
self.send_bytes(b"\x01")
|
||||
self.send_bytes(b"\x90") # 128
|
||||
self.send_bytes(b"\x01")
|
||||
self.send_bytes(b"\x2c")
|
||||
|
||||
self.send_command(b"\x82") # vcom_DC setting
|
||||
self.send_bytes(b"\x28")
|
||||
|
||||
self.send_command(b"\x50") # VCOM AND DATA INTERVAL SETTING
|
||||
self.send_bytes(b"\x97") # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
|
||||
|
||||
self.set_full()
|
||||
# Clear display
|
||||
self.send_command(b"\x10")
|
||||
for j in range(_EPD_HEIGHT):
|
||||
self.send_bytes(b"\xff" * _BWIDTH)
|
||||
|
||||
self.send_command(b"\x13")
|
||||
for j in range(_EPD_HEIGHT):
|
||||
self.send_bytes(b"\xff" * _BWIDTH)
|
||||
|
||||
self.send_command(b"\x12")
|
||||
time.sleep_ms(10)
|
||||
self.display_on()
|
||||
|
||||
def set_full(self): # Normal full updates
|
||||
self.send_command(b"\x20")
|
||||
self.send_bytes(EPD_lut_vcom0)
|
||||
|
||||
self.send_command(b"\x21")
|
||||
self.send_bytes(EPD_lut_ww)
|
||||
|
||||
self.send_command(b"\x22")
|
||||
self.send_bytes(EPD_lut_bw)
|
||||
|
||||
self.send_command(b"\x23")
|
||||
self.send_bytes(EPD_lut_wb)
|
||||
|
||||
self.send_command(b"\x24")
|
||||
self.send_bytes(EPD_lut_bb)
|
||||
|
||||
def set_partial(self): # Partial updates
|
||||
self.send_command(b"\x20")
|
||||
self.send_bytes(EPD_partial_lut_vcom1)
|
||||
|
||||
self.send_command(b"\x21")
|
||||
self.send_bytes(EPD_partial_lut_ww1)
|
||||
|
||||
self.send_command(b"\x22")
|
||||
self.send_bytes(EPD_partial_lut_bw1)
|
||||
|
||||
self.send_command(b"\x23")
|
||||
self.send_bytes(EPD_partial_lut_wb1)
|
||||
|
||||
self.send_command(b"\x24")
|
||||
self.send_bytes(EPD_partial_lut_bb1)
|
||||
|
||||
def wait_until_ready(self):
|
||||
while not self.ready():
|
||||
time.sleep_ms(100)
|
||||
|
||||
async def wait(self):
|
||||
while not self.ready():
|
||||
await asyncio.sleep_ms(100)
|
||||
|
||||
# Pause until framebuf has been copied to device.
|
||||
async def updated(self):
|
||||
self._updated.clear()
|
||||
await self._updated.wait()
|
||||
self._updated.clear()
|
||||
|
||||
# For polling in asynchronous code. Just checks pin state.
|
||||
# 0 == busy. Comment in official code is wrong. Code is correct.
|
||||
def ready(self):
|
||||
return not (self._busy or (self.busy_pin() == 0)) # 0 == busy
|
||||
|
||||
def _line(self, n, buf=bytearray(_BWIDTH)):
|
||||
img = self.mvb
|
||||
s = n * _BWIDTH
|
||||
for x, b in enumerate(img[s : s + _BWIDTH]):
|
||||
buf[x] = b ^ 0xFF
|
||||
self.send_bytes(buf)
|
||||
|
||||
async def _as_show(self):
|
||||
self.send_command(b"\x13")
|
||||
for j in range(_EPD_HEIGHT): # Loop would block ~300ms
|
||||
self._line(j)
|
||||
await asyncio.sleep_ms(0)
|
||||
self._updated.set()
|
||||
self.send_command(b"\x12") # Async .display_on()
|
||||
while not self.busy_pin():
|
||||
await asyncio.sleep_ms(10) # About 1.7s
|
||||
self._busy = False
|
||||
|
||||
async def do_refresh(self, split): # For micro-gui
|
||||
await self._as_show()
|
||||
|
||||
def show(self):
|
||||
if self._busy:
|
||||
raise RuntimeError('Cannot refresh: display is busy.')
|
||||
self._busy = True # Immediate busy flag. Pin goes low much later.
|
||||
if self._asyn:
|
||||
asyncio.create_task(self._as_show())
|
||||
return
|
||||
self.send_command(b"\x13")
|
||||
for j in range(_EPD_HEIGHT):
|
||||
self._line(j)
|
||||
self._busy = False
|
||||
self.display_on()
|
||||
self.wait_until_ready()
|
||||
|
||||
def sleep(self):
|
||||
# self.send_command(b"\x02") # power off
|
||||
# self.wait_until_ready()
|
||||
self.send_command(b"\x07") # deep sleep
|
||||
self.send_bytes(b"\xA5")
|
|
@ -0,0 +1,183 @@
|
|||
# epaper.py micro-gui demo of multiple controls on an ePaper display type
|
||||
# https://www.waveshare.com/pico-epaper-4.2.htm
|
||||
# Use with setup_examples/pico_epaper_42_pico.py
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2023 Peter Hinch
|
||||
|
||||
# Initialise hardware and framebuf before importing modules.
|
||||
# Create SSD instance. Must be done first because of RAM use.
|
||||
import hardware_setup
|
||||
|
||||
from gui.core.ugui import Screen, ssd
|
||||
from gui.core.writer import CWriter
|
||||
import gui.fonts.arial10 as arial10 # Font for CWriter
|
||||
from gui.core.colors import *
|
||||
# Widgets
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.dial import Dial, Pointer
|
||||
from gui.widgets.meter import Meter
|
||||
from gui.widgets.scale import Scale
|
||||
from gui.widgets.buttons import Button, ButtonList, RadioButtons, CloseButton
|
||||
from gui.widgets.checkbox import Checkbox
|
||||
from gui.widgets.led import LED
|
||||
|
||||
import cmath
|
||||
import uasyncio as asyncio
|
||||
import utime
|
||||
import gc
|
||||
|
||||
async def full_refresh():
|
||||
Screen.rfsh_done.clear() # Enable completion flag
|
||||
await Screen.rfsh_done.wait() # Wait for a refresh to end
|
||||
ssd.set_full()
|
||||
Screen.rfsh_done.clear() # Enable completion flag
|
||||
await Screen.rfsh_done.wait() # Wait for a single full refresh to end
|
||||
ssd.set_partial()
|
||||
|
||||
|
||||
class FooScreen(Screen):
|
||||
def __init__(self):
|
||||
buttons = []
|
||||
|
||||
# A ButtonList with two entries
|
||||
table_buttonset = (
|
||||
{'fgcolor' : RED, 'text' : 'Disable', 'args' : (buttons, True)},
|
||||
{'fgcolor' : GREEN, 'text' : 'Enable', 'args' : (buttons, False)},
|
||||
)
|
||||
|
||||
table_radiobuttons = (
|
||||
{'text' : '1', 'args' : ('1',)},
|
||||
{'text' : '2', 'args' : ('2',)},
|
||||
{'text' : '3', 'args' : ('3',)},
|
||||
{'text' : '4', 'args' : ('4',)},
|
||||
)
|
||||
|
||||
def tickcb(f, c):
|
||||
if f > 0.8:
|
||||
return RED
|
||||
if f < -0.8:
|
||||
return BLUE
|
||||
return c
|
||||
|
||||
def bcb(b):
|
||||
print('Button pressed', b)
|
||||
|
||||
super().__init__()
|
||||
self.rb0 = None
|
||||
self.bs0 = None
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
lbltim = Label(wri, 65, 100, 'this is a test', bdcolor=RED)
|
||||
|
||||
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'))
|
||||
# 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
|
||||
scale = Scale(wri, 2, 100, width = 124, tickcb = tickcb,
|
||||
pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN)
|
||||
|
||||
row = 105
|
||||
col = 2
|
||||
Label(wri, row, col, 'Normal buttons')
|
||||
# Four Button instances
|
||||
row = 120
|
||||
ht = 30
|
||||
for i, s in enumerate(('a', 'b', 'c', 'd')):
|
||||
col= 2 + i * (ht + 5)
|
||||
buttons.append(Button(wri, row, col, height=ht, callback=bcb, text=s, litcolor=RED, shape=CIRCLE, bgcolor=DARKGREEN))
|
||||
|
||||
# ButtonList
|
||||
self.bs = ButtonList(self.callback)
|
||||
self.bs0 = None
|
||||
col+= 50
|
||||
Label(wri, row - 15, col, 'ButtonList')
|
||||
for t in table_buttonset: # Buttons overlay each other at same location
|
||||
button = self.bs.add_button(wri, row, col, shape=RECTANGLE, textcolor=BLUE, height=30, **t)
|
||||
if self.bs0 is None: # Save for reset button callback
|
||||
self.bs0 = button
|
||||
|
||||
# Reset button
|
||||
col+= 60
|
||||
btn = Button(wri, row, col, height=30, callback=self.rstcb, text='reset', litcolor=RED, fgcolor=GREEN, bgcolor=DARKGREEN)
|
||||
|
||||
col = 2
|
||||
row = 170
|
||||
Label(wri, row, col, 'Radio buttons')
|
||||
# Radio buttons
|
||||
row = 185
|
||||
self.rb = RadioButtons(BLUE, self.rbcb) # color of selected button
|
||||
self.rb0 = None
|
||||
for t in table_radiobuttons:
|
||||
button = self.rb.add_button(wri, row, col, textcolor = WHITE,
|
||||
fgcolor = BLUE, bgcolor = DARKBLUE, shape=CIRCLE, height = 30, **t)
|
||||
if self.rb0 is None: # Save for reset button callback
|
||||
self.rb0 = button
|
||||
col+= 35
|
||||
# Checkbox
|
||||
col+= 35
|
||||
Label(wri, row - 15, col, 'Checkbox and LED')
|
||||
Checkbox(wri, row, col, callback=self.cbcb)
|
||||
col+= 40
|
||||
self.led = LED(wri, row, col, color=YELLOW, bdcolor=GREEN)
|
||||
CloseButton(wri)
|
||||
asyncio.create_task(run(dial, lbltim, m0, scale))
|
||||
|
||||
|
||||
def callback(self, button, buttons, val):
|
||||
buttons[2].greyed_out(val)
|
||||
|
||||
def rbcb(self, button, val):
|
||||
print('RadioButtons callback', val)
|
||||
|
||||
def rstcb(self, button):
|
||||
print('Reset button: init ButtonList and RadioButtons, do full refresh.')
|
||||
self.bs.value(self.bs0)
|
||||
self.rb.value(self.rb0)
|
||||
asyncio.create_task(full_refresh())
|
||||
|
||||
def cbcb(self, cb):
|
||||
self.led.value(cb.value())
|
||||
gc.collect()
|
||||
print('Free RAM:', gc.mem_free())
|
||||
|
||||
async def run(dial, lbltim, m0, scale):
|
||||
days = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
|
||||
'Sunday')
|
||||
months = ('Jan', 'Feb', 'March', 'April', 'May', 'June', 'July',
|
||||
'Aug', 'Sept', 'Oct', 'Nov', 'Dec')
|
||||
uv = lambda phi : cmath.rect(1, phi) # Return a unit vector of phase phi
|
||||
pi = cmath.pi
|
||||
hrs = Pointer(dial)
|
||||
mins = Pointer(dial)
|
||||
secs = Pointer(dial)
|
||||
|
||||
hstart = 0 + 0.7j # Pointer lengths and position at top
|
||||
mstart = 0 + 0.92j
|
||||
sstart = 0 + 0.92j
|
||||
|
||||
cv = -1.0 # Scale
|
||||
dv = 0.005
|
||||
while True:
|
||||
t = utime.localtime()
|
||||
hrs.value(hstart * uv(-t[3]*pi/6 - t[4]*pi/360), YELLOW)
|
||||
mins.value(mstart * uv(-t[4] * pi/30), YELLOW)
|
||||
secs.value(sstart * uv(-t[5] * pi/30), RED)
|
||||
lbltim.value('{:02d}.{:02d}.{:02d}'.format(t[3], t[4], t[5]))
|
||||
dial.text('{} {} {} {}'.format(days[t[6]], t[2], months[t[1] - 1], t[0]))
|
||||
m0.value(t[5]/60)
|
||||
scale.value(cv)
|
||||
await asyncio.sleep_ms(200)
|
||||
cv += dv
|
||||
if abs(cv) > 1.0:
|
||||
dv = -dv
|
||||
cv += dv
|
||||
|
||||
|
||||
def test():
|
||||
if ssd.height < 240 or ssd.width < 320:
|
||||
print(' This test requires a display of at least 320x240 pixels.')
|
||||
else:
|
||||
print('Testing micro-gui...')
|
||||
Screen.change(FooScreen)
|
||||
|
||||
test()
|
|
@ -1,15 +1,12 @@
|
|||
# ili9341_pico.py Customise for your hardware config
|
||||
# pico_epaper_42.py on my PCB (non-standard connection)
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2021 Peter Hinch
|
||||
# Copyright (c) 2023 Peter Hinch
|
||||
|
||||
# As written, supports:
|
||||
# ili9341 240x320 displays on Pi Pico
|
||||
# Edit the driver import for other displays.
|
||||
|
||||
# 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
|
||||
# possible before importing other modules.
|
||||
# This Hardware_setup.py is for # https://www.waveshare.com/pico-epaper-4.2.htm
|
||||
# wired to the Pico using the ribbon cable rather than the default socket.
|
||||
# This was to enable testing using my ILI9341 PCB and its pushbuttons.
|
||||
# Use commented-out code below if using the built-in Pico socket.
|
||||
|
||||
# WIRING
|
||||
# Pico Display
|
||||
|
@ -33,15 +30,19 @@
|
|||
from machine import Pin, SPI, freq
|
||||
import gc
|
||||
|
||||
from drivers.ili93xx.ili9341 import ILI9341 as SSD
|
||||
from drivers.epaper.pico_epaper_42 import EPD as SSD
|
||||
freq(250_000_000) # RP2 overclock
|
||||
# Create and export an SSD instance
|
||||
prst = Pin(9, Pin.OUT, value=1)
|
||||
pcs = Pin(10, Pin.OUT, value=1)
|
||||
pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins
|
||||
spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4), baudrate=30_000_000)
|
||||
busy = Pin(15, Pin.IN)
|
||||
spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4), baudrate=4_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, usd=True)
|
||||
|
||||
# Using normal socket connection default args apply
|
||||
# ssd = SSD()
|
||||
ssd = SSD(spi, pcs, pdc, prst, busy)
|
||||
gc.collect()
|
||||
from gui.core.ugui import Display, quiet
|
||||
# quiet()
|
||||
|
@ -53,4 +54,6 @@ prev = Pin(18, Pin.IN, Pin.PULL_UP) # Move to previous control
|
|||
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||
decrease = Pin(17, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
||||
# display = Display(ssd, nxt, sel, prev) # 3-button mode
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease, 4) # Encoder mode
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease) # 5-button mode
|
||||
ssd.wait_until_ready() # Blocking wait
|
||||
ssd.set_partial() # Subsequent refreshes are partial
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# pico_epaper_42.py on my PCB (non-standard connection)
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2023 Peter Hinch
|
||||
|
||||
# This Hardware_setup.py is for # https://www.waveshare.com/pico-epaper-4.2.htm
|
||||
# wired to the Pico using the ribbon cable rather than the default socket.
|
||||
# This was to enable testing using my ILI9341 PCB and its pushbuttons.
|
||||
# Use commented-out code below if using the built-in Pico socket.
|
||||
|
||||
# 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
|
||||
|
||||
from drivers.epaper.pico_epaper_42 import EPD as SSD
|
||||
freq(250_000_000) # RP2 overclock
|
||||
# Create and export an SSD instance
|
||||
prst = Pin(9, Pin.OUT, value=1)
|
||||
pcs = Pin(10, Pin.OUT, value=1)
|
||||
pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins
|
||||
busy = Pin(15, Pin.IN)
|
||||
spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4), baudrate=4_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
|
||||
# Using normal socket connection default args apply
|
||||
# ssd = SSD()
|
||||
ssd = SSD(spi, pcs, pdc, prst, busy)
|
||||
gc.collect()
|
||||
from gui.core.ugui import Display, quiet
|
||||
# quiet()
|
||||
# Create and export a Display instance
|
||||
# Define control buttons
|
||||
nxt = Pin(19, Pin.IN, Pin.PULL_UP) # Move to next control
|
||||
sel = Pin(16, Pin.IN, Pin.PULL_UP) # Operate current control
|
||||
prev = Pin(18, Pin.IN, Pin.PULL_UP) # Move to previous control
|
||||
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||
decrease = Pin(17, Pin.IN, Pin.PULL_UP) # Decrease control's value
|
||||
# display = Display(ssd, nxt, sel, prev) # 3-button mode
|
||||
display = Display(ssd, nxt, sel, prev, increase, decrease) # 5-button mode
|
||||
ssd.wait_until_ready() # Blocking wait
|
||||
ssd.set_partial() # Subsequent refreshes are partial
|
Ładowanie…
Reference in New Issue