Add support for new Pico displays.

pull/42/head
Peter Hinch 2022-09-05 11:33:05 +01:00
rodzic 97d3be6dc9
commit de0466cd7c
7 zmienionych plików z 455 dodań i 59 usunięć

Wyświetl plik

@ -21,6 +21,7 @@ Width and height are pixels.
| 1.44C | 128 | 128 | TFT | [ST7735R][4d] | [Adafruit 2088][5m] | |
| 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | |
| 1.3C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 4313][7m] | |
| 2.0C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare Pico LCD 2][18m]| For Pi Pico |
| 1.54C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 3787][8m] | |
| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [T-Display][9m] | ESP32 with attached display |
| 2.8C | 320 | 240 | TFT | [ST7789][5d] | [Waveshare pico 2.8][10m] | Display for Pi Pico |
@ -28,6 +29,7 @@ Width and height are pixels.
| 3.2C | 320 | 240 | TFT | [ILI9341][6d] | [Adafruit 1743][12m] | Big display. eBay equivalents work here. |
| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4262][13m] | Flexible ePaper display |
| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4777][15m] | FeatherWing ePaper display |
| 4.2M | 400 | 300 | eInk | [WS][10d] | [Waveshare pico 4.2][19m] | Pico, Pico W plug in. Other hosts via cable |
| 2.7M | 274 | 176 | eInk | [HAT][8d] | [Waveshare HAT][14m] | HAT designed for Raspberry Pi, repurposed. |
| 2.7M | 400 | 240 | Sharp | [Sharp][9d] | [Adafruit 4694][16m] | Micropower monochrome display. |
| 1.3M | 168 | 144 | Sharp | [Sharp][9d] | [Adafruit 3502][17m] | Ditto |
@ -83,6 +85,7 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details.
[7d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#51-adafruit-monochrome-eink-displays
[8d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#52-waveshare-eink-display-hat
[9d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#4-drivers-for-sharp-displays
[10d]: https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#53-waveshare-400x300-pi-pico-display
[1m]: https://www.adafruit.com/product/684
[2m]: https://www.adafruit.com/product/1673
@ -101,4 +104,5 @@ simple. See [this doc](./DRIVERS.md#7-writing-device-drivers) for details.
[15m]: https://www.adafruit.com/product/4777
[16m]: https://www.adafruit.com/product/4694
[17m]: https://www.adafruit.com/product/3502
[18m]: https://www.waveshare.com/wiki/Pico-LCD-2
[19m]: https://thepihut.com/collections/epaper-displays-for-raspberry-pi/products/4-2-e-paper-display-module-for-raspberry-pi-pico-black-white-400x300

Wyświetl plik

@ -37,7 +37,8 @@ access via the `Writer` and `CWriter` classes is documented
3.3 [Drivers for ST7789](./DRIVERS.md#33-drivers-for-st7789) Small high density TFTs
     3.3.1 [TTGO T Display](./DRIVERS.md#331-ttgo-t-display) Low cost ESP32 with integrated display
     3.3.2 [Waveshare Pico Res Touch](./DRIVERS.md#332-waveshare-pico-res-touch)
     3.3.3 [Troubleshooting](./DRIVERS.md#333-troubleshooting)
     3.3.3 [Waveshare Pico LCD 2](./DRIVERS.md#333-waveshare-pico-lcd-2)
     3.3.4 [Troubleshooting](./DRIVERS.md#334-troubleshooting)
4. [Drivers for sharp displays](./DRIVERS.md#4-drivers-for-sharp-displays) Large low power monochrome displays
4.1 [Display characteristics](./DRIVERS.md#41-display-characteristics)
     4.1.1 [The VCOM bit](./DRIVERS.md#411-the-vcom-bit)
@ -60,9 +61,9 @@ access via the `Writer` and `CWriter` classes is documented
     5.2.1 [EPD constructor args](./DRIVERS.md#521-epd-constructor-args)
     5.2.2 [EPD public methods](./DRIVERS.md#522-epd-public-methods)
     5.2.3 [EPD public bound variables](./DRIVERS.md#523-epd-public-bound-variables)
6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support)
7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers)
8. [Links](./DRIVERS.md#8-links)
6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support)
7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers)
8. [Links](./DRIVERS.md#8-links)
The [Micropower use](./DRIVERS.md#515-micropower-use) section is applicable to
EPD's in general but makes specific reference to the 2.9" micropower demo.
@ -565,7 +566,34 @@ driver which may be found in the MicroPython source tree in
`drivers/sdcard/sdcard.py`. I am not an expert on SD cards. Mine worked fine at
31.25MHz but this may or may not be universally true.
### 3.3.3 Troubleshooting
### 3.3.3 Waveshare Pico LCD 2
Support for this display resulted from a collaboration with Mike Wilson
(@MikeTheGent).
This is a "plug and play" 2" color TFT for `nano-gui` and the Pi Pico. Users of
`micro-gui` will need to find a way to connect pushbuttons, using stacking
headers on the Pico or soldering wires to its pads. The `color_setup.py` file
is as follows.
```python
from machine import Pin, SPI
import gc
from drivers.st7789.st7789_4bit import *
SSD = ST7789
gc.collect() # Precaution before instantiating framebuf
# Conservative low baudrate. Can go to 62.5MHz.
spi = SPI(1, 30_000_000, sck=Pin(10), mosi=Pin(11), miso=None)
pcs = Pin(9, Pin.OUT, value=1)
prst = Pin(12, Pin.OUT, value=1)
pbl = Pin(13, Pin.OUT, value=1)
pdc = Pin(8, Pin.OUT, value=0)
ssd = SSD(spi, height=240, width=320, dc=pdc, cs=pcs, rst=prst, disp_mode=LANDSCAPE, display=PI_PICO_LCD_2)
```
### 3.3.4 Troubleshooting
If your display shows garbage, check the following (I have seen both):
* SPI baudrate too high for your physical layout.
@ -1023,6 +1051,30 @@ Pins 26-40 unused and omitted.
seconds to enable viewing. This enables generic nanogui demos to be run on an
EPD.
## 5.3 Waveshare 400x300 Pi Pico display
This 4.2" display supports a Pi Pico or Pico W plugged into the rear of the
unit. Alternatively it can be connected to any other host using the supplied
cable. With a Pico variant the `color_setup` file is very simple:
```python
import machine
import gc
from drivers.epaper.pico_epaper_42 import EPD as SSD
gc.collect() # Precaution before instantiating framebuf.
ssd = SSD() # Create a display instance. For normal applications.
# ssd = SSD(asyn=True) # Alternative for asynchronous applications.
```
For other hosts the pins need to be specified in `color_setup.py` via the
following constructor args:
* `spi=None` An SPI bus instance defined with default args.
* `cs=None` A `Pin` instance defined as `Pin.OUT`.
* `dc=None` A `Pin` instance defined as `Pin.OUT`.
* `rst=None` A `Pin` instance defined as `Pin.OUT`.
* `busy=None` A `Pin` instance defined as `Pin.IN, Pin.PULL_UP`.
* `asyn=False` Set `True` for asynchronous applications.
###### [Contents](./DRIVERS.md#contents)
# 6. EPD Asynchronous support
@ -1054,6 +1106,7 @@ The following illustrates the kind of approach which may be used:
```python
while True:
# Before refresh, ensure that a previous refresh is complete
# Not strictly necessary if .updated() used after refresh.
await ssd.wait()
refresh(ssd) # Immediate return. Creates a task to copy content to EPD.
# Wait until the framebuf content has been passed to EPD.
@ -1062,7 +1115,6 @@ The following illustrates the kind of approach which may be used:
# framebuffer in background
evt.set()
evt.clear()
# The 2.9 inch display should not be updated too frequently
await asyncio.sleep(180)
```
@ -1082,21 +1134,19 @@ driver chips support graphics primitives in hardware; drivers using these
capabilities will be faster than those provided here and may often be found
using a forum search.
## 7.1 The basics
For a driver to support `nanogui` it must be subclassed from
`framebuf.FrameBuffer` and provide `height` and `width` bound variables being
the display size in pixels. This, and a `show` method, are all that is required
for monochrome drivers. Generality can be extended by providing this static
method:
```python
@staticmethod
def rgb(r, g, b):
return int(((r | g | b) & 0x80) > 0)
```
This ensures compatibility with code written for color displays by converting
RGB values to a single bit.
for monochrome drivers.
For color display drivers some boilerplate code is required for rendering
monochrome objects such as glyphs:
## 7.2 Color and color compatible drivers
Some additional boilerplate code is required for color drivers to enable them
to render monochrome object such as glyphs. To enable a monochrome driver to
run code written for color displays it too should incorporate this code.
Otherise color code will fail with an "Incompatible device driver" exception.
```python
from drivers.boolpalette import BoolPalette
# In the constructor:
@ -1104,16 +1154,45 @@ from drivers.boolpalette import BoolPalette
self.palette = BoolPalette(mode)
super().__init__(buf, self.width, self.height, mode)
```
The GUI achieves hardware independence by using 24 bit color. The driver must
convert this, typically to a format used by the hardware. This is done by a
static `rgb` method. In the case of a monochrome display, any color with high
brightness is mapped to white with:
```python
@staticmethod
def rgb(r, g, b):
return int(((r | g | b) & 0x80) > 0)
```
A typical color display with 8-bit `rrrgggbb` hardware will use:
```python
@staticmethod
def rgb(r, g, b):
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
```
See the discussion below for other color mappings including 16-bit and 4-bit
variants.
## 7.3 Show
Refresh must be handled by a `show` method taking no arguments; when called,
the contents of the buffer underlying the `FrameBuffer` must be copied to the
hardware.
For color drivers, to conserve RAM it is suggested that 8-bit color is used
for the `FrameBuffer`. If the hardware does not support this, conversion to the
supported color space needs to be done "on the fly" as per the SSD1351 driver.
This uses `framebuf.GS8` to stand in for 8 bit color in `rrrgggbb` format. To
maximise update speed consider using native, viper or assembler for the
conversion, typically to RGB565 format.
## 7.4 Minimising RAM usage
In the simplest case the `FrameBuffer` mode is chosen to match a mode used by
the hardware. The `rgb` static method converts colors to that format and
`.show` writes it out.
In some cases this can result in a need for a large `FrameBuffer`, either
because the hardware can only accept 16 bit color values or because the
display has a large number of pixels. In these cases the `FrameBuffer` uses
a mode for 8 bit or 4 bit color with mapping taking place on the fly in the
`.show` method. To maximise update speed consider using native, viper or
assembler for this mapping.
An example of hardware that does not support 8 bit color is the SSD1351 driver.
This uses `framebuf.GS8` to stand in for 8 bit color in `rrrgggbb` format.
An alternative is to design for 4-bit color which halves the size of the
framebuffer. This means using `GS4_HMSB` mode. The class must include the class
@ -1127,41 +1206,21 @@ acceptable to the hardware. The "on the fly" converter unpacks the values in
the frame buffer and uses them as indices into the `lut` bytearray. See the
various supplied 4-bit drivers.
Color drivers should have a static method converting rgb(255, 255, 255) to a
form acceptable to the driver. For 8-bit rrrgggbb this can be:
```python
@staticmethod
def rgb(r, g, b):
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
```
This should be amended if the hardware uses a different 8-bit format. If the
hardware expects a 16 bit value, the "on the fly" converter will map the 8-bit
value to 16 bits. In the case of 4-bit drivers the LUT is 16 bits in size, and
`.rgb` is called only when populating the LUT. In this case `.rgb` returns a 16
bit value in a format compatible with the hardware and the byte order of the
"on the fly" conversion code.
The color driver static method should be amended if the hardware uses a
different 8-bit format. If the hardware expects a 16 bit value, the "on the
fly" converter will map the 8-bit value to 16 bits. In a 4-bit driver the LUT
is 16 bits in size, and `.rgb` is called only when populating the LUT. In this
case `.rgb` returns a 16 bit value in a format compatible with the hardware and
the byte order of the "on the fly" conversion code.
The convention I use is that the LS byte from `.rgb()` is transmitted first. So
long as `.rgb()` and the "on the fly" converter match, this is arbitrary.
The `Writer` (monochrome) or `CWriter` (color) classes and the `nanogui` module
should then work automatically.
## 7.5 Debugging
For color displays the following provides a substantial performance boost when
rendering text.
```python
from drivers.boolpalette import BoolPalette
```
and, in `__init__.py`:
```python
mode = framebuf.GS4_HMSB # The format to be used by the driver
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width // 2) # Computation must match the format
super().__init__(buf, self.width, self.height, mode)
```
Assuming firmware dated after 26th Aug 2021, a fast C blit will be used to
render glyphs instead of Python code.
If the above guidelines are followed the `Writer` (monochrome) or `CWriter`
(color) classes, `nanogui` and `micro-gui`modules should then work
automatically.
The following script is useful for testing color display drivers after
configuring `color_setup.py`. It draws squares at the extreme corners of the
@ -1185,6 +1244,42 @@ If this produces correct output the GUI's can be expected to work.
Authors of device drivers are encouraged to raise an issue or PR so that the
library can be extended.
## 7.6 Reducing blocking time in show
This is available for `micro-gui` only. The blocking time of the `.show` method
is reduced by splitting it into segments and yielding to the scheduler after
each segment. It is handled transparently by the GUI. It consists of providing
an asynchronous `do_refresh` method taking an integer `split` arg. See
[the ili9341 driver](https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/ili93xx/ili9341.py)
for an example. The GUI will pass a `split` value which is a divisor of the
number of lines in the display. The `do_refresh` method calculates the number
of lines in each segment. For each segment it outputs those lines and yields
to the scheduler.
## 7.7 ePaper drivers
These are not supported by `micro-gui` owing to their very slow refresh time.
They are supported by `Witer`, `CWriter` and `nano-gui`.
Owing to the long refresh periods some synchronisation is necessary. This
comprises `ready` and `wait_until_ready` methods. The `ready` method
immediately returns a `bool` indicating if the hardware can accept data. The
`wait_until_ready` method blocks until the device is ready to accept data. This
is all that is required for synchronous applications. The `.show.` method calls
`wait_until_ready` at the end, removing the need for explicit synchronisation
in the application. The cost is that display refresh blocks for a long period.
For applications using asynchronous code this blocking is usually unacceptable.
It can be restricted to a single task, with others, able to continue running by
adding two asynchronous methods, `.wait` and `.updated`. The `.wait` method is
an asynchronous version of `.wait_until_ready`. The `.updated` method is issued
after a `refresh` has been issued and pauses until the physical refresh is
complete. After a `refresh` applications should avoid changing the frame buffer
contents until `.updated` has returned. They should wait on `.wait` before
issuing `.refresh`.
###### [Contents](./DRIVERS.md#contents)
# 8. Links
#### [Device driver document.](./DRIVERS.md)

Wyświetl plik

@ -0,0 +1,234 @@
# pico_epaper_42.py A 1-bit monochrome display driver for the Waveshare Pico
# ePaper 4.2" display.
# Adapted from the Waveshare driver by Peter Hinch Sept 2022.
# *****************************************************************************
# * | 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.
#
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"
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) if spi is None else spi
self.spi.init(baudrate=4_000_000)
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()
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.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)
# 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 wait_until_ready(self):
while(not self.ready()):
self.send_command(b"\x71")
time.sleep_ms(100)
async def wait(self):
await asyncio.sleep_ms(0) # Ensure tasks run that might make it unready
while not self.ready():
self.send_command(b"\x71")
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_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 take ~300ms
self._line(j)
await asyncio.sleep_ms(0)
self.send_command(b"\x12") # Async .display_on()
while not self.busy_pin():
await asyncio.sleep_ms(10) # About 1.7s
self._updated.set()
self._updated.clear()
self._as_busy = False
def show(self):
if self._asyn:
if self._as_busy:
raise RuntimeError('Cannot refresh: display is busy.')
self._as_busy = True # Immediate busy flag. Pin goes low much later.
asyncio.create_task(self._as_show())
return
self.send_command(b"\x13")
for j in range(_EPD_HEIGHT):
self._line(j)
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")

Wyświetl plik

@ -31,7 +31,7 @@ PORTRAIT = 4
# Display types
GENERIC = (0, 0, 0)
TDISPLAY = (52, 40, 1)
PI_PICO_LCD_2 = (0, 0, 1) # Waveshare Pico LCD 2 determined by Mike Wilson.
@micropython.viper
def _lcopy(dest:ptr16, source:ptr8, lut:ptr16, length:int):
@ -61,8 +61,8 @@ class ST7789(framebuf.FrameBuffer):
disp_mode=LANDSCAPE, init_spi=False, display=GENERIC):
if not 0 <= disp_mode <= 7:
raise ValueError('Invalid display mode:', disp_mode)
if not display in (GENERIC, TDISPLAY):
raise ValueError('Invalid display type.')
if not display in (GENERIC, TDISPLAY, PI_PICO_LCD_2):
print("WARNING: unsupported display parameter value.")
self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns)
self._rst = rst # Pins
self._dc = dc

Wyświetl plik

@ -1,8 +1,10 @@
# waveshare_test.py Demo program for nano_gui on an Waveshare ePaper screen
# epd_async.py Demo of nano_gui asynchronous code.
# Needs a large screen e.g.
# https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT
# or 4.2" Waveshare Pico ePaper display.
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# Copyright (c) 2020-2022 Peter Hinch
# color_setup must set landcsape False, asyn True and must not set demo_mode
import uasyncio as asyncio

Wyświetl plik

@ -0,0 +1,18 @@
# color_setup.py Customise for your hardware config
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch
# Supports Waveshare 4.2" 400x300 ePaper display with Raspberry Pico
# https://thepihut.com/collections/epaper-displays-for-raspberry-pi/products/4-2-e-paper-display-module-for-raspberry-pi-pico-black-white-400x300
# Waveshare code
# https://github.com/waveshare/Pico_ePaper_Code/blob/a6b26ac714d56f958010ddfca3b7fef8410c59c2/python/Pico-ePaper-4.2.py
import machine
import gc
from drivers.epaper.pico_epaper_42 import EPD as SSD
gc.collect() # Precaution before instantiating framebuf
# Set asyn True to run asynchronous code such as epd_async.py
# Set False for normal synchronous code e.g. other demos.
ssd = SSD(asyn=True) # Create a display instance

Wyświetl plik

@ -0,0 +1,43 @@
# color_setup.py Customise for your hardware config
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# As written, supports:
# Adafruit 1.5" 128*128 OLED display: https://www.adafruit.com/product/1431
# Adafruit 1.27" 128*96 display https://www.adafruit.com/product/1673
# 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.
# WIRING (Adafruit pin nos and names).
# Pyb SSD
# 3v3 Vin (10)
# Gnd Gnd (11)
# Y1 DC (3 DC)
# Y2 CS (5 OC OLEDCS)
# Y3 Rst (4 R RESET)
# Y6 CLK (2 CL SCK)
# Y8 DATA (1 SI MOSI)
import machine
import gc
# *** Choose your color display driver here ***
# Driver supporting non-STM platforms
# from drivers.ssd1351.ssd1351_generic import SSD1351 as SSD
# STM specific driver
from drivers.ssd1351.ssd1351 import SSD1351 as SSD
height = 96 # 1.27 inch 96*128 (rows*cols) display
# height = 128 # 1.5 inch 128*128 display
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)
spi = machine.SPI(2)
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance