Update ili9x and gc9a01 drivers for image display.

pull/48/head
Peter Hinch 2024-06-07 09:58:10 +01:00
rodzic efe10ca765
commit c282cfa6c9
4 zmienionych plików z 68 dodań i 46 usunięć

Wyświetl plik

@ -81,7 +81,7 @@ class GC9A01(framebuf.FrameBuffer):
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * width // 2) # Frame buffer
self._mvb = memoryview(buf)
self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode)
self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors)
@ -188,7 +188,7 @@ class GC9A01(framebuf.FrameBuffer):
def show(self): # Physical display is in portrait mode
clut = GC9A01.lut
lb = self._linebuf
buf = self._mvb
buf = self.mvb
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM
@ -209,7 +209,7 @@ class GC9A01(framebuf.FrameBuffer):
raise ValueError("Invalid do_refresh arg.")
clut = GC9A01.lut
lb = self._linebuf
buf = self._mvb
buf = self.mvb
self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1)
wd = self.width // 2

Wyświetl plik

@ -23,17 +23,14 @@ from drivers.boolpalette import BoolPalette
# g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5
@micropython.viper
def _lcopy(dest: ptr16, source: ptr8, length: int, gscale: bool):
def _lcopy(dest: ptr16, source: ptr8, length: int):
# rgb565 - 16bit/pixel
n: int = 0
while length:
c = source[n]
if gscale: # Source byte holds 8-bit greyscale
# dest rrrr rggg gggb bbbb
dest[n] = (c & 0xF1) | (c >> 5) | ((c & 0x1C) << 11) | ((c & 0xF1) << 5)
else: # Source byte holds 8-bit rrrgggbb
# dest 000b b000 rrr0 0ggg
dest[n] = (c & 0xE0) | ((c & 0x1C) >> 2) | ((c & 0x03) << 11)
# Source byte holds 8-bit rrrgggbb
# dest 000b b000 rrr0 0ggg
dest[n] = (c & 0xE0) | ((c & 0x1C) >> 2) | ((c & 0x03) << 11)
n += 1
length -= 1
@ -66,12 +63,11 @@ class GC9A01(framebuf.FrameBuffer):
self.height = height # Logical dimensions for GUIs
self.width = width
self._spi_init = init_spi
self._gscale = False # Interpret buffer as rrrgggbb color
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * width) # Frame buffer
self._mvb = memoryview(buf)
self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode)
self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors)
@ -171,14 +167,9 @@ class GC9A01(framebuf.FrameBuffer):
self._spi.write(data)
self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
def show(self): # Physical display is in portrait mode
lb = self._linebuf
buf = self._mvb
buf = self.mvb
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM
@ -186,9 +177,8 @@ class GC9A01(framebuf.FrameBuffer):
self._cs(0)
wd = self.width
ht = self.height
cm = self._gscale # color False, greyscale True
for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], wd, cm) # Copy and map colors
_lcopy(lb, buf[start:], wd) # Copy and map colors
self._spi.write(lb)
self._cs(1)
@ -198,18 +188,17 @@ class GC9A01(framebuf.FrameBuffer):
if mod:
raise ValueError("Invalid do_refresh arg.")
lb = self._linebuf
buf = self._mvb
buf = self.mvb
self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1)
wd = self.width
cm = self._gscale # color False, greyscale True
line = 0
for _ in range(split): # For each segment
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], wd, cm) # Copy and map colors
_lcopy(lb, buf[start:], wd) # Copy and map colors
self._spi.write(lb)
line += lines
self._cs(1) # Allow other tasks to use bus

Wyświetl plik

@ -15,17 +15,26 @@ import asyncio
from drivers.boolpalette import BoolPalette
# Output RGB565 format, 16 bit/pixel:
# g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5
# ~80μs on RP2 @ 250MHz.
@micropython.viper
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int):
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int, gscale: bool):
# rgb565 - 16bit/pixel
n: int = 0
x: int = 0
while length:
c = source[x]
dest[n] = lut[c >> 4] # current pixel
n += 1
dest[n] = lut[c & 0x0F] # next pixel
p = c >> 4 # current pixel
q = c & 0x0F # next pixel
if gscale:
dest[n] = p >> 1 | p << 4 | p << 9 | ((p & 0x01) << 15)
n += 1
dest[n] = q >> 1 | q << 4 | q << 9 | ((q & 0x01) << 15)
else:
dest[n] = lut[p] # current pixel
n += 1
dest[n] = lut[q] # next pixel
n += 1
x += 1
length -= 1
@ -52,11 +61,12 @@ class ILI9341(framebuf.FrameBuffer):
self.height = height
self.width = width
self._spi_init = init_spi
self._gscale = False # Interpret buffer as index into color LUT
mode = framebuf.GS4_HMSB
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width // 2)
self._mvb = memoryview(buf)
self.mvb = memoryview(buf)
super().__init__(buf, self.width, self.height, mode)
self._linebuf = bytearray(self.width * 2)
# Hardware reset
@ -118,6 +128,11 @@ class ILI9341(framebuf.FrameBuffer):
self._spi.write(data)
self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
# Time (ESP32 stock freq) 196ms portrait, 185ms landscape.
# mem free on ESP32 43472 bytes (vs 110192)
@micropython.native
@ -125,8 +140,9 @@ class ILI9341(framebuf.FrameBuffer):
clut = ILI9341.lut
wd = self.width // 2
ht = self.height
cm = self._gscale # color False, greyscale True
lb = self._linebuf
buf = self._mvb
buf = self.mvb
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
# Commands needed to start data write
@ -136,7 +152,7 @@ class ILI9341(framebuf.FrameBuffer):
self._dc(1)
self._cs(0)
for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
_lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb)
self._cs(1)
@ -148,8 +164,9 @@ class ILI9341(framebuf.FrameBuffer):
clut = ILI9341.lut
wd = self.width // 2
ht = self.height
cm = self._gscale # color False, greyscale True
lb = self._linebuf
buf = self._mvb
buf = self.mvb
# Commands needed to start data write
self._wcd(b"\x2a", int.to_bytes(self.width, 4, "big")) # SET_COLUMN
self._wcd(b"\x2b", int.to_bytes(ht, 4, "big")) # SET_PAGE
@ -161,7 +178,7 @@ class ILI9341(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared
self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
_lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb)
line += lines
self._cs(1) # Allow other tasks to use bus

