ST7789 add uasyncio support.

pull/8/head
Peter Hinch 2021-03-25 10:18:45 +00:00
rodzic 7bd781033b
commit e1a61b67e8
4 zmienionych plików z 59 dodań i 32 usunięć

Wyświetl plik

@ -350,13 +350,14 @@ are required. However this period may be unacceptable for some `uasyncio`
applications. The driver provides an asynchronous `do_refresh(split=4)` method.
If this is run the display will regularly be refreshed, but will periodically
yield to the scheduler enabling other tasks to run. The arg determines the
number of times this will occur, so by default it will block for about 50ms.
A `ValueError` will result if `split` is not an integer divisor of the display
height.
number of times in a frame where this will occur, so by default it will block
for about 50ms. A `ValueError` will result if `split` is not an integer divisor
of the `height` passed to the constructor.
An application using this should call `refresh(ssd, True)` once at the start,
then launch the `do_refresh` method. After that, no calls to `refresh` should
be made. See `gui/demos/scale_ili.py`.
be made. See `gui/demos/scale_async.py`. The initial synchronous `refresh` call
will block for the full refresh period.
Another option to reduce blocking is overclocking the SPI bus.
@ -376,9 +377,6 @@ The response may be of interest.
## 3.3 Drivers for ST7789
**UNDER DEVELOPMENT**
Initial testing on Adafruit display looks good.
These displays tend to be physically small with a high pixel density. The chip
supports up to 240x320 displays. The Adafruit units tested are 240x240. To keep
the buffer size down, the driver uses 4-bit color with dynamic conversion to 16
@ -394,7 +392,7 @@ uses the same CircuitPython driver so can be expected to work.
The `color_setup.py` file should initialise the SPI bus with a baudrate of
30_000_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster. 30MHz is a conservative value: see
below.
below. An example file for the Pi Pico is in `color_setup/ssd7789.py`.
#### ST7789 Constructor args:
* `spi` An initialised SPI bus instance. The chip supports clock rates of upto
@ -453,8 +451,22 @@ On Adafruit displays, combinations that don't produce mirror images are:
#### Use with uasyncio
Running the SPI bus at 60MHz a refresh blocks for 83ms. Considering whether to
offer a solution as per ili9341.
Running the SPI bus at 60MHz a refresh blocks for 83ms. If this is acceptable,
no special precautions are required. This period may be unacceptable for some
`uasyncio` applications. Some may use lower SPI baudrates either for electrical
reasons or where the host cannot support high speeds.
The driver provides an asynchronous `do_refresh(split=4)` method. If this is
run the display will regularly be refreshed, but will periodically yield to the
scheduler enabling other tasks to run. The arg determines the number of times
in a frame where this will occur, so by default it will block for about 15ms. A
`ValueError` will result if `split` is not an integer divisor of the `height`
passed to the constructor.
An application using this should call `refresh(ssd, True)` once at the start,
then launch the `do_refresh` method. After that, no calls to `refresh` should
be made. See `gui/demos/scale_async.py`. The initial synchronous `refresh` call
will block for the full refresh period.
###### [Contents](./DRIVERS.md#contents)

Wyświetl plik

@ -26,15 +26,14 @@
from machine import Pin, SPI
import gc
# *** Choose your color display driver here ***
from drivers.st7789.st7789_4bit import ST7789 as SSD, PORTRAIT, USD
from drivers.st7789.st7789_4bit import ST7789 as SSD, PORTRAIT, USD, REFLECT
pdc = Pin(13, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(14, Pin.OUT, value=1)
prst = Pin(15, Pin.OUT, value=1)
gc.collect() # Precaution before instantiating framebuf
spi = SPI(1, 60_000_000, sck=Pin(10), mosi=Pin(11), miso=Pin(8))
# Conservative low baudrate. Can go to 62.5MHz.
spi = SPI(1, 30_000_000, sck=Pin(10), mosi=Pin(11), miso=Pin(8))
ssd = SSD(spi, dc=pdc, cs=pcs, rst=prst) #, disp_mode=PORTRAIT | USD)

Wyświetl plik

@ -13,27 +13,17 @@
# 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
import uasyncio as asyncio
PORTRAIT = 0x20
REFLECT = 0x40
USD = 0x80
#_INIT_SEQUENCE = (
#b"\x01\x80\x96" # _SWRESET and Delay 150ms
#b"\x11\x80\xFF" # _SLPOUT and Delay 500ms
#b"\x3A\x81\x55\x0A" # _COLMOD and Delay 10ms
#b"\x36\x01\x08" # _MADCTL
#b"\x21\x80\x0A" # _INVON Hack and Delay 10ms
#b"\x13\x80\x0A" # _NORON and Delay 10ms
#b"\x36\x01\xC0" # _MADCTL
#b"\x29\x80\xFF" # _DISPON and Delay 500ms
#)
@micropython.viper
def _lcopy(dest:ptr8, source:ptr8, lut:ptr8, length:int):
n = 0
@ -62,7 +52,6 @@ class ST7789(framebuf.FrameBuffer):
return ((b & 0xf8) << 5 | (g & 0x1c) << 11 | (g & 0xe0) >> 5 | (r & 0xf8)) ^ 0xffff
# rst and cs are active low, SPI is mode 0
# TEST height=140, width=200, disp_mode=PORTRAIT|REFLECT
def __init__(self, spi, cs, dc, rst, height=240, width=240, disp_mode=0, init_spi=False):
self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns)
self._rst = rst # Pins
@ -167,21 +156,47 @@ class ST7789(framebuf.FrameBuffer):
# Row address set
self._wcd(b'\x2b', int.to_bytes(ys, 2, 'big') + int.to_bytes(ye, 2, 'big'))
#@micropython.native # Made virtually no difference to timing.
def show(self): # Blocks for 83ms @60MHz SPI
# ts = ticks_us()
#ts = ticks_us()
clut = ST7789.lut
wd = self.width // 2
end = self.height * wd
lb = self._linebuf
buf = self._mvb
self._dc(0)
self._cs(0)
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
self._dc(0)
self._cs(0)
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=4):
lines, mod = divmod(self.height, split) # Lines per segment
if mod:
raise ValueError('Invalid do_refresh arg.')
clut = ST7789.lut
wd = self.width // 2
lb = self._linebuf
buf = self._mvb
while True:
line = 0
for n in range(split):
if self._spi_init: # A callback was passed
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._dc(1)
for start in range(wd * line, wd * (line + lines), wd):
_lcopy(lb, buf[start :], clut, wd) # Copy and map colors
self._spi.write(lb)
line += lines
self._cs(1)
await asyncio.sleep(0)

Wyświetl plik

@ -1,4 +1,5 @@
# scale_ili.py Test/demo of scale widget for nano-gui
# scale_async.py Test/demo of scale widget for nano-gui using asynchronous code
# Requires a supporting display (ili9341 or ST7789)
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch