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.44C | 128 | 128 | TFT | [ST7735R][4d] | [Adafruit 2088][5m] | |
| 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | | | 1.5C | 160 | 128 | TFT | [ST7735R][4d] | [Adafruit 358][6m] | |
| 1.3C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 4313][7m] | | | 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.54C | 240 | 240 | TFT | [ST7789][5d] | [Adafruit 3787][8m] | |
| 1.14C | 240 | 135 | TFT | [ST7789][5d] | [T-Display][9m] | ESP32 with attached display | | 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 | | 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. | | 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 4262][13m] | Flexible ePaper display |
| 2.9M | 296 | 128 | eInk | [UC8151D][7d] | [Adafruit 4777][15m] | FeatherWing 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 | 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. | | 2.7M | 400 | 240 | Sharp | [Sharp][9d] | [Adafruit 4694][16m] | Micropower monochrome display. |
| 1.3M | 168 | 144 | Sharp | [Sharp][9d] | [Adafruit 3502][17m] | Ditto | | 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 [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 [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 [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 [1m]: https://www.adafruit.com/product/684
[2m]: https://www.adafruit.com/product/1673 [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 [15m]: https://www.adafruit.com/product/4777
[16m]: https://www.adafruit.com/product/4694 [16m]: https://www.adafruit.com/product/4694
[17m]: https://www.adafruit.com/product/3502 [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 [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.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.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. [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 [Display characteristics](./DRIVERS.md#41-display-characteristics)
     4.1.1 [The VCOM bit](./DRIVERS.md#411-the-vcom-bit)      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.1 [EPD constructor args](./DRIVERS.md#521-epd-constructor-args)
     5.2.2 [EPD public methods](./DRIVERS.md#522-epd-public-methods)      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)      5.2.3 [EPD public bound variables](./DRIVERS.md#523-epd-public-bound-variables)
6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support) 6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support)
7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers) 7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers)
8. [Links](./DRIVERS.md#8-links) 8. [Links](./DRIVERS.md#8-links)
The [Micropower use](./DRIVERS.md#515-micropower-use) section is applicable to 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. 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 `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. 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): If your display shows garbage, check the following (I have seen both):
* SPI baudrate too high for your physical layout. * 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 seconds to enable viewing. This enables generic nanogui demos to be run on an
EPD. 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) ###### [Contents](./DRIVERS.md#contents)
# 6. EPD Asynchronous support # 6. EPD Asynchronous support
@ -1054,6 +1106,7 @@ The following illustrates the kind of approach which may be used:
```python ```python
while True: while True:
# Before refresh, ensure that a previous refresh is complete # Before refresh, ensure that a previous refresh is complete
# Not strictly necessary if .updated() used after refresh.
await ssd.wait() await ssd.wait()
refresh(ssd) # Immediate return. Creates a task to copy content to EPD. refresh(ssd) # Immediate return. Creates a task to copy content to EPD.
# Wait until the framebuf content has been passed 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 # framebuffer in background
evt.set() evt.set()
evt.clear() evt.clear()
# The 2.9 inch display should not be updated too frequently
await asyncio.sleep(180) 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 capabilities will be faster than those provided here and may often be found
using a forum search. using a forum search.
## 7.1 The basics
For a driver to support `nanogui` it must be subclassed from For a driver to support `nanogui` it must be subclassed from
`framebuf.FrameBuffer` and provide `height` and `width` bound variables being `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 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 for monochrome drivers.
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 color display drivers some boilerplate code is required for rendering ## 7.2 Color and color compatible drivers
monochrome objects such as glyphs:
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 ```python
from drivers.boolpalette import BoolPalette from drivers.boolpalette import BoolPalette
# In the constructor: # In the constructor:
@ -1104,16 +1154,45 @@ from drivers.boolpalette import BoolPalette
self.palette = BoolPalette(mode) self.palette = BoolPalette(mode)
super().__init__(buf, self.width, self.height, 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, 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 the contents of the buffer underlying the `FrameBuffer` must be copied to the
hardware. hardware.
For color drivers, to conserve RAM it is suggested that 8-bit color is used ## 7.4 Minimising RAM usage
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. In the simplest case the `FrameBuffer` mode is chosen to match a mode used by
This uses `framebuf.GS8` to stand in for 8 bit color in `rrrgggbb` format. To the hardware. The `rgb` static method converts colors to that format and
maximise update speed consider using native, viper or assembler for the `.show` writes it out.
conversion, typically to RGB565 format.
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 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 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 the frame buffer and uses them as indices into the `lut` bytearray. See the
various supplied 4-bit drivers. various supplied 4-bit drivers.
Color drivers should have a static method converting rgb(255, 255, 255) to a The color driver static method should be amended if the hardware uses a
form acceptable to the driver. For 8-bit rrrgggbb this can be: different 8-bit format. If the hardware expects a 16 bit value, the "on the
```python fly" converter will map the 8-bit value to 16 bits. In a 4-bit driver the LUT
@staticmethod is 16 bits in size, and `.rgb` is called only when populating the LUT. In this
def rgb(r, g, b): case `.rgb` returns a 16 bit value in a format compatible with the hardware and
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6) the byte order of the "on the fly" conversion code.
```
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 convention I use is that the LS byte from `.rgb()` is transmitted first. So 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. long as `.rgb()` and the "on the fly" converter match, this is arbitrary.
The `Writer` (monochrome) or `CWriter` (color) classes and the `nanogui` module ## 7.5 Debugging
should then work automatically.
For color displays the following provides a substantial performance boost when If the above guidelines are followed the `Writer` (monochrome) or `CWriter`
rendering text. (color) classes, `nanogui` and `micro-gui`modules should then work
```python automatically.
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.
The following script is useful for testing color display drivers after The following script is useful for testing color display drivers after
configuring `color_setup.py`. It draws squares at the extreme corners of the 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 Authors of device drivers are encouraged to raise an issue or PR so that the
library can be extended. 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 # 8. Links
#### [Device driver document.](./DRIVERS.md) #### [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 # Display types
GENERIC = (0, 0, 0) GENERIC = (0, 0, 0)
TDISPLAY = (52, 40, 1) TDISPLAY = (52, 40, 1)
PI_PICO_LCD_2 = (0, 0, 1) # Waveshare Pico LCD 2 determined by Mike Wilson.
@micropython.viper @micropython.viper
def _lcopy(dest:ptr16, source:ptr8, lut:ptr16, length:int): 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): disp_mode=LANDSCAPE, init_spi=False, display=GENERIC):
if not 0 <= disp_mode <= 7: if not 0 <= disp_mode <= 7:
raise ValueError('Invalid display mode:', disp_mode) raise ValueError('Invalid display mode:', disp_mode)
if not display in (GENERIC, TDISPLAY): if not display in (GENERIC, TDISPLAY, PI_PICO_LCD_2):
raise ValueError('Invalid display type.') print("WARNING: unsupported display parameter value.")
self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns) self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns)
self._rst = rst # Pins self._rst = rst # Pins
self._dc = dc 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 # 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. # 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 # color_setup must set landcsape False, asyn True and must not set demo_mode
import uasyncio as asyncio 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