diff --git a/drivers/ili94xx/ili9488.py b/drivers/ili94xx/ili9488.py index a76aec1..2f0a15f 100644 --- a/drivers/ili94xx/ili9488.py +++ b/drivers/ili94xx/ili9488.py @@ -22,122 +22,64 @@ import framebuf import asyncio from drivers.boolpalette import BoolPalette - -# Portrait mode greyscale +# Do processing from end to beginning for +# small performance improvement. +# greyscale @micropython.viper -def _lcopy_gs(dest: ptr8, source: ptr8, length: int): +def _lcopy_gs(dest: ptr8, source: ptr8, length: int) : # rgb666 - 18bit/pixel - n: int = 0 - x: int = 0 - while x < length: - c: uint = source[x] + n: int = length * 6 - 1 + while length: + length -= 1 + c : uint = source[length] # Store the index in the 4 high order bits - p: uint = c & 0xF0 # current pixel - q: uint = c << 4 # next pixel - - dest[n] = p - n += 1 - dest[n] = p - n += 1 - dest[n] = p - n += 1 + p : uint = c & 0xF0 # current pixel + q : uint = c << 4 # next pixel dest[n] = q - n += 1 + n -= 1 dest[n] = q - n += 1 - dest[n] = q - n += 1 + n -= 1 + dest[n] = q + n -= 1 - x += 1 + dest[n] = p + n -= 1 + dest[n] = p + n -= 1 + dest[n] = p + n -= 1 - -# Portrait mode color +# Do processing from end to beginning for +# small performance improvement. +# color @micropython.viper -def _lcopy(dest: ptr8, source: ptr8, lut: ptr16, length: int): +def _lcopy(dest: ptr8, source: ptr8, lut: ptr16, length: int) : # Convert lut rgb 565 to rgb666 - n: int = 0 - x: int = 0 - while x < length: - c: uint = source[x] - p: uint = c >> 4 # current pixel - q = c & 0x0F # next pixel + n: int = length * 6 - 1 + while length: + length -= 1 + c : uint = source[length] - v: uint16 = lut[p] - dest[n] = (v & 0xF800) >> 8 # R - n += 1 - dest[n] = (v & 0x07E0) >> 3 # G - n += 1 + v = lut[c & 0x0F] # next pixel dest[n] = (v & 0x001F) << 3 # B - n += 1 - - v = lut[q] - dest[n] = (v & 0xF800) >> 8 # R - n += 1 + n -= 1 dest[n] = (v & 0x07E0) >> 3 # G - n += 1 - dest[n] = (v & 0x001F) << 3 # B - n += 1 - - x += 1 - - -# FB is in landscape mode greyscale -@micropython.viper -def _lscopy_gs(dest: ptr8, source: ptr8, ch: int): - col = ch & 0x1FF # Unpack (viper old 4 parameter limit) - height = (ch >> 9) & 0x1FF - wbytes = ch >> 19 # Width in bytes is width // 2 - # rgb666 - 18bit/pixel - n = 0 - clsb = col & 1 - idx = col >> 1 # 2 pixels per byte - while height: - if clsb: - c = source[idx] << 4 - else: - c = source[idx] & 0xF0 - dest[n] = c - n += 1 - dest[n] = c - n += 1 - dest[n] = c - n += 1 - idx += wbytes - height -= 1 - - -# FB is in landscape mode color, hence issue a column at a time to portrait mode hardware. -@micropython.viper -def _lscopy(dest: ptr8, source: ptr8, lut: ptr16, ch: int): - # Convert lut rgb 565 to rgb666 - col = ch & 0x1FF # Unpack (viper old 4 parameter limit) - height = (ch >> 9) & 0x1FF - wbytes = ch >> 19 # Width in bytes is width // 2 - n = 0 - clsb = col & 1 - idx = col >> 1 # 2 pixels per byte - while height: - if clsb: - c = source[idx] & 0x0F - else: - c = source[idx] >> 4 - v: uint16 = lut[c] + n -= 1 dest[n] = (v & 0xF800) >> 8 # R - n += 1 - dest[n] = (v & 0x07E0) >> 3 # G - n += 1 + n -= 1 + + v : uint = lut[c >> 4] # current pixel dest[n] = (v & 0x001F) << 3 # B - n += 1 - - idx += wbytes - height -= 1 - + n -= 1 + dest[n] = (v & 0x07E0) >> 3 # G + n -= 1 + dest[n] = (v & 0xF800) >> 8 # R + n -= 1 class ILI9488(framebuf.FrameBuffer): lut = bytearray(32) - COLOR_INVERT = 0 # Convert r, g, b in range 0-255 to a 16 bit colour value @@ -149,7 +91,8 @@ class ILI9488(framebuf.FrameBuffer): # Transpose width & height for landscape mode def __init__( - self, spi, cs, dc, rst, height=320, width=480, usd=False, mirror=False, init_spi=False + self, spi, cs, dc, rst, height=320, width=480, usd=False, mirror=False, init_spi=False, + lines_per_write=4 ): self._spi = spi self._cs = cs @@ -158,17 +101,21 @@ class ILI9488(framebuf.FrameBuffer): self.lock_mode = False # If set, user lock is passed to .do_refresh self.height = height # Logical dimensions for GUIs self.width = width - 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 self.mode = framebuf.GS4_HMSB self.palette = BoolPalette(self.mode) + # + # lines_per_write must divide evenly into height + # + if (self.height % lines_per_write) != 0 : + raise ValueError('lines_per_write invalid') + self._lines_per_write=lines_per_write gc.collect() buf = bytearray(height * width // 2) self.mvb = memoryview(buf) super().__init__(buf, width, height, self.mode) # Logical aspect ratio - self._linebuf = bytearray(self._short * 3) + self._linebuf = bytearray(self._lines_per_write*self.width * 3) # Hardware reset self._rst(0) @@ -185,18 +132,18 @@ class ILI9488(framebuf.FrameBuffer): self._wcmd(b"\x11") # sleep out sleep_ms(20) self._wcd(b"\x3a", b"\x66") # interface pixel format 18 bits per pixel - # Normally use defaults. This allows it to work on the Waveshare board with a - # shift register. If size is not 320x480 assume no shift register. - # Default column address start == 0, end == 0x13F (319) - if self._short != 320: # Not the Waveshare board: no shift register - self._wcd(b"\x2a", int.to_bytes(self._short - 1, 4, "big")) - # Default page address start == 0 end == 0x1DF (479) - if self._long != 480: - self._wcd(b"\x2b", int.to_bytes(self._long - 1, 4, "big")) # SET_PAGE ht - # self._wcd(b"\x36", b"\x48" if usd else b"\x88") # MADCTL: RGB portrait mode - madctl = 0x48 if usd else 0x88 + + self._wcd(b"\x2a", int.to_bytes(self.width - 1, 4, "big")) + self._wcd(b"\x2b", int.to_bytes(self.height - 1, 4, "big")) # SET_PAGE ht + + if self.width > self.height : + # landscape + madctl = 0xe8 if usd else 0x28 + else : + #portrait + madctl = 0x48 if usd else 0x88 if mirror: - madctl ^= 0x80 + madctl ^= 0x80 # toggle MY self._wcd(b"\x36", madctl.to_bytes(1, "big")) # MADCTL: RGB portrait mode self._wcmd(b"\x11") # sleep out self._wcmd(b"\x29") # display on @@ -226,7 +173,6 @@ class ILI9488(framebuf.FrameBuffer): # @micropython.native # Made almost no difference to timing def show(self): # Physical display is in portrait mode - clut = ILI9488.lut lb = self._linebuf buf = self.mvb cm = self._gscale # color False, greyscale True @@ -235,30 +181,22 @@ class ILI9488(framebuf.FrameBuffer): self._wcmd(b"\x2c") # WRITE_RAM self._dc(1) self._cs(0) - if self.width < self.height: # Portrait 350 ms on ESP32 160 MHz, 26.6 MHz SPI clock - wd = self.width // 2 - ht = self.height - if cm: - for start in range(0, wd * ht, wd): # For each line - _lcopy_gs(lb, buf[start:], wd) # Copy greyscale - self._spi.write(lb) - else: - for start in range(0, wd * ht, wd): # For each line - _lcopy(lb, buf[start:], clut, wd) # Copy and map colors - self._spi.write(lb) - else: # Landscape 370 ms on ESP32 160 MHz, 26.6 MHz SPI clock - width = self.width - wd = width - 1 - cargs = (self.height << 9) + (width << 18) # Viper 4-arg limit - if cm: - for col in range(width): # For each column of landscape display - _lscopy_gs(lb, buf, wd - col + cargs) # Copy greyscale - self._spi.write(lb) - else: - for col in range(width): # For each column of landscape display - _lscopy(lb, buf, clut, wd - col + cargs) # Copy and map colors - self._spi.write(lb) - + wd = self.width >> 1 + ht = self.height + spi_write = self._spi.write + length = self._lines_per_write*wd + r = range(0, wd * ht, length) + if cm : + lcopy = _lcopy_gs # Copy greyscale + for start in r : # For each line + lcopy(lb, buf[start:], length) + spi_write(lb) + else : + clut = ILI9488.lut + lcopy = _lcopy # Copy and map colors + for start in r : # For each line + lcopy(lb, buf[start:], clut, length) + spi_write(lb) self._cs(1) def short_lock(self, v=None): @@ -272,58 +210,38 @@ class ILI9488(framebuf.FrameBuffer): if elock is None: elock = asyncio.Lock() async with self._lock: - lines, mod = divmod(self._long, split) # Lines per segment + lines, mod = divmod(self.height, split) # Lines per segment if mod: - raise ValueError("Invalid do_refresh arg.") + raise ValueError("Invalid do_refresh arg 'split'") + if lines % self._lines_per_write != 0 : + raise ValueError("Invalid do_refresh arg 'split' for lines_per_write of %d" %(self._lines_per_write)) clut = ILI9488.lut lb = self._linebuf 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 - wd = self.width // 2 - line = 0 - for _ in range(split): # For each segment - async with elock: - if self._spi_init: # A callback was passed - self._spi_init(self._spi) # Bus may be shared - self._cs(0) - if cm: - for start in range( - wd * line, wd * (line + lines), wd - ): # For each line - _lcopy_gs(lb, buf[start:], wd) # Copy and greyscale - self._spi.write(lb) - else: - for start in range( - wd * line, wd * (line + lines), wd - ): # For each line - _lcopy(lb, buf[start:], clut, wd) # Copy and map colors - self._spi.write(lb) + wd = self.width // 2 + line = 0 + spi_write = self._spi.write + length = self._lines_per_write*wd + for _ in range(split): # For each segment + async with elock: + if self._spi_init: # A callback was passed + self._spi_init(self._spi) # Bus may be shared + self._cs(0) + r = range(wd * line, wd * (line + lines), length) + if cm: + lcopy = _lcopy_gs # Copy and greyscale + for start in r : + lcopy(lb, buf[start:], length) + spi_write(lb) + else : + lcopy = _lcopy # Copy and map colors + for start in r : + lcopy(lb, buf[start:], clut, length) + spi_write(lb) - line += lines - self._cs(1) # Allow other tasks to use bus - await asyncio.sleep_ms(0) - else: # Landscape: write sets of cols. lines is no. of cols per segment. - cargs = (self.height << 9) + (self.width << 18) # Viper 4-arg limit - sc = self.width - 1 # Start and end columns - ec = sc - lines # End column - for _ in range(split): # For each segment - async with elock: - if self._spi_init: # A callback was passed - self._spi_init(self._spi) # Bus may be shared - self._cs(0) - if cm: - for col in range(sc, ec, -1): # For each column of landscape display - _lscopy_gs(lb, buf, col + cargs) # Copy and map colors - self._spi.write(lb) - else: - for col in range(sc, ec, -1): # For each column of landscape display - _lscopy(lb, buf, clut, col + cargs) # Copy and map colors - self._spi.write(lb) - - sc -= lines - ec -= lines - self._cs(1) # Allow other tasks to use bus - await asyncio.sleep_ms(0) + line += lines + self._cs(1) # Allow other tasks to use bus + await asyncio.sleep_ms(0)