kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
Prior to merge.
rodzic
f4c6631c91
commit
bfba0b71a6
154
DRIVERS.md
154
DRIVERS.md
|
@ -1,7 +1,7 @@
|
|||
# Display drivers for nano-gui
|
||||
|
||||
The nano-gui project currently supports three display technologies: OLED (color
|
||||
and monochrome), color TFT, and monochrome Sharp displays.
|
||||
The nano-gui project currently supports four display technologies: OLED (color
|
||||
and monochrome), color TFT, monochrome Sharp displays and EPD (ePaper/eInk).
|
||||
|
||||
# Contents
|
||||
|
||||
|
@ -23,10 +23,14 @@ and monochrome), color TFT, and monochrome Sharp displays.
|
|||
6.4.1 [Micropower applications](./DRIVERS.md#641-micropower-applications)
|
||||
6.5 [Resources](./DRIVERS.md#65-resources)
|
||||
7. [ePaper displays](./DRIVERS.md#7-epaper-displays)
|
||||
7.1 [Waveshare eInk Display HAT](./DRIVERS.md#71-waveshare-eink-display-hat)
|
||||
7.1 [Adafruit flexible eInk Display](./DRIVERS.md#71-adafruit-flexible-eink-display)
|
||||
7.1.1 [EPD constructor args](./DRIVERS.md#711-epd-constructor-args)
|
||||
7.1.2 [EPD public methods](./DRIVERS.md#712-epd-public-methods)
|
||||
8. [Writing device drivers](./DRIVERS.md#8-writing-device-drivers)
|
||||
7.2 [Waveshare eInk Display HAT](./DRIVERS.md#71-waveshare-eink-display-hat)
|
||||
7.2.1 [EPD constructor args](./DRIVERS.md#711-epd-constructor-args)
|
||||
7.2.2 [EPD public methods](./DRIVERS.md#712-epd-public-methods)
|
||||
8. [EPD Asynchronous support](./DRIVERS.md#8-epd-asynchronous-support)
|
||||
9. [Writing device drivers](./DRIVERS.md#8-writing-device-drivers)
|
||||
|
||||
###### [Main README](./README.md)
|
||||
|
||||
|
@ -505,11 +509,91 @@ device retaining the image indefinitely. Some devices such as the Waveshare
|
|||
units perform the refresh internally. Earlier devices required the driver to
|
||||
perform this, tying up the CPU for the duration.
|
||||
|
||||
## 7.1 Waveshare eInk Display HAT
|
||||
The drivers are compatible with `uasyncio`. One approach is to use synchronous
|
||||
methods only and the standard demos (some of which use `uasyncio`) may be run.
|
||||
However copying the framebuffer to the device blocks for some time - 250ms or
|
||||
more - which may be problematic for applications which need to respond to
|
||||
external events. A specific asynchronous mode provides support for reducing
|
||||
blocking time. See [EPD Asynchronous support](./DRIVERS.md#8-epd-asynchronous-support).
|
||||
|
||||
This 2.7" 176*274 portrait mode display is designed for the Raspberry Pi.
|
||||
Details [here](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT). The driver
|
||||
is cross-platform.
|
||||
## 7.1 Adafruit flexible eInk Display
|
||||
|
||||
The driver assumes an Adafruit 2.9 inch 296*128 pixel flexible
|
||||
[display](https://www.adafruit.com/product/4262) interfaced via their
|
||||
[interface breakout](https://www.adafruit.com/product/4224).
|
||||
|
||||
This is currently my preferred ePaper setup, not least because the breakout
|
||||
enables the display to be completely powered down. This facilitates micropower
|
||||
applications: the host shuts down the display before going into deep sleep.
|
||||
|
||||
The driver is cross platform and supports landscape or portrait mode. To keep
|
||||
the buffer size down (to 4736 bytes) there is no greyscale support.
|
||||
|
||||
##### Wiring
|
||||
|
||||
The following assumes a Pyboard host. Pyboard pin numbers are based on hardware
|
||||
SPI 2 and my arbitrary choice of GPIO. All may be changed and soft SPI may be
|
||||
used.
|
||||
|
||||
| Pyb | Breakout |
|
||||
|:---:|:---------:|
|
||||
| Vin | Vin (1) |
|
||||
| Gnd | Gnd (3) |
|
||||
| Y8 | MOSI (6) |
|
||||
| Y6 | SCK (4) |
|
||||
| Y4 | BUSY (11) | (Low = Busy)
|
||||
| Y3 | RST (10) |
|
||||
| Y2 | CS (7) |
|
||||
| Y1 | DC (8) |
|
||||
|
||||
In normal use the `ENA` pin (12) may be left unconnected. For micropower use,
|
||||
see below.
|
||||
|
||||
### 7.1.1 EPD constructor args
|
||||
* `spi` An initialised SPI bus instance. The device can support clock rates of
|
||||
upto 10MHz.
|
||||
* `cs` An initialised output pin. Initial value should be 1.
|
||||
* `dc` An initialised output pin. Initial value should be 0.
|
||||
* `rst` An initialised output pin. Initial value should be 1.
|
||||
* `busy` An initialised input pin.
|
||||
* `landscape=True` By default the long axis is horizontal.
|
||||
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
|
||||
[EPD Asynchronous support](./DRIVERS.md#8-epd-asynchronous-support).
|
||||
|
||||
### 7.1.2 EPD public methods
|
||||
|
||||
##### Synchronous methods
|
||||
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
||||
is called by the constructor. It needs to explicitly be called to exit from a
|
||||
deep sleep.
|
||||
* `sleep` No args. Puts the display into deep sleep. If called while a refresh
|
||||
is in progress it will block until the refresh is complete. `sleep` should be
|
||||
called before a power down to avoid leaving the display in an abnormal state.
|
||||
* `ready` No args. After issuing a `refresh` the device will become busy for
|
||||
a period: `ready` status should be checked before issuing `refresh`.
|
||||
* `wait_until_ready` No args. Pause until the device is ready.
|
||||
|
||||
##### Asynchronous methods
|
||||
* `updated` Asynchronous. No args. Pause until the framebuffer has been copied
|
||||
to the display.
|
||||
* `wait` Asynchronous. No args. Pause until the display refresh is complete.
|
||||
|
||||
|
||||
|
||||
** POWER DOWN HARDWARE **
|
||||
|
||||
## 7.2 Waveshare eInk Display HAT
|
||||
|
||||
This 2.7" 176*274 display is designed for the Raspberry Pi and is detailed
|
||||
[here](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT).
|
||||
|
||||
I bought two of these units from different sources. Both have hardware issues
|
||||
discussed [here](https://forum.micropython.org/viewtopic.php?f=2&t=9564). I
|
||||
have failed to achieve consistent behaviour. Units behave perfectly one day and
|
||||
fail the next. I published this driver on the assumption that I was sold
|
||||
dubious Chinese clones and that genuine ones would be reliable.
|
||||
|
||||
The driver is cross-platform.
|
||||
|
||||
##### Wiring
|
||||
|
||||
|
@ -520,7 +604,6 @@ connector is shown, with connections to a Pyboard to match `waveshare_setup.py`.
|
|||
Connections may be adapted for other MicroPython targets. The board may be
|
||||
powered from 5V or 3.3V: there is a regulator on board.
|
||||
|
||||
|:---:|:----:|:--:|:--:|:----:|:---:|
|
||||
| Pyb | | L | R | | Pyb |
|
||||
|:---:|:----:|:--:|:--:|:----:|:---:|
|
||||
| Vin | VIN | 2 | 1 | | |
|
||||
|
@ -538,24 +621,69 @@ powered from 5V or 3.3V: there is a regulator on board.
|
|||
|
||||
Pins 26-40 unused and omitted.
|
||||
|
||||
### 7.1.1 EPD constructor args
|
||||
### 7.2.1 EPD constructor args
|
||||
* `spi` An initialised SPI bus instance. The device can support clock rates of
|
||||
upto 2MHz.
|
||||
* `cs` An initialised output pin. Initial value should be 1.
|
||||
* `dc` An initialised output pin. Initial value should be 0.
|
||||
* `rst` An initialised output pin. Initial value should be 1.
|
||||
* `busy` An initialised input pin.
|
||||
* `landscape=False` By default the long axis is vertical.
|
||||
* `asyn=False`
|
||||
|
||||
### 7.1.2 EPD public methods
|
||||
### 7.2.2 EPD public methods
|
||||
|
||||
##### Synchronous methods
|
||||
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
||||
is called by the constructor. It needs to explicitly be called to exit from a
|
||||
deep sleep.
|
||||
* `sleep` No args. Puts the display into deep sleep.
|
||||
* `sleep` No args. Puts the display into deep sleep. If called while a refresh
|
||||
is in progress it will block until the refresh is complete. `sleep` should be
|
||||
called before a power down to avoid leaving the display in an abnormal state.
|
||||
* `ready` No args. After issuing a `refresh` the device will become busy for
|
||||
a period: `ready` status should be checked before issuing `refresh`.
|
||||
* `wait_until_ready` No args. Pause until the device is ready.
|
||||
|
||||
# 8. Writing device drivers
|
||||
##### Asynchronous methods
|
||||
* `updated` Asynchronous. No args. Pause until the framebuffer has been copied
|
||||
to the display.
|
||||
* `wait` Asynchronous. No args. Pause until the display refresh is complete.
|
||||
|
||||
# 8. EPD Asynchronous support
|
||||
|
||||
Normally when GUI code issues
|
||||
```python
|
||||
refresh(ssd)
|
||||
```
|
||||
display data is copied to the device and a physical refresh is initiated. The
|
||||
code blocks - typically for 250ms or more - before returning, with physical
|
||||
refresh being performed by the display hardware and taking several seconds.
|
||||
This blocking period, which may be longer on non-Pyboard hosts, is too long for
|
||||
many `uasyncio` applications.
|
||||
|
||||
If an `EPD` is instantiated with `asyn=True` the process of copying the data to
|
||||
the device is performed by a task which periodically yields to the scheduler.
|
||||
By default blocking is limited to around 30ms.
|
||||
|
||||
An `updated` method allows user code to pause after issuing `refresh` before
|
||||
modifying the content of the framebuf - at which time the old data has been
|
||||
entirely copied to the hardware. The `wait` method will pause until any
|
||||
physical update is complete.
|
||||
|
||||
```python
|
||||
while True:
|
||||
# Normal procedure before refresh, but 10s sleep should mean it always returns immediately
|
||||
await ssd.wait()
|
||||
refresh(ssd) # Launches ._as_show()
|
||||
await ssd.updated()
|
||||
# Content has now been shifted out so coros can update
|
||||
# framebuffer in background
|
||||
evt.set()
|
||||
evt.clear()
|
||||
await asyncio.sleep(20) # Allow for slow refresh
|
||||
```
|
||||
|
||||
# 9. Writing device drivers
|
||||
|
||||
Device drivers capable of supporting `nanogui` can be extremely simple: see
|
||||
`drivers/sharp/sharp.py` for a minimal example. It should be noted that the
|
||||
|
|
|
@ -516,7 +516,7 @@ Keyword only args:
|
|||
11. `label=None` A text string will cause a `Label` to be drawn below the
|
||||
meter. An integer will create a `Label` of that width for later use.
|
||||
12. `style=Meter.LINE` The pointer is a horizontal line. `Meter.BAR` causes a
|
||||
vertical bar to be displayed.
|
||||
vertical bar to be displayed. Much easier to read on monochrome displays.
|
||||
13. `legends=None` If a tuple of strings is passed, `Label` instances will be
|
||||
displayed to the right hand side of the meter, starting at the bottom. E.G.
|
||||
`('0.0', '0.5', '1.0')`
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# epd96_asyn.py Config for asynchronous applications on 2.9" ePaper.
|
||||
# Customise for your hardware config
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# Supports Adafruit 2.9" monochrome EPD with interface board connected to Pyboard.
|
||||
# Interface breakout: https://www.adafruit.com/product/4224
|
||||
# Display: https://www.adafruit.com/product/4262
|
||||
|
||||
# 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. Adafruit schematic linked on the product web pagerefers to a different
|
||||
# device. These are the pins on the physical board.
|
||||
# Pyb Breakout
|
||||
# Vin Vin (1)
|
||||
# Gnd Gnd (3)
|
||||
# Y8 MOSI (6)
|
||||
# Y6 SCK (4)
|
||||
# Y4 BUSY (11) (Low = Busy)
|
||||
# Y3 RST (10)
|
||||
# Y2 CS (7)
|
||||
# Y1 DC (8)
|
||||
import machine
|
||||
import gc
|
||||
|
||||
from drivers.epaper.epd29 import EPD as SSD
|
||||
|
||||
pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
|
||||
pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
||||
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||
|
||||
# Baudrate. Adafruit use 1MHz at
|
||||
# https://learn.adafruit.com/adafruit-eink-display-breakouts/circuitpython-code-2
|
||||
# Datasheet P35 indicates up to 10MHz.
|
||||
spi = machine.SPI(2, baudrate=5_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, pbusy, asyn=True) # Create a display instance
|
|
@ -1,9 +1,12 @@
|
|||
# epd96_demo.py Allow standard demos to run on ePaper. Customise for your hardware config
|
||||
# epd96_demo.py Allow standard demos to run on ePaper.
|
||||
# Customise for your hardware config.
|
||||
# Beware of running demos for long as they refresh the display more frequently
|
||||
# than is advised by Adafruit.
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# As written, supports Adafruit 2.9" monochrome EPD with interface board.
|
||||
# Supports Adafruit 2.9" monochrome EPD with interface board connected to Pyboard.
|
||||
# Interface breakout: https://www.adafruit.com/product/4224
|
||||
# Display: https://www.adafruit.com/product/4262
|
||||
|
||||
|
@ -11,8 +14,8 @@
|
|||
# when instantiating the frame buffer. The aim is to do this as early as
|
||||
# possible before importing other modules.
|
||||
|
||||
# WIRING. Adafruit schematic is incorrect in that it references a nonexistent
|
||||
# SD card so that interface has an extra pin.
|
||||
# WIRING. Adafruit schematic linked on the product web pagerefers to a different
|
||||
# device. These are the pins on the physical board.
|
||||
# Pyb Breakout
|
||||
# Vin Vin (1)
|
||||
# Gnd Gnd (3)
|
||||
|
@ -32,9 +35,10 @@ pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
|||
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||
|
||||
# Baudrate from https://learn.adafruit.com/adafruit-eink-display-breakouts/circuitpython-code-2
|
||||
# Baudrate. Adafruit use 1MHz at
|
||||
# https://learn.adafruit.com/adafruit-eink-display-breakouts/circuitpython-code-2
|
||||
# Datasheet P35 indicates up to 10MHz.
|
||||
spi = machine.SPI(2, baudrate=1_000_000)
|
||||
spi = machine.SPI(2, baudrate=5_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, pbusy) # Create a display instance
|
||||
ssd.demo_mode = True
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# waveshare_demo.py Allow standard demos to run on ePaper. Customise for your hardware config
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# As written, supports:
|
||||
# Waveshare 2.7" 264h*176w monochrome ePaper display:
|
||||
# https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT
|
||||
|
||||
# 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 Pin numbers refer to RPI connector.
|
||||
# Pyb ePaper
|
||||
# Vin Vcc (2)
|
||||
# Gnd Gnd (9)
|
||||
# Y8 DIN MOSI (19)
|
||||
# Y6 CLK SCK (23)
|
||||
# Y4 BUSY (18) (Low = Busy)
|
||||
# Y3 RST (11)
|
||||
# Y2 CS (24)
|
||||
# Y1 DC (22)
|
||||
import machine
|
||||
import gc
|
||||
|
||||
from drivers.epaper.epaper2in7_fb import EPD as SSD
|
||||
|
||||
pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
|
||||
pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
||||
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||
spi = machine.SPI(2, baudrate=4_000_000) # From https://github.com/mcauser/micropython-waveshare-epaper/blob/master/examples/2in9-hello-world/test.py
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, pbusy, landscape=True) # Create a display instance
|
||||
ssd.demo_mode = True
|
|
@ -0,0 +1,36 @@
|
|||
# waveshare_setup.py Customise for your hardware config
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# As written, supports:
|
||||
# Waveshare 2.7" 264h*176w monochrome ePaper display:
|
||||
# https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT
|
||||
|
||||
# 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 Pin numbers refer to RPI connector.
|
||||
# Pyb ePaper
|
||||
# Vin Vcc (2)
|
||||
# Gnd Gnd (9)
|
||||
# Y8 DIN MOSI (19)
|
||||
# Y6 CLK SCK (23)
|
||||
# Y4 BUSY (18) (Low = Busy)
|
||||
# Y3 RST (11)
|
||||
# Y2 CS (24)
|
||||
# Y1 DC (22)
|
||||
import machine
|
||||
import gc
|
||||
|
||||
from drivers.epaper.epaper2in7_fb import EPD as SSD
|
||||
|
||||
pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
|
||||
pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
||||
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||
spi = machine.SPI(2, baudrate=4_000_000) # From https://github.com/mcauser/micropython-waveshare-epaper/blob/master/examples/2in9-hello-world/test.py
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, pbusy, landscape=False, asyn=True) # Create a display instance
|
||||
#ssd.demo_mode = True
|
|
@ -0,0 +1,302 @@
|
|||
# epaper2in7_fb.py nanogui driver for ePpaper 2.7" display
|
||||
# Tested with Pyboard linked to Raspberry Pi 2.7" E-Ink Display HAT
|
||||
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
|
||||
# Optimisations to reduce allocations and RAM use.
|
||||
|
||||
# Copyright (c) Peter Hinch 2020
|
||||
# Released under the MIT license see LICENSE
|
||||
|
||||
# Based on the following sources:
|
||||
# https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT
|
||||
# MicroPython Waveshare 2.7" Black/White GDEW027W3 e-paper display driver
|
||||
# https://github.com/mcauser/micropython-waveshare-epaper referred to as "mcauser"
|
||||
# https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in7.py ("official")
|
||||
|
||||
import framebuf
|
||||
import uasyncio as asyncio
|
||||
from time import sleep_ms, ticks_ms, ticks_us, ticks_diff
|
||||
|
||||
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, cs, dc, rst, busy, landscape=False, asyn=False):
|
||||
self._spi = spi
|
||||
self._cs = cs # Pins
|
||||
self._dc = dc
|
||||
self._rst = rst
|
||||
self._busy = busy
|
||||
self._lsc = landscape
|
||||
self._asyn = asyn
|
||||
self._as_busy = False # Set immediately on start of task. Cleared when busy pin is logically false (physically 1).
|
||||
self._updated = asyncio.Event()
|
||||
# Dimensions in pixels. Waveshare code is portrait mode.
|
||||
# Public bound variables required by nanogui.
|
||||
self.width = 264 if landscape else 176
|
||||
self.height = 176 if landscape else 264
|
||||
self.demo_mode = False # Special mode enables demos to run
|
||||
self._buffer = bytearray(self.height * self.width // 8)
|
||||
self._mvb = memoryview(self._buffer)
|
||||
mode = framebuf.MONO_VLSB if landscape else framebuf.MONO_HLSB
|
||||
super().__init__(self._buffer, self.width, self.height, mode)
|
||||
self.init()
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(command)
|
||||
self._cs(1)
|
||||
if data is not None:
|
||||
self._dc(1)
|
||||
self._cs(0)
|
||||
self._spi.write(data)
|
||||
self._cs(1)
|
||||
|
||||
def init(self):
|
||||
# Hardware reset
|
||||
self._rst(1)
|
||||
sleep_ms(200)
|
||||
self._rst(0)
|
||||
sleep_ms(200) # 5ms in Waveshare code
|
||||
self._rst(1)
|
||||
sleep_ms(200)
|
||||
# Initialisation
|
||||
cmd = self._command
|
||||
cmd(b'\x01', b'\x03\x00\x2B\x2B\x09') # POWER_SETTING: VDS_EN VDG_EN, VCOM_HV VGHL_LV[1] VGHL_LV[0], VDH, VDL, VDHR
|
||||
cmd(b'\x06', b'\x07\x07\x17') # BOOSTER_SOFT_START
|
||||
cmd(b'\xf8', b'\x60\xA5') # POWER_OPTIMIZATION
|
||||
cmd(b'\xf8', b'\x89\xA5')
|
||||
cmd(b'\xf8', b'\x90\x00')
|
||||
cmd(b'\xf8', b'\x93\x2A')
|
||||
cmd(b'\xf8', b'\xA0\xA5')
|
||||
cmd(b'\xf8', b'\xA1\x00')
|
||||
cmd(b'\xf8', b'\x73\x41')
|
||||
cmd(b'\x16', b'\x00') # PARTIAL_DISPLAY_REFRESH
|
||||
cmd(b'\x04') # POWER_ON
|
||||
self.wait_until_ready()
|
||||
cmd(b'\x00', b'\xAF') # PANEL_SETTING: KW-BF, KWR-AF, BWROTP 0f
|
||||
cmd(b'\x30', b'\x3A') # PLL_CONTROL: 3A 100HZ, 29 150Hz, 39 200HZ 31 171HZ
|
||||
cmd(b'\x50', b'\x57') # Vcom and data interval setting (PGH)
|
||||
cmd(b'\x82', b'\x12') # VCM_DC_SETTING_REGISTER
|
||||
sleep_ms(2) # No delay in official code
|
||||
# Set LUT. Local bytes objects reduce RAM usage.
|
||||
|
||||
# Values used by mcauser
|
||||
#lut_vcom_dc =\
|
||||
#b'\x00\x00\x00\x0F\x0F\x00\x00\x05\x00\x32\x32\x00\x00\x02\x00'\
|
||||
#b'\x0F\x0F\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
#b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
#lut_ww =\
|
||||
#b'\x50\x0F\x0F\x00\x00\x05\x60\x32\x32\x00\x00\x02\xA0\x0F\x0F'\
|
||||
#b'\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
#b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # R21H
|
||||
#lut_bb =\
|
||||
#b'\xA0\x0F\x0F\x00\x00\x05\x60\x32\x32\x00\x00\x02\x50\x0F\x0F'\
|
||||
#b'\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
#b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # R24H b
|
||||
|
||||
# Values from official code:
|
||||
lut_vcom_dc =\
|
||||
b'\x00\x00\x00\x08\x00\x00\x00\x02\x60\x28\x28\x00\x00\x01\x00'\
|
||||
b'\x14\x00\x00\x00\x01\x00\x12\x12\x00\x00\x01\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
lut_ww =\
|
||||
b'\x40\x08\x00\x00\x00\x02\x90\x28\x28\x00\x00\x01\x40\x14\x00'\
|
||||
b'\x00\x00\x01\xA0\x12\x12\x00\x00\x01\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
lut_bb =\
|
||||
b'\x80\x08\x00\x00\x00\x02\x90\x28\x28\x00\x00\x01\x80\x14\x00'\
|
||||
b'\x00\x00\x01\x50\x12\x12\x00\x00\x01\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
# Both agree on this:
|
||||
lut_bw = lut_ww # R22H r
|
||||
lut_wb = lut_bb # R23H w
|
||||
cmd(b'\x20', lut_vcom_dc) # LUT_FOR_VCOM vcom
|
||||
cmd(b'\x21', lut_ww) # LUT_WHITE_TO_WHITE ww --
|
||||
cmd(b'\x22', lut_bw) # LUT_BLACK_TO_WHITE bw r
|
||||
cmd(b'\x23', lut_bb) # LUT_WHITE_TO_BLACK wb w
|
||||
cmd(b'\x24', lut_wb) # LUT_BLACK_TO_BLACK bb b
|
||||
print('Init Done.')
|
||||
|
||||
def wait_until_ready(self):
|
||||
sleep_ms(50)
|
||||
t = ticks_ms()
|
||||
while not self.ready():
|
||||
sleep_ms(100)
|
||||
dt = ticks_diff(ticks_ms(), t)
|
||||
print('wait_until_ready {}ms {:5.1f}mins'.format(dt, dt/60_000))
|
||||
|
||||
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):
|
||||
await self._updated.wait()
|
||||
|
||||
# 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._as_busy or (self._busy() == 0)) # 0 == busy
|
||||
|
||||
async def _as_show(self, buf1=bytearray(1)):
|
||||
mvb = self._mvb
|
||||
send = self._spi.write
|
||||
cmd = self._command
|
||||
cmd(b'\x10') # DATA_START_TRANSMISSION_1
|
||||
self._dc(1) # For some reason don't need to deassert CS here
|
||||
buf1[0] = 0xff
|
||||
t = ticks_ms()
|
||||
for i in range(len(mvb)):
|
||||
self._cs(0) # but do when copying the framebuf
|
||||
send(buf1)
|
||||
if not(i & 0x1f) and (ticks_diff(ticks_ms(), t) > 20):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
self._cs(1)
|
||||
cmd(b'\x13') # DATA_START_TRANSMISSION_2 not in datasheet
|
||||
|
||||
self._dc(1)
|
||||
# Necessary to deassert CS after each byte otherwise display does not
|
||||
# clear down correctly
|
||||
t = ticks_ms()
|
||||
if self._lsc: # Landscape mode
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for i in range(len(mvb)):
|
||||
self._cs(0)
|
||||
buf1[0] = mvb[idx] # INVERSION HACK ~data
|
||||
send(buf1)
|
||||
self._cs(1)
|
||||
idx -= self.width
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
if not(i & 0x1f) and (ticks_diff(ticks_ms(), t) > 20):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
else:
|
||||
for i, b in enumerate(mvb):
|
||||
self._cs(0)
|
||||
buf1[0] = b # INVERSION HACK ~data
|
||||
send(buf1)
|
||||
self._cs(1)
|
||||
if not(i & 0x1f) and (ticks_diff(ticks_ms(), t) > 20):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
|
||||
self._updated.set() # framebuf has now been copied to the device
|
||||
self._updated.clear()
|
||||
cmd(b'\x12') # DISPLAY_REFRESH
|
||||
await asyncio.sleep(1)
|
||||
while self._busy() == 0:
|
||||
await asyncio.sleep_ms(200) # Don't release lock until update is complete
|
||||
self._as_busy = False
|
||||
|
||||
# draw the current frame memory. Blocking time ~180ms
|
||||
def show(self, buf1=bytearray(1)):
|
||||
if self._asyn:
|
||||
if self._as_busy:
|
||||
raise RuntimeError('Cannot refresh: display is busy.')
|
||||
self._as_busy = True
|
||||
asyncio.create_task(self._as_show())
|
||||
return
|
||||
t = ticks_us()
|
||||
mvb = self._mvb
|
||||
send = self._spi.write
|
||||
cmd = self._command
|
||||
cmd(b'\x10') # DATA_START_TRANSMISSION_1
|
||||
self._dc(1) # For some reason don't need to deassert CS here
|
||||
buf1[0] = 0xff
|
||||
for i in range(len(mvb)):
|
||||
self._cs(0) # but do when copying the framebuf
|
||||
send(buf1)
|
||||
self._cs(1)
|
||||
cmd(b'\x13') # DATA_START_TRANSMISSION_2 not in datasheet
|
||||
|
||||
self._dc(1)
|
||||
# Necessary to deassert CS after each byte otherwise display does not
|
||||
# clear down correctly
|
||||
if self._lsc: # Landscape mode
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for _ in range(len(mvb)):
|
||||
self._cs(0)
|
||||
buf1[0] = mvb[idx] # INVERSION HACK ~data
|
||||
send(buf1)
|
||||
self._cs(1)
|
||||
idx -= self.width
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
else:
|
||||
for b in mvb:
|
||||
self._cs(0)
|
||||
buf1[0] = b # INVERSION HACK ~data
|
||||
send(buf1)
|
||||
self._cs(1)
|
||||
|
||||
cmd(b'\x12') # DISPLAY_REFRESH
|
||||
te = ticks_us()
|
||||
print('show time', ticks_diff(te, t)//1000, 'ms')
|
||||
if not self.demo_mode:
|
||||
# Immediate return to avoid blocking the whole application.
|
||||
# User should wait for ready before calling refresh()
|
||||
return
|
||||
self.wait_until_ready()
|
||||
sleep_ms(2000) # Give time for user to see result
|
||||
|
||||
|
||||
# to wake call init()
|
||||
def sleep(self):
|
||||
self._as_busy = False
|
||||
self.wait_until_ready()
|
||||
cmd = self._command
|
||||
cmd(b'\x50', b'\xf7') # From Waveshare code
|
||||
cmd(b'\x02') # POWER_OFF
|
||||
cmd(b'\x07', b'\xA5') # DEEP_SLEEP (Waveshare and mcauser)
|
||||
self._rst(0) # According to schematic this turns off the power
|
||||
|
||||
# Testing connections by toggling pins connected to 40-way connector and checking volts on small connector
|
||||
# All OK except rst: a 1 level produced only about 1.6V as against 3.3V for all other I/O.
|
||||
# Further the level on the 40-way connector read 2.9V as agains 3.3V for others. Suspect hardware problem,
|
||||
# ordered a second unit from Amazon.
|
||||
#import machine
|
||||
#import gc
|
||||
|
||||
#pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
|
||||
#pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
||||
#prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
#pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||
## baudrate
|
||||
## From https://github.com/mcauser/micropython-waveshare-epaper/blob/master/examples/2in9-hello-world/test.py 2MHz
|
||||
## From https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in7.py 4MHz
|
||||
#spi = machine.SPI(2, baudrate=2_000_000)
|
||||
#gc.collect() # Precaution before instantiating framebuf
|
||||
#epd = EPD(spi, pcs, pdc, prst, pbusy) # Create a display instance
|
||||
#sleep_ms(100)
|
||||
#epd.init()
|
||||
#print('Initialised')
|
||||
#epd.fill(1) # 1 seems to be white
|
||||
#epd.show()
|
||||
#sleep_ms(1000)
|
||||
#epd.fill(0)
|
||||
#epd.show()
|
||||
#epd._rst(0)
|
||||
#epd._dc(0) # Turn off power according to RPI code
|
|
@ -31,12 +31,13 @@ class EPD(framebuf.FrameBuffer):
|
|||
def rgb(r, g, b):
|
||||
return int((r > 127) or (g > 127) or (b > 127))
|
||||
|
||||
def __init__(self, spi, cs, dc, rst, busy, asyn=False):
|
||||
def __init__(self, spi, cs, dc, rst, busy, landscape=True, asyn=False):
|
||||
self._spi = spi
|
||||
self._cs = cs # Pins
|
||||
self._dc = dc
|
||||
self._rst = rst # Active low.
|
||||
self._busy = busy # Active low on IL0373
|
||||
self._lsc = landscape
|
||||
self._asyn = asyn
|
||||
# ._as_busy is set immediately on start of task. Cleared
|
||||
# when busy pin is logically false (physically 1).
|
||||
|
@ -44,15 +45,15 @@ class EPD(framebuf.FrameBuffer):
|
|||
self._updated = asyncio.Event()
|
||||
# Public bound variables required by nanogui.
|
||||
# Dimensions in pixels as seen by nanogui (landscape mode).
|
||||
self.width = 296
|
||||
self.height = 128
|
||||
self.width = 296 if landscape else 128
|
||||
self.height = 128 if landscape else 296
|
||||
# Other public bound variable.
|
||||
# Special mode enables demos written for generic displays to run.
|
||||
self.demo_mode = False
|
||||
|
||||
self._buffer = bytearray(self.height * self.width // 8)
|
||||
self._mvb = memoryview(self._buffer)
|
||||
mode = framebuf.MONO_VLSB
|
||||
mode = framebuf.MONO_VLSB if landscape else framebuf.MONO_HLSB
|
||||
super().__init__(self._buffer, self.width, self.height, mode)
|
||||
self.init()
|
||||
|
||||
|
@ -101,9 +102,9 @@ class EPD(framebuf.FrameBuffer):
|
|||
cmd(b'\x30', b'\x29')
|
||||
# Resolution 128w * 296h as required by IL0373
|
||||
cmd(b'\x61', b'\x80\x01\x28') # Note hex(296) == 0x128
|
||||
# Set VCM_DC. 0 is datasheet default. I think Adafruit send 0x50 (-2.6V) rather than 0x12 (-1.0V)
|
||||
# Set VCM_DC. Now clarified with Adafruit.
|
||||
# https://github.com/adafruit/Adafruit_CircuitPython_IL0373/issues/17
|
||||
cmd(b'\x82', b'\x12') # Set Vcom to -1.0V is my guess at Adafruit's intention.
|
||||
cmd(b'\x82', b'\x12') # Set Vcom to -1.0V
|
||||
sleep_ms(50)
|
||||
print('Init Done.')
|
||||
|
||||
|
@ -136,24 +137,33 @@ class EPD(framebuf.FrameBuffer):
|
|||
dat = self._data
|
||||
cmd(b'\x13')
|
||||
t = ticks_ms()
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for i in range(len(mvb)):
|
||||
buf1[0] = mvb[idx] ^ 0xff
|
||||
dat(buf1)
|
||||
idx -= wid
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
if not(i & 0x0f) and (ticks_diff(ticks_ms(), t) > _MAX_BLOCK):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
if self._lsc: # Landscape mode
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for i in range(len(mvb)):
|
||||
buf1[0] = ~mvb[idx]
|
||||
dat(buf1)
|
||||
idx -= wid
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
if not(i & 0x0f) and (ticks_diff(ticks_ms(), t) > _MAX_BLOCK):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
else:
|
||||
for i, b in enumerate(mvb):
|
||||
buf1[0] = ~b
|
||||
dat(buf1)
|
||||
if not(i & 0x0f) and (ticks_diff(ticks_ms(), t) > _MAX_BLOCK):
|
||||
await asyncio.sleep_ms(0)
|
||||
t = ticks_ms()
|
||||
|
||||
cmd(b'\x11') # Data stop
|
||||
self._updated.set()
|
||||
self._updated.clear()
|
||||
|
@ -174,7 +184,6 @@ class EPD(framebuf.FrameBuffer):
|
|||
asyncio.create_task(self._as_show())
|
||||
return
|
||||
|
||||
# t = ticks_us()
|
||||
mvb = self._mvb
|
||||
cmd = self._command
|
||||
dat = self._data
|
||||
|
@ -182,28 +191,32 @@ class EPD(framebuf.FrameBuffer):
|
|||
# busy pin low (True) and that it stays logically True until
|
||||
# refresh is complete. In my testing this doesn't happen.
|
||||
cmd(b'\x13')
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for _ in range(len(mvb)):
|
||||
buf1[0] = mvb[idx] ^ 0xff
|
||||
dat(buf1)
|
||||
idx -= wid
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
if self._lsc: # Landscape mode
|
||||
wid = self.width
|
||||
tbc = self.height // 8 # Vertical bytes per column
|
||||
iidx = wid * (tbc - 1) # Initial index
|
||||
idx = iidx # Index into framebuf
|
||||
vbc = 0 # Current vertical byte count
|
||||
hpc = 0 # Horizontal pixel count
|
||||
for _ in range(len(mvb)):
|
||||
buf1[0] = ~mvb[idx]
|
||||
dat(buf1)
|
||||
idx -= wid
|
||||
vbc += 1
|
||||
vbc %= tbc
|
||||
if not vbc:
|
||||
hpc += 1
|
||||
idx = iidx + hpc
|
||||
else:
|
||||
for b in mvb:
|
||||
buf1[0] = ~b
|
||||
dat(buf1)
|
||||
|
||||
cmd(b'\x11') # Data stop
|
||||
sleep_us(20) # Allow for data coming back: currently ignore this
|
||||
cmd(b'\x12') # DISPLAY_REFRESH
|
||||
# 258ms to get here on Pyboard D
|
||||
# Checking with scope, busy goes low now. For 4.9s.
|
||||
# te = ticks_us()
|
||||
# print('show time', ticks_diff(te, t)//1000, 'ms')
|
||||
if not self.demo_mode:
|
||||
# Immediate return to avoid blocking the whole application.
|
||||
# User should wait for ready before calling refresh()
|
||||
|
@ -213,6 +226,8 @@ class EPD(framebuf.FrameBuffer):
|
|||
|
||||
# to wake call init()
|
||||
def sleep(self):
|
||||
self._as_busy = False
|
||||
self.wait_until_ready()
|
||||
cmd = self._command
|
||||
# CDI: not sure about value or why we set this here. Copying Adafruit.
|
||||
cmd(b'\x50', b'\x17')
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# color_setup must set landcsape False, asyn True and must not set demo_mode
|
||||
# color_setup must set landcsape True, asyn True and must not set demo_mode
|
||||
import uasyncio as asyncio
|
||||
from color_setup import ssd
|
||||
from gui.core.writer import Writer
|
||||
|
@ -70,9 +70,13 @@ async def meter(evt):
|
|||
wri = Writer(ssd, arial10, verbose=False)
|
||||
row = 10
|
||||
col = 150
|
||||
m0 = Meter(wri, row, col, height = 80, width = 15, divisions = 4, legends=('0.0', '0.5', '1.0'))
|
||||
m1 = Meter(wri, row, col + 50, height = 80, width = 15, divisions = 4, legends=('-1', '0', '+1'))
|
||||
m2 = Meter(wri, row, col + 100, height = 80, width = 15, divisions = 4, legends=('-1', '0', '+1'))
|
||||
args = {'height' : 80,
|
||||
'width' : 15,
|
||||
'divisions' : 4,
|
||||
'style' : Meter.BAR}
|
||||
m0 = Meter(wri, row, col, legends=('0.0', '0.5', '1.0'), **args)
|
||||
m1 = Meter(wri, row, col + 50, legends=('-1', '0', '+1'), **args)
|
||||
m2 = Meter(wri, row, col + 100, legends=('-1', '0', '+1'), **args)
|
||||
random = xorshift64star(2**24 - 1)
|
||||
while True:
|
||||
steps = 10
|
||||
|
@ -114,6 +118,6 @@ try:
|
|||
except KeyboardInterrupt:
|
||||
# Defensive code: avoid leaving EPD hardware in an undefined state.
|
||||
print('Waiting for display to become idle')
|
||||
ssd.wait_until_ready() # Synchronous code
|
||||
ssd.sleep() # Synchronous code. May block for 5s if display is updating.
|
||||
finally:
|
||||
_ = asyncio.new_event_loop()
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
# waveshare_test.py Demo program for nano_gui on an Waveshare ePaper screen
|
||||
# https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# color_setup must set landcsape False, asyn True and must not set demo_mode
|
||||
import uasyncio as asyncio
|
||||
from color_setup import ssd
|
||||
from gui.core.writer import Writer
|
||||
from gui.core.nanogui import refresh
|
||||
from gui.widgets.meter import Meter
|
||||
from gui.widgets.label import Label
|
||||
|
||||
# Fonts
|
||||
import gui.fonts.arial10 as arial10
|
||||
import gui.fonts.courier20 as fixed
|
||||
import gui.fonts.font6 as small
|
||||
|
||||
# Some ports don't support uos.urandom.
|
||||
# See https://github.com/peterhinch/micropython-samples/tree/master/random
|
||||
def xorshift64star(modulo, seed = 0xf9ac6ba4):
|
||||
x = seed
|
||||
def func():
|
||||
nonlocal x
|
||||
x ^= x >> 12
|
||||
x ^= ((x << 25) & 0xffffffffffffffff) # modulo 2**64
|
||||
x ^= x >> 27
|
||||
return (x * 0x2545F4914F6CDD1D) % modulo
|
||||
return func
|
||||
|
||||
async def fields(evt):
|
||||
wri = Writer(ssd, fixed, verbose=False)
|
||||
wri.set_clip(False, False, False)
|
||||
textfield = Label(wri, 0, 2, wri.stringlen('longer'))
|
||||
numfield = Label(wri, 25, 2, wri.stringlen('99.990'), bdcolor=None)
|
||||
countfield = Label(wri, 0, 90, wri.stringlen('1'))
|
||||
n = 1
|
||||
random = xorshift64star(65535)
|
||||
while True:
|
||||
for s in ('short', 'longer', '1', ''):
|
||||
textfield.value(s)
|
||||
numfield.value('{:5.2f}'.format(random() /1000))
|
||||
countfield.value('{:1d}'.format(n))
|
||||
n += 1
|
||||
await evt.wait()
|
||||
|
||||
async def multi_fields(evt):
|
||||
wri = Writer(ssd, small, verbose=False)
|
||||
wri.set_clip(False, False, False)
|
||||
|
||||
nfields = []
|
||||
dy = small.height() + 10
|
||||
y = 80
|
||||
col = 20
|
||||
width = wri.stringlen('99.990')
|
||||
for txt in ('X:', 'Y:', 'Z:'):
|
||||
Label(wri, y, 0, txt)
|
||||
nfields.append(Label(wri, y, col, width, bdcolor=None)) # Draw border
|
||||
y += dy
|
||||
|
||||
random = xorshift64star(2**24 - 1)
|
||||
while True:
|
||||
for _ in range(10):
|
||||
for field in nfields:
|
||||
value = random() / 167772
|
||||
field.value('{:5.2f}'.format(value))
|
||||
await evt.wait()
|
||||
|
||||
async def meter(evt):
|
||||
wri = Writer(ssd, arial10, verbose=False)
|
||||
args = {'height' : 80,
|
||||
'width' : 15,
|
||||
'divisions' : 4,
|
||||
'style' : Meter.BAR}
|
||||
m0 = Meter(wri, 165, 2, legends=('0.0', '0.5', '1.0'), **args)
|
||||
m1 = Meter(wri, 165, 62, legends=('-1', '0', '+1'), **args)
|
||||
m2 = Meter(wri, 165, 122, legends=('-1', '0', '+1'), **args)
|
||||
random = xorshift64star(2**24 - 1)
|
||||
while True:
|
||||
steps = 10
|
||||
for n in range(steps + 1):
|
||||
m0.value(random() / 16777216)
|
||||
m1.value(n/steps)
|
||||
m2.value(1 - n/steps)
|
||||
await evt.wait()
|
||||
|
||||
async def main():
|
||||
# ssd.fill(1)
|
||||
# ssd.show()
|
||||
# await ssd.wait()
|
||||
refresh(ssd, True) # Clear display
|
||||
await ssd.wait()
|
||||
print('Ready')
|
||||
evt = asyncio.Event()
|
||||
asyncio.create_task(meter(evt))
|
||||
asyncio.create_task(multi_fields(evt))
|
||||
asyncio.create_task(fields(evt))
|
||||
while True:
|
||||
# Normal procedure before refresh, but 10s sleep should mean it always returns immediately
|
||||
await ssd.wait()
|
||||
refresh(ssd) # Launches ._as_show()
|
||||
await ssd.updated()
|
||||
# Content has now been shifted out so coros can update
|
||||
# framebuffer in background
|
||||
evt.set()
|
||||
evt.clear()
|
||||
await asyncio.sleep(9) # Allow for slow refresh
|
||||
|
||||
|
||||
tstr = '''Runs the following tests, updates every 10s
|
||||
fields() Label test with dynamic data.
|
||||
multi_fields() More Labels.
|
||||
meter() Demo of Meter object.
|
||||
'''
|
||||
|
||||
print(tstr)
|
||||
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print('Waiting for display to become idle')
|
||||
ssd.wait_until_ready() # Synchronous code
|
||||
finally:
|
||||
_ = asyncio.new_event_loop()
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 10 KiB |
Ładowanie…
Reference in New Issue