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
|
# Project status
|
||||||
|
|
||||||
|
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
|
||||||
|
|
||||||
July 2022: Add ESP32 touch pad support.
|
July 2022: Add ESP32 touch pad support.
|
||||||
|
|
||||||
June 2022: Add [QRMap](./README.md#620-qrmap-widget) and
|
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.
|
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.
|
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. [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 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.
|
[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
|
Supported displays are as per
|
||||||
[the nano-gui list](https://github.com/peterhinch/micropython-nano-gui/blob/master/README.md#12-description).
|
[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
|
In general ePaper and Sharp displays are unlikely to yield good results because
|
||||||
refresh times. I haven't tested these, or the Sharp displays.
|
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).
|
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.
|
* `adjust_vec.py` A pair of `Adjuster`s vary a vector.
|
||||||
* `bitmap.py` Demo of the `BitMap` widget showing a changing image.
|
* `bitmap.py` Demo of the `BitMap` widget showing a changing image.
|
||||||
* `qrcode.py` Display a QR code. Requires the uQR module.
|
* `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
|
### 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
|
There is an issue regarding ePaper displays discussed
|
||||||
[here](https://github.com/peterhinch/micropython-nano-gui/blob/master/README.md#312-monochrome-displays).
|
[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
|
The driver for the [Waveshare pico_epaper_42](https://www.waveshare.com/pico-epaper-4.2.htm)
|
||||||
time.
|
renders colored objects as black on white.
|
||||||
|
|
||||||
###### [Contents](./README.md#0-contents)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
|
@ -3030,6 +3042,27 @@ another from occurring.
|
||||||
```
|
```
|
||||||
The demo `gui/demos/audio.py` provides example usage.
|
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)
|
###### [Contents](./README.md#0-contents)
|
||||||
|
|
||||||
# Appendix 1 Application design
|
# 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.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2021 Peter Hinch
|
# Copyright (c) 2023 Peter Hinch
|
||||||
|
|
||||||
# As written, supports:
|
# This Hardware_setup.py is for # https://www.waveshare.com/pico-epaper-4.2.htm
|
||||||
# ili9341 240x320 displays on Pi Pico
|
# wired to the Pico using the ribbon cable rather than the default socket.
|
||||||
# Edit the driver import for other displays.
|
# This was to enable testing using my ILI9341 PCB and its pushbuttons.
|
||||||
|
# Use commented-out code below if using the built-in Pico socket.
|
||||||
# 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.
|
|
||||||
|
|
||||||
# WIRING
|
# WIRING
|
||||||
# Pico Display
|
# Pico Display
|
||||||
|
@ -33,15 +30,19 @@
|
||||||
from machine import Pin, SPI, freq
|
from machine import Pin, SPI, freq
|
||||||
import gc
|
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
|
freq(250_000_000) # RP2 overclock
|
||||||
# Create and export an SSD instance
|
# Create and export an SSD instance
|
||||||
prst = Pin(9, Pin.OUT, value=1)
|
prst = Pin(9, Pin.OUT, value=1)
|
||||||
pcs = Pin(10, Pin.OUT, value=1)
|
pcs = Pin(10, Pin.OUT, value=1)
|
||||||
pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins
|
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
|
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()
|
gc.collect()
|
||||||
from gui.core.ugui import Display, quiet
|
from gui.core.ugui import Display, quiet
|
||||||
# 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
|
increase = Pin(20, Pin.IN, Pin.PULL_UP) # Increase control's value
|
||||||
decrease = Pin(17, 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) # 3-button mode
|
# 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