Wyświetl plik

@ -20,15 +20,22 @@ from drivers.boolpalette import BoolPalette
# Portrait mode
@micropython.viper
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int):
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int, gscale: bool):
# rgb565 - 16bit/pixel
n: int = 0
x: int = 0
while length:
c = source[x]
dest[n] = lut[c >> 4] # current pixel
n += 1
dest[n] = lut[c & 0x0F] # next pixel
p = c >> 4 # current pixel
q = c & 0x0F # next pixel
if gscale:
dest[n] = p >> 1 | p << 4 | p << 9 | ((p & 0x01) << 15)
n += 1
dest[n] = q >> 1 | q << 4 | q << 9 | ((q & 0x01) << 15)
else:
dest[n] = lut[p] # current pixel
n += 1
dest[n] = lut[q] # next pixel
n += 1
x += 1
length -= 1
@ -36,8 +43,8 @@ def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int):
# FB is in landscape mode, hence issue a column at a time to portrait mode hardware.
@micropython.viper
def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int):
col = ch & 0x1FF # Unpack (viper 4 parameter limit)
def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int, gscale: bool):
col = ch & 0x1FF # Unpack (viper old 4 parameter limit)
height = (ch >> 9) & 0x1FF
wbytes = ch >> 19 # Width in bytes is width // 2
# rgb565 - 16bit/pixel
@ -49,7 +56,8 @@ def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int):
c = source[idx] & 0x0F
else:
c = source[idx] >> 4
dest[n] = lut[c] # 16 bit transfer of rightmost 4-bit pixel
dest[n] = c >> 1 | c << 4 | c << 9 | ((c & 0x01) << 15) if gscale else lut[c]
# dest[n] = lut[c] # 16 bit transfer of rightmost 4-bit pixel
n += 1 # 16 bit
idx += wbytes
height -= 1
@ -83,11 +91,12 @@ class ILI9486(framebuf.FrameBuffer):
self._long = max(height, width) # Physical dimensions of screen and aspect ratio
self._short = min(height, width)
self._spi_init = init_spi
self._gscale = False # Interpret buffer as index into color LUT
mode = framebuf.GS4_HMSB
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * width // 2)
self._mvb = memoryview(buf)
self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode) # Logical aspect ratio
self._linebuf = bytearray(self._short * 2)
@ -140,11 +149,17 @@ class ILI9486(framebuf.FrameBuffer):
self._spi.write(data)
self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
# @micropython.native # Made almost no difference to timing
def show(self): # Physical display is in portrait mode
clut = ILI9486.lut
lb = self._linebuf
buf = self._mvb
buf = self.mvb
cm = self._gscale # color False, greyscale True
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM
@ -154,14 +169,14 @@ class ILI9486(framebuf.FrameBuffer):
wd = self.width // 2
ht = self.height
for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
_lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb)
else: # Landscpe 264ms on RP2 120MHz, 30MHz SPI clock
width = self.width
wd = width - 1
cargs = (self.height << 9) + (width << 18) # Viper 4-arg limit
for col in range(width): # For each column of landscape display
_lscopy(lb, buf, clut, wd - col + cargs) # Copy and map colors
_lscopy(lb, buf, clut, wd - col + cargs, cm) # Copy and map colors
self._spi.write(lb)
self._cs(1)
@ -172,7 +187,8 @@ class ILI9486(framebuf.FrameBuffer):
raise ValueError("Invalid do_refresh arg.")
clut = ILI9486.lut
lb = self._linebuf
buf = self._mvb
buf = self.mvb
cm = self._gscale # color False, greyscale True
self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1)
if self.width < self.height: # Portrait: write sets of rows
@ -183,7 +199,7 @@ class ILI9486(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared
self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors
_lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb)
line += lines
self._cs(1) # Allow other tasks to use bus
@ -197,7 +213,7 @@ class ILI9486(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared
self._cs(0)
for col in range(sc, ec, -1): # For each column of landscape display
_lscopy(lb, buf, clut, col + cargs) # Copy and map colors
_lscopy(lb, buf, clut, col + cargs, cm) # Copy and map colors
self._spi.write(lb)
sc -= lines
ec -= lines