kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
ST7789: Add support for Waveshare 1.3inch LCD
rodzic
521c7b553d
commit
172c976df0
25
DRIVERS.md
25
DRIVERS.md
|
@ -315,11 +315,11 @@ cross-platform but assume `micropython.viper` capability. They use 8-bit or
|
|||
4-bit color to minimise the RAM used by the frame buffer.
|
||||
|
||||
Drivers for [Adafruit 1.8" display](https://www.adafruit.com/product/358).
|
||||
* `st7735r.py` 8-bit color.
|
||||
* `st7735r.py` 8-bit color.
|
||||
* `st7735r_4bit.py` 4-bit color for further RAM reduction.
|
||||
|
||||
For [Adafruit 1.44" display](https://www.adafruit.com/product/2088).
|
||||
* `st7735r144.py` 8-bit color.
|
||||
* `st7735r144.py` 8-bit color.
|
||||
* `st7735r144_4bit` 4 bit color.
|
||||
|
||||
Users of other ST7735R based displays should beware: there are many variants
|
||||
|
@ -360,8 +360,8 @@ soft SPI may be used but hard may be faster.
|
|||
* `init_spi=False` This optional arg enables flexible options in configuring
|
||||
the SPI bus. See below.
|
||||
|
||||
#### The init_spi constructor arg
|
||||
|
||||
#### The init_spi constructor arg
|
||||
|
||||
The `False` default assumes exclusive access to the bus. It is initialised by
|
||||
`color_setup.py` and those settings are left in place. If a callback function
|
||||
is passed, it will be called prior to each SPI bus write. This is for shared
|
||||
|
@ -419,7 +419,7 @@ On an ESP32 without SPIRAM, `nano-gui` runs but
|
|||
frozen bytecode. The RP2 Pico runs both GUI's.
|
||||
|
||||
See [Color handling](./DRIVERS.md#11-color-handling) for details of the
|
||||
implications of 4-bit color.
|
||||
implications of 4-bit color.
|
||||
|
||||
The driver uses the `micropython.viper` decorator. If your platform does not
|
||||
support this, the Viper code will need to be rewritten with a substantial hit
|
||||
|
@ -524,6 +524,7 @@ Display types (values for `display`):
|
|||
`TDISPLAY` For the TTGO T-Display and Waveshare Pico LCD.
|
||||
`PI_PICO_LCD_2` Waveshare Pico LCD 2 determined by Mike Wilson.
|
||||
`DFR0995` DFR0995 Contributed by @EdgarKluge
|
||||
`WAVESHARE_13` Waveshare 1.3" 240x240 LCD contributed by Aaron Mittelmeier
|
||||
|
||||
### init_spi
|
||||
|
||||
|
@ -948,7 +949,7 @@ contiguous RAM is available.
|
|||
### 4.4.1 Micropower applications
|
||||
|
||||
These comments largely assume a Pyboard host. The application should import
|
||||
`upower` from
|
||||
`upower` from
|
||||
[micropython-micropower](https://github.com/peterhinch/micropython-micropower).
|
||||
This turns the USB interface off if not in use to conserve power. It also
|
||||
provides an `lpdelay` function to implement a delay using `pyb.stop()` to
|
||||
|
@ -990,9 +991,9 @@ the CPU enabling user code to continue to run.
|
|||
The standard refresh method blocks (monopolises the CPU) until refresh is
|
||||
complete, adding an additional 2s delay. This enables the demo scripts to run
|
||||
unchanged, with the 2s delay allowing the results to be seen before the next
|
||||
refresh begins. This is fine for simple applications. The drivers also support
|
||||
refresh begins. This is fine for simple applications. The drivers also support
|
||||
concurrency with `uasyncio`. Such applications can perform other tasks while a
|
||||
refresh is in progress. See
|
||||
refresh is in progress. See
|
||||
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||
|
||||
Finally the [Waveshare 400x300 Pi Pico display](./DRIVERS.md#53-waveshare-400x300-pi-pico-display)
|
||||
|
@ -1010,7 +1011,7 @@ An alternative is the
|
|||
[wiring details](./DRIVERS.md#514-featherwing-wiring) listed below.
|
||||
|
||||
In my testing there are differences between these alternatives. The FeatherWing
|
||||
shows a black border around the display. The reason for this is
|
||||
shows a black border around the display. The reason for this is
|
||||
[unclear](https://github.com/adafruit/Adafruit_CircuitPython_IL0373/issues/11#issuecomment-763704622).
|
||||
In development I encountered instances where the image on the flexible display
|
||||
gradually degraded after the system was powered down. The white background
|
||||
|
@ -1398,8 +1399,8 @@ The driver supports the WeAct Studio SSD1680 2.9 inch 296*128 pixel
|
|||
[display](https://github.com/WeActStudio/WeActStudio.EpaperModule) that uses the
|
||||
[SSD1680 driver](https://github.com/WeActStudio/WeActStudio.EpaperModule/blob/master/Doc/SSD1680.pdf).
|
||||
|
||||
This display lacks many features when compared to the ones from Waveshare,
|
||||
two important examples are fast refresh and partial refresh. The big pro however is the price,
|
||||
This display lacks many features when compared to the ones from Waveshare,
|
||||
two important examples are fast refresh and partial refresh. The big pro however is the price,
|
||||
it costs half the money of the Waveshare 2.9in alternative.
|
||||
|
||||
The driver is cross platform and supports landscape or portrait mode. To keep
|
||||
|
@ -1556,7 +1557,7 @@ hardware.
|
|||
## 7.4 Mapped drivers
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# https://www.adafruit.com/product/4313
|
||||
# TTGO T-Display
|
||||
# http://www.lilygo.cn/prod_view.aspx?TypeId=50044&Id=1126
|
||||
|
||||
|
||||
# Based on
|
||||
# Adfruit https://github.com/adafruit/Adafruit_CircuitPython_ST7789/blob/master/adafruit_st7789.py
|
||||
# Also see st7735r_4bit.py for other source acknowledgements
|
||||
|
@ -16,7 +16,7 @@
|
|||
# SPI bus: default mode. Driver performs no read cycles.
|
||||
# Datasheet table 6 p44 scl write cycle 16ns == 62.5MHz
|
||||
|
||||
from time import sleep_ms #, ticks_us, ticks_diff
|
||||
from time import sleep_ms # , ticks_us, ticks_diff
|
||||
import framebuf
|
||||
import gc
|
||||
import micropython
|
||||
|
@ -32,19 +32,22 @@ PORTRAIT = 4
|
|||
GENERIC = (0, 0, 0)
|
||||
TDISPLAY = (52, 40, 1)
|
||||
PI_PICO_LCD_2 = (0, 0, 1) # Waveshare Pico LCD 2 determined by Mike Wilson.
|
||||
DFR0995 = (34, 0, 0) # DFR0995 Contributed by @EdgarKluge
|
||||
DFR0995 = (34, 0, 0) # DFR0995 Contributed by @EdgarKluge
|
||||
WAVESHARE_13 = (0, 0, 16) # Waveshare 1.3" 240x240 LCD contributed by Aaron Mittelmeier
|
||||
|
||||
|
||||
@micropython.viper
|
||||
def _lcopy(dest:ptr16, source:ptr8, lut:ptr16, length:int):
|
||||
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int):
|
||||
# rgb565 - 16bit/pixel
|
||||
n = 0
|
||||
for x in range(length):
|
||||
c = source[x]
|
||||
dest[n] = lut[c >> 4] # current pixel
|
||||
n += 1
|
||||
dest[n] = lut[c & 0x0f] # next pixel
|
||||
dest[n] = lut[c & 0x0F] # next pixel
|
||||
n += 1
|
||||
|
||||
|
||||
class ST7789(framebuf.FrameBuffer):
|
||||
|
||||
lut = bytearray(0xFF for _ in range(32)) # set all colors to BLACK
|
||||
|
@ -55,13 +58,23 @@ class ST7789(framebuf.FrameBuffer):
|
|||
# For some reason color must be inverted on this controller.
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return ((b & 0xf8) << 5 | (g & 0x1c) << 11 | (g & 0xe0) >> 5 | (r & 0xf8)) ^ 0xffff
|
||||
return ((b & 0xF8) << 5 | (g & 0x1C) << 11 | (g & 0xE0) >> 5 | (r & 0xF8)) ^ 0xFFFF
|
||||
|
||||
# rst and cs are active low, SPI is mode 0
|
||||
def __init__(self, spi, cs, dc, rst, height=240, width=240,
|
||||
disp_mode=LANDSCAPE, init_spi=False, display=GENERIC):
|
||||
def __init__(
|
||||
self,
|
||||
spi,
|
||||
cs,
|
||||
dc,
|
||||
rst,
|
||||
height=240,
|
||||
width=240,
|
||||
disp_mode=LANDSCAPE,
|
||||
init_spi=False,
|
||||
display=GENERIC,
|
||||
):
|
||||
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, 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)
|
||||
|
@ -121,16 +134,16 @@ class ST7789(framebuf.FrameBuffer):
|
|||
self._spi_init(self._spi) # Bus may be shared
|
||||
cmd = self._wcmd
|
||||
wcd = self._wcd
|
||||
cmd(b'\x01') # SW reset datasheet specifies 120ms before SLPOUT
|
||||
cmd(b"\x01") # SW reset datasheet specifies 120ms before SLPOUT
|
||||
sleep_ms(150)
|
||||
cmd(b'\x11') # SLPOUT: exit sleep mode
|
||||
cmd(b"\x11") # SLPOUT: exit sleep mode
|
||||
sleep_ms(10) # Adafruit delay 500ms (datsheet 5ms)
|
||||
wcd(b'\x3a', b'\x55') # _COLMOD 16 bit/pixel, 65Kbit color space
|
||||
cmd(b'\x20') # INVOFF Adafruit turn inversion on. This driver fixes .rgb
|
||||
cmd(b'\x13') # NORON Normal display mode
|
||||
wcd(b"\x3a", b"\x55") # _COLMOD 16 bit/pixel, 65Kbit color space
|
||||
cmd(b"\x20") # INVOFF Adafruit turn inversion on. This driver fixes .rgb
|
||||
cmd(b"\x13") # NORON Normal display mode
|
||||
|
||||
# Table maps user request onto hardware values. index values:
|
||||
# 0 Normal
|
||||
# 0 Normal
|
||||
# 1 Reflect
|
||||
# 2 USD
|
||||
# 3 USD reflect
|
||||
|
@ -146,11 +159,11 @@ class ST7789(framebuf.FrameBuffer):
|
|||
# PORTRAIT = 0x20
|
||||
# REFLECT = 0x40
|
||||
# USD = 0x80
|
||||
mode = (0x60, 0xe0, 0xa0, 0x20, 0, 0x40, 0xc0, 0x80)[user_mode]
|
||||
mode = (0x60, 0xE0, 0xA0, 0x20, 0, 0x40, 0xC0, 0x80)[user_mode]
|
||||
# Set display window depending on mode, .height and .width.
|
||||
self.set_window(mode)
|
||||
wcd(b'\x36', int.to_bytes(mode, 1, 'little'))
|
||||
cmd(b'\x29') # DISPON. Adafruit then delay 500ms.
|
||||
wcd(b"\x36", int.to_bytes(mode, 1, "little"))
|
||||
cmd(b"\x29") # DISPON. Adafruit then delay 500ms.
|
||||
|
||||
# Define the mapping between RAM and the display.
|
||||
# Datasheet section 8.12 p124.
|
||||
|
@ -166,7 +179,7 @@ class ST7789(framebuf.FrameBuffer):
|
|||
xs = xoff
|
||||
xe = wwd + xoff - 1
|
||||
ys = yoff # y start
|
||||
ye = wht + yoff - 1 # y end
|
||||
ye = wht + yoff - 1 # y end
|
||||
if mode & reflect:
|
||||
ys = rwd - wht - yoff
|
||||
ye = rwd - yoff - 1
|
||||
|
@ -179,7 +192,7 @@ class ST7789(framebuf.FrameBuffer):
|
|||
xs = xoff
|
||||
xe = wwd + xoff - 1
|
||||
ys = yoff # y start
|
||||
ye = wht + yoff - 1 # y end
|
||||
ye = wht + yoff - 1 # y end
|
||||
if mode & usd:
|
||||
ys = rht - wht - yoff
|
||||
ye = rht - yoff - 1
|
||||
|
@ -188,15 +201,15 @@ class ST7789(framebuf.FrameBuffer):
|
|||
xe = rwd - xoff - 1
|
||||
|
||||
# Col address set.
|
||||
self._wcd(b'\x2a', int.to_bytes((xs << 16) + xe, 4, 'big'))
|
||||
self._wcd(b"\x2a", int.to_bytes((xs << 16) + xe, 4, "big"))
|
||||
# Row address set
|
||||
self._wcd(b'\x2b', int.to_bytes((ys << 16) + ye, 4, 'big'))
|
||||
self._wcd(b"\x2b", int.to_bytes((ys << 16) + ye, 4, "big"))
|
||||
|
||||
#@micropython.native # Made virtually no difference to timing.
|
||||
# @micropython.native # Made virtually no difference to timing.
|
||||
def show(self): # Blocks for 83ms @60MHz SPI
|
||||
# Blocks for 60ms @30MHz SPI on TTGO in PORTRAIT mode
|
||||
# Blocks for 46ms @30MHz SPI on TTGO in LANDSCAPE mode
|
||||
#ts = ticks_us()
|
||||
# ts = ticks_us()
|
||||
clut = ST7789.lut
|
||||
wd = -(-self.width // 2) # Ceiling division for odd number widths
|
||||
end = self.height * wd
|
||||
|
@ -206,20 +219,20 @@ class ST7789(framebuf.FrameBuffer):
|
|||
self._spi_init(self._spi) # Bus may be shared
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(b'\x2c') # RAMWR
|
||||
self._spi.write(b"\x2c") # RAMWR
|
||||
self._dc(1)
|
||||
for start in range(0, end, wd):
|
||||
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
|
||||
self._spi.write(lb)
|
||||
self._cs(1)
|
||||
#print(ticks_diff(ticks_us(), ts))
|
||||
# print(ticks_diff(ticks_us(), ts))
|
||||
|
||||
# Asynchronous refresh with support for reducing blocking time.
|
||||
async def do_refresh(self, split=5):
|
||||
async with self._lock:
|
||||
lines, mod = divmod(self.height, split) # Lines per segment
|
||||
if mod:
|
||||
raise ValueError('Invalid do_refresh arg.')
|
||||
raise ValueError("Invalid do_refresh arg.")
|
||||
clut = ST7789.lut
|
||||
wd = -(-self.width // 2)
|
||||
lb = memoryview(self._linebuf)
|
||||
|
@ -230,10 +243,10 @@ class ST7789(framebuf.FrameBuffer):
|
|||
self._spi_init(self._spi) # Bus may be shared
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(b'\x3c' if n else b'\x2c') # RAMWR/Write memory continue
|
||||
self._spi.write(b"\x3c" if n else b"\x2c") # RAMWR/Write memory continue
|
||||
self._dc(1)
|
||||
for start in range(wd * line, wd * (line + lines), wd):
|
||||
_lcopy(lb, buf[start :], clut, wd) # Copy and map colors
|
||||
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
|
||||
self._spi.write(lb)
|
||||
line += lines
|
||||
self._cs(1)
|
||||
|
|
Ładowanie…
Reference in New Issue