313 lines
8.3 KiB
Python
313 lines
8.3 KiB
Python
import time
|
|
from micropython import const
|
|
import ustruct as struct
|
|
|
|
# commands
|
|
ST77XX_NOP = const(0x00)
|
|
ST77XX_SWRESET = const(0x01)
|
|
ST77XX_RDDID = const(0x04)
|
|
ST77XX_RDDST = const(0x09)
|
|
|
|
ST77XX_SLPIN = const(0x10)
|
|
ST77XX_SLPOUT = const(0x11)
|
|
ST77XX_PTLON = const(0x12)
|
|
ST77XX_NORON = const(0x13)
|
|
|
|
ST77XX_INVOFF = const(0x20)
|
|
ST77XX_INVON = const(0x21)
|
|
ST77XX_DISPOFF = const(0x28)
|
|
ST77XX_DISPON = const(0x29)
|
|
ST77XX_CASET = const(0x2A)
|
|
ST77XX_RASET = const(0x2B)
|
|
ST77XX_RAMWR = const(0x2C)
|
|
ST77XX_RAMRD = const(0x2E)
|
|
|
|
ST77XX_PTLAR = const(0x30)
|
|
ST77XX_COLMOD = const(0x3A)
|
|
ST7789_MADCTL = const(0x36)
|
|
|
|
ST7789_MADCTL_MY = const(0x80)
|
|
ST7789_MADCTL_MX = const(0x40)
|
|
ST7789_MADCTL_MV = const(0x20)
|
|
ST7789_MADCTL_ML = const(0x10)
|
|
ST7789_MADCTL_BGR = const(0x08)
|
|
ST7789_MADCTL_MH = const(0x04)
|
|
ST7789_MADCTL_RGB = const(0x00)
|
|
|
|
ST7789_RDID1 = const(0xDA)
|
|
ST7789_RDID2 = const(0xDB)
|
|
ST7789_RDID3 = const(0xDC)
|
|
ST7789_RDID4 = const(0xDD)
|
|
|
|
ColorMode_65K = const(0x50)
|
|
ColorMode_262K = const(0x60)
|
|
ColorMode_12bit = const(0x03)
|
|
ColorMode_16bit = const(0x05)
|
|
ColorMode_18bit = const(0x06)
|
|
ColorMode_16M = const(0x07)
|
|
|
|
# Color definitions
|
|
BLACK = const(0x0000)
|
|
BLUE = const(0x001F)
|
|
RED = const(0xF800)
|
|
GREEN = const(0x07E0)
|
|
CYAN = const(0x07FF)
|
|
MAGENTA = const(0xF81F)
|
|
YELLOW = const(0xFFE0)
|
|
WHITE = const(0xFFFF)
|
|
|
|
_ENCODE_PIXEL = ">H"
|
|
_ENCODE_POS = ">HH"
|
|
_DECODE_PIXEL = ">BBB"
|
|
|
|
_BUFFER_SIZE = const(256)
|
|
|
|
|
|
def delay_ms(ms):
|
|
time.sleep_ms(ms)
|
|
|
|
|
|
def color565(r, g=0, b=0):
|
|
"""Convert red, green and blue values (0-255) into a 16-bit 565 encoding. As
|
|
a convenience this is also available in the parent adafruit_rgb_display
|
|
package namespace."""
|
|
try:
|
|
r, g, b = r # see if the first var is a tuple/list
|
|
except TypeError:
|
|
pass
|
|
return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
|
|
|
|
|
|
class ST77xx:
|
|
def __init__(self, spi, width, height, reset, dc, cs=None, backlight=None,
|
|
xstart=-1, ystart=-1):
|
|
"""
|
|
display = st7789.ST7789(
|
|
SPI(1, baudrate=40000000, phase=0, polarity=1),
|
|
240, 240,
|
|
reset=machine.Pin(5, machine.Pin.OUT),
|
|
dc=machine.Pin(2, machine.Pin.OUT),
|
|
)
|
|
|
|
"""
|
|
self.width = width
|
|
self.height = height
|
|
self.spi = spi
|
|
if spi is None:
|
|
import machine
|
|
self.spi = machine.SPI(1, baudrate=40000000, phase=0, polarity=1)
|
|
self.reset = reset
|
|
self.dc = dc
|
|
self.cs = cs
|
|
self.backlight = backlight
|
|
if xstart >= 0 and ystart >= 0:
|
|
self.xstart = xstart
|
|
self.ystart = ystart
|
|
elif (self.width, self.height) == (240, 240):
|
|
self.xstart = 0
|
|
self.ystart = 0
|
|
elif (self.width, self.height) == (135, 240):
|
|
self.xstart = 52
|
|
self.ystart = 40
|
|
else:
|
|
raise ValueError(
|
|
"Unsupported display. Only 240x240 and 135x240 are supported "
|
|
"without xstart and ystart provided"
|
|
)
|
|
|
|
def dc_low(self):
|
|
self.dc.off()
|
|
|
|
def dc_high(self):
|
|
self.dc.on()
|
|
|
|
def reset_low(self):
|
|
if self.reset:
|
|
self.reset.off()
|
|
|
|
def reset_high(self):
|
|
if self.reset:
|
|
self.reset.on()
|
|
|
|
def cs_low(self):
|
|
if self.cs:
|
|
self.cs.off()
|
|
|
|
def cs_high(self):
|
|
if self.cs:
|
|
self.cs.on()
|
|
|
|
def write(self, command=None, data=None):
|
|
"""SPI write to the device: commands and data"""
|
|
self.cs_low()
|
|
if command is not None:
|
|
self.dc_low()
|
|
self.spi.write(bytes([command]))
|
|
if data is not None:
|
|
self.dc_high()
|
|
self.spi.write(data)
|
|
self.cs_high()
|
|
|
|
def hard_reset(self):
|
|
self.cs_low()
|
|
self.reset_high()
|
|
delay_ms(50)
|
|
self.reset_low()
|
|
delay_ms(50)
|
|
self.reset_high()
|
|
delay_ms(150)
|
|
self.cs_high()
|
|
|
|
def soft_reset(self):
|
|
self.write(ST77XX_SWRESET)
|
|
delay_ms(150)
|
|
|
|
def sleep_mode(self, value):
|
|
if value:
|
|
self.write(ST77XX_SLPIN)
|
|
else:
|
|
self.write(ST77XX_SLPOUT)
|
|
|
|
def inversion_mode(self, value):
|
|
if value:
|
|
self.write(ST77XX_INVON)
|
|
else:
|
|
self.write(ST77XX_INVOFF)
|
|
|
|
def _set_color_mode(self, mode):
|
|
self.write(ST77XX_COLMOD, bytes([mode & 0x77]))
|
|
|
|
def init(self, *args, **kwargs):
|
|
self.hard_reset()
|
|
self.soft_reset()
|
|
self.sleep_mode(False)
|
|
if self.backlight:
|
|
self.backlight.on()
|
|
|
|
def _set_mem_access_mode(self, rotation, vert_mirror, horz_mirror, is_bgr):
|
|
rotation &= 7
|
|
value = {
|
|
0: 0,
|
|
1: ST7789_MADCTL_MX,
|
|
2: ST7789_MADCTL_MY,
|
|
3: ST7789_MADCTL_MX | ST7789_MADCTL_MY,
|
|
4: ST7789_MADCTL_MV,
|
|
5: ST7789_MADCTL_MV | ST7789_MADCTL_MX,
|
|
6: ST7789_MADCTL_MV | ST7789_MADCTL_MY,
|
|
7: ST7789_MADCTL_MV | ST7789_MADCTL_MX | ST7789_MADCTL_MY,
|
|
}[rotation]
|
|
|
|
if vert_mirror:
|
|
value = ST7789_MADCTL_ML
|
|
elif horz_mirror:
|
|
value = ST7789_MADCTL_MH
|
|
|
|
if is_bgr:
|
|
value |= ST7789_MADCTL_BGR
|
|
self.write(ST7789_MADCTL, bytes([value]))
|
|
|
|
def _encode_pos(self, x, y):
|
|
"""Encode a postion into bytes."""
|
|
return struct.pack(_ENCODE_POS, x, y)
|
|
|
|
def _encode_pixel(self, color):
|
|
"""Encode a pixel color into bytes."""
|
|
return struct.pack(_ENCODE_PIXEL, color)
|
|
|
|
def _set_columns(self, start, end):
|
|
if start > end or end >= self.width:
|
|
return
|
|
start += self.xstart
|
|
end += self.xstart
|
|
self.write(ST77XX_CASET, self._encode_pos(start, end))
|
|
|
|
def _set_rows(self, start, end):
|
|
if start > end or end >= self.height:
|
|
return
|
|
start += self.ystart
|
|
end += self.ystart
|
|
self.write(ST77XX_RASET, self._encode_pos(start, end))
|
|
|
|
def set_window(self, x0, y0, x1, y1):
|
|
self._set_columns(x0, x1)
|
|
self._set_rows(y0, y1)
|
|
self.write(ST77XX_RAMWR)
|
|
|
|
def vline(self, x, y, length, color):
|
|
self.fill_rect(x, y, 1, length, color)
|
|
|
|
def hline(self, x, y, length, color):
|
|
self.fill_rect(x, y, length, 1, color)
|
|
|
|
def pixel(self, x, y, color):
|
|
self.set_window(x, y, x, y)
|
|
self.write(None, self._encode_pixel(color))
|
|
|
|
def blit_buffer(self, buffer, x, y, width, height):
|
|
self.set_window(x, y, x + width - 1, y + height - 1)
|
|
self.write(None, buffer)
|
|
|
|
def rect(self, x, y, w, h, color):
|
|
self.hline(x, y, w, color)
|
|
self.vline(x, y, h, color)
|
|
self.vline(x + w - 1, y, h, color)
|
|
self.hline(x, y + h - 1, w, color)
|
|
|
|
def fill_rect(self, x, y, width, height, color):
|
|
self.set_window(x, y, x + width - 1, y + height - 1)
|
|
chunks, rest = divmod(width * height, _BUFFER_SIZE)
|
|
pixel = self._encode_pixel(color)
|
|
self.dc_high()
|
|
if chunks:
|
|
data = pixel * _BUFFER_SIZE
|
|
for _ in range(chunks):
|
|
self.write(None, data)
|
|
if rest:
|
|
self.write(None, pixel * rest)
|
|
|
|
def fill(self, color):
|
|
self.fill_rect(0, 0, self.width, self.height, color)
|
|
|
|
def line(self, x0, y0, x1, y1, color):
|
|
# Line drawing function. Will draw a single pixel wide line starting at
|
|
# x0, y0 and ending at x1, y1.
|
|
steep = abs(y1 - y0) > abs(x1 - x0)
|
|
if steep:
|
|
x0, y0 = y0, x0
|
|
x1, y1 = y1, x1
|
|
if x0 > x1:
|
|
x0, x1 = x1, x0
|
|
y0, y1 = y1, y0
|
|
dx = x1 - x0
|
|
dy = abs(y1 - y0)
|
|
err = dx // 2
|
|
if y0 < y1:
|
|
ystep = 1
|
|
else:
|
|
ystep = -1
|
|
while x0 <= x1:
|
|
if steep:
|
|
self.pixel(y0, x0, color)
|
|
else:
|
|
self.pixel(x0, y0, color)
|
|
err -= dy
|
|
if err < 0:
|
|
y0 += ystep
|
|
err += dx
|
|
x0 += 1
|
|
|
|
|
|
class ST7789(ST77xx):
|
|
def init(self, *, color_mode=ColorMode_65K | ColorMode_16bit):
|
|
super().init()
|
|
self._set_color_mode(color_mode)
|
|
delay_ms(50)
|
|
self._set_mem_access_mode(4, True, True, False)
|
|
self.inversion_mode(True)
|
|
delay_ms(10)
|
|
self.write(ST77XX_NORON)
|
|
delay_ms(10)
|
|
self.fill(0)
|
|
self.write(ST77XX_DISPON)
|
|
delay_ms(500)
|