# MicroPython ST7789 display driver, currently only has an SPI interface import micropython from micropython import const from time import sleep_ms # register definitions _SWRESET = const(0x01) _SLPIN = const(0x10) _SLPOUT = const(0x11) _NORON = const(0x13) _INVOFF = const(0x20) _INVON = const(0x21) _DISPON = const(0x29) _CASET = const(0x2a) _RASET = const(0x2b) _RAMWR = const(0x2c) _COLMOD = const(0x3a) _MADCTL = const(0x36) class ST7789(object): def __init__(self, width, height): self.width = width self.height = height self.linebuffer = bytearray(2 * width) self.init_display() def init_display(self): self.reset() self.write_cmd(_SLPOUT) sleep_ms(10) for cmd in ( (_COLMOD, b'\x05'), # MCU will send 16-bit RGB565 (_MADCTL, b'\x00'), # Left to right, top to bottom #(_INVOFF, None), # Results in odd palette (_INVON, None), (_NORON, None), ): self.write_cmd(cmd[0]) if cmd[1]: self.write_data(cmd[1]) self.fill(0) self.write_cmd(_DISPON) # From the point we sent the SLPOUT there must be a # 120ms gap before any subsequent SLPIN. In most cases # (i.e. wen the SPI baud rate is slower than 8M then # that time already elapsed as we zeroed the RAM). #sleep_ms(125) def poweroff(self): self.write_cmd(_SLPIN) sleep_ms(125) def poweron(self): self.write_cmd(_SLPOUT) sleep_ms(125) def contrast(self, contrast): pass def invert(self, invert): if invert: self.write_cmd(_INVON) else: self.write_cmd(_INVOFF) def set_window(self, x=0, y=0, width=None, height=None): if not width: width = self.width if not height: height = self.height xp = x + width - 1 yp = y + height - 1 self.write_cmd(_CASET) self.write_data(bytearray([x >> 8, x & 0xff, xp >> 8, xp & 0xff])) self.write_cmd(_RASET) self.write_data(bytearray([y >> 8, y & 0xff, yp >> 8, yp & 0xff])) self.write_cmd(_RAMWR) def fill(self, bg): self.set_window() # Populate the line buffer for x in range(0, 2 * self.width, 2): self.linebuffer[x] = bg >> 8 self.linebuffer[x+1] = bg & 0xff for y in range(self.height): self.write_data(self.linebuffer) @micropython.native def rleblit(self, image, pos=(0, 0), fg=0xffff, bg=0): (sx, sy, rle) = image self.set_window(pos[0], pos[1], sx, sy) # TODO: rework algorithm to allow us to reuse the line buffer buf = bytearray(2*sx) bp = 0 color = bg for rl in rle: while rl: buf[bp] = color >> 8 buf[bp+1] = color & 0xff bp += 2 rl -= 1 if bp >= (2*sx): self.write_data(buf) bp = 0 if color == bg: color = fg else: color = bg class ST7789_SPI(ST7789): def __init__(self, width, height, spi, cs, dc, res=None, rate=8000000): self.spi = spi self.dc = dc self.res = res self.cs = cs self.rate = rate #self.spi.init(baudrate=self.rate, polarity=1, phase=1) cs.init(cs.OUT, value=1) dc.init(dc.OUT, value=0) if res: res.init(res.OUT, value=0) super().__init__(width, height) def reset(self): if self.res: self.res(0) sleep_ms(10) self.res(1) else: self.write_cmd(_SWRESET) sleep_ms(125) def write_cmd(self, cmd): self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.dc(1) self.cs(0) self.spi.write(buf) self.cs(1)