初始化提交
This commit is contained in:
330
boards/default/micropython/build/lib/st7789.py
Normal file
330
boards/default/micropython/build/lib/st7789.py
Normal file
@@ -0,0 +1,330 @@
|
||||
"""
|
||||
ST7789
|
||||
|
||||
MicroPython library for the ST7789(TFT-SPI)
|
||||
=======================================================
|
||||
#Preliminary composition 20220830
|
||||
#https://github.com/russhughes/st7789py_mpy
|
||||
|
||||
dahanzimin From the Mixly Team
|
||||
"""
|
||||
|
||||
import time
|
||||
from machine import Pin
|
||||
from micropython import const
|
||||
from uframebuf import Font_Uincode, Image
|
||||
import ustruct as struct
|
||||
|
||||
|
||||
ST7789_NOP = const(0x00)
|
||||
ST7789_SWRESET = const(0x01)
|
||||
ST7789_RDDID = const(0x04)
|
||||
ST7789_RDDST = const(0x09)
|
||||
ST7789_SLPIN = const(0x10)
|
||||
ST7789_SLPOUT = const(0x11)
|
||||
ST7789_PTLON = const(0x12)
|
||||
ST7789_NORON = const(0x13)
|
||||
ST7789_INVOFF = const(0x20)
|
||||
ST7789_INVON = const(0x21)
|
||||
ST7789_DISPOFF = const(0x28)
|
||||
ST7789_DISPON = const(0x29)
|
||||
ST7789_CASET = const(0x2A)
|
||||
ST7789_RASET = const(0x2B)
|
||||
ST7789_RAMWR = const(0x2C)
|
||||
ST7789_RAMRD = const(0x2E)
|
||||
ST7789_PTLAR = const(0x30)
|
||||
ST7789_VSCRDEF = const(0x33)
|
||||
ST7789_COLMOD = const(0x3A)
|
||||
ST7789_MADCTL = const(0x36)
|
||||
ST7789_VSCSAD = const(0x37)
|
||||
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)
|
||||
COLOR_MODE_65K = const(0x50)
|
||||
COLOR_MODE_262K = const(0x60)
|
||||
COLOR_MODE_12BIT = const(0x03)
|
||||
COLOR_MODE_16BIT = const(0x05)
|
||||
COLOR_MODE_18BIT = const(0x06)
|
||||
COLOR_MODE_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)
|
||||
|
||||
_BUFFER_SIZE = const(256)
|
||||
|
||||
# Rotation tables (width, height, xstart, ystart)[rotation % 4]
|
||||
WIDTH_320 = [(240, 320, 0, 0),(320, 240, 0, 0),(240, 320, 0, 0),(320, 240, 0, 0)]
|
||||
WIDTH_240 = [(240, 240, 0, 0),(240, 240, 0, 0),(240, 240, 0, 80),(240, 240, 80, 0)]
|
||||
WIDTH_135 = [(135, 240, 52, 40),(240, 135, 40, 53),(135, 240, 53, 40),(240, 135, 40, 52)]
|
||||
|
||||
ROTATIONS = [0x00, 0x60, 0xc0, 0xa0]
|
||||
|
||||
def color565(red, green=0, blue=0):
|
||||
""" Convert red, green and blue values (0-255) into a 16-bit 565 encoding."""
|
||||
try:
|
||||
red, green, blue = red # see if the first var is a tuple/list
|
||||
except TypeError:
|
||||
pass
|
||||
return (red & 0xf8) << 8 | (green & 0xfc) << 3 | blue >> 3
|
||||
|
||||
def _encode_pos(x, y):
|
||||
"""Encode a postion into bytes."""
|
||||
return struct.pack(">HH", x, y)
|
||||
|
||||
def _encode_pixel(color):
|
||||
"""Encode a pixel color into bytes."""
|
||||
return struct.pack(">H", color)
|
||||
|
||||
class ST7789():
|
||||
def __init__(self, spi, width, height, dc_pin=None,cs_pin=None, rotation=0, font_address=0x700000):
|
||||
if height != 240 or width not in [320, 240, 135]:
|
||||
raise ValueError("Unsupported display. 320x240, 240x240 and 135x240 are supported.")
|
||||
|
||||
self._font= Font_Uincode(font_address)
|
||||
self._image= Image()
|
||||
self._display_width = self.width = width
|
||||
self._display_height = self.height = height
|
||||
self.xstart = 0
|
||||
self.ystart = 0
|
||||
self.spi = spi
|
||||
self.spi.init(polarity=1)
|
||||
self.dc = Pin(dc_pin, Pin.OUT)
|
||||
self.cs = Pin(cs_pin, Pin.OUT)
|
||||
self._rotation = rotation % 4
|
||||
self.soft_reset()
|
||||
self.sleep_mode(False)
|
||||
|
||||
self._set_color_mode(COLOR_MODE_65K | COLOR_MODE_16BIT)
|
||||
time.sleep_ms(50)
|
||||
self.rotation(self._rotation)
|
||||
self.inversion_mode(True)
|
||||
time.sleep_ms(10)
|
||||
self._write(ST7789_NORON)
|
||||
time.sleep_ms(10)
|
||||
self.fill(0)
|
||||
self._write(ST7789_DISPON)
|
||||
time.sleep_ms(500)
|
||||
|
||||
def _write(self, command=None, data=None):
|
||||
"""SPI write to the device: commands and data."""
|
||||
if self.cs:
|
||||
self.cs.off()
|
||||
if command is not None:
|
||||
self.dc.off()
|
||||
self.spi.write(bytes([command]))
|
||||
if data is not None:
|
||||
self.dc.on()
|
||||
self.spi.write(data)
|
||||
if self.cs:
|
||||
self.cs.on()
|
||||
|
||||
def soft_reset(self):
|
||||
"""Soft reset display."""
|
||||
self._write(ST7789_SWRESET)
|
||||
time.sleep_ms(150)
|
||||
|
||||
def sleep_mode(self, value):
|
||||
"""Enable or disable display sleep mode."""
|
||||
if value:
|
||||
self._write(ST7789_SLPIN)
|
||||
else:
|
||||
self._write(ST7789_SLPOUT)
|
||||
|
||||
def inversion_mode(self, value):
|
||||
"""Enable or disable display inversion mode."""
|
||||
if value:
|
||||
self._write(ST7789_INVON)
|
||||
else:
|
||||
self._write(ST7789_INVOFF)
|
||||
|
||||
def _set_color_mode(self, mode):
|
||||
""" Set display color mode. """
|
||||
self._write(ST7789_COLMOD, bytes([mode & 0x77]))
|
||||
|
||||
def rotation(self, rotation):
|
||||
"""Set display rotation."""
|
||||
rotation %= 4
|
||||
self._rotation = rotation
|
||||
madctl = ROTATIONS[rotation]
|
||||
|
||||
if self._display_width == 320:
|
||||
table = WIDTH_320
|
||||
elif self._display_width == 240:
|
||||
table = WIDTH_240
|
||||
elif self._display_width == 135:
|
||||
table = WIDTH_135
|
||||
else:
|
||||
raise ValueError("Unsupported display. 320x240, 240x240 and 135x240 are supported.")
|
||||
self.width, self.height, self.xstart, self.ystart = table[rotation]
|
||||
self._write(ST7789_MADCTL, bytes([madctl]))
|
||||
|
||||
def _set_columns(self, start, end):
|
||||
"""Send CASET (column address set) command to display."""
|
||||
if start <= end <= self.width:
|
||||
self._write(ST7789_CASET, _encode_pos(
|
||||
start+self.xstart, end + self.xstart))
|
||||
|
||||
def _set_rows(self, start, end):
|
||||
"""Send RASET (row address set) command to display."""
|
||||
if start <= end <= self.height:
|
||||
self._write(ST7789_RASET, _encode_pos(
|
||||
start+self.ystart, end+self.ystart))
|
||||
|
||||
def _set_window(self, x0, y0, x1, y1):
|
||||
"""Set window to column and row address."""
|
||||
self._set_columns(x0, x1)
|
||||
self._set_rows(y0, y1)
|
||||
self._write(ST7789_RAMWR)
|
||||
|
||||
def vline(self, x, y, length, color):
|
||||
"""Draw vertical line at the given location and color."""
|
||||
self.fill_rect(x, y, 1, length, color)
|
||||
|
||||
def hline(self, x, y, length, color):
|
||||
"""Draw horizontal line at the given location and color."""
|
||||
self.fill_rect(x, y, length, 1, color)
|
||||
|
||||
def pixel(self, x, y, color):
|
||||
"""Draw a pixel at the given location and color."""
|
||||
self._set_window(x, y, x, y)
|
||||
pixel_color =_encode_pixel(color) if type(color)==int else _encode_pixel(color565(color))
|
||||
self._write(None, pixel_color)
|
||||
|
||||
def blit_buffer(self, buffer, x, y, width, height):
|
||||
"""Copy buffer to display at the given location."""
|
||||
self._set_window(x, y, x + width - 1, y + height - 1)
|
||||
self._write(None, buffer)
|
||||
|
||||
def rect(self, x, y, w, h, color):
|
||||
"""Draw a rectangle at the given location, size and 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):
|
||||
"""Draw a rectangle at the given location, size and filled with color."""
|
||||
self._set_window(x, y, x + width - 1, y + height - 1)
|
||||
chunks, rest = divmod(width * height, _BUFFER_SIZE)
|
||||
pixel = _encode_pixel(color) if type(color)==int else _encode_pixel(color565(color))
|
||||
self.dc.on()
|
||||
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):
|
||||
"""Fill the entire FrameBuffer with the specified color."""
|
||||
self.fill_rect(0, 0, self.width, self.height, color)
|
||||
|
||||
def line(self, x0, y0, x1, y1, color):
|
||||
"""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
|
||||
ystep = 1 if y0 < y1 else -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
|
||||
|
||||
def image(self,path, x=0, y=0, size=1, color=WHITE, invert=0):
|
||||
"""Set buffer to value of Python Imaging Library image"""
|
||||
size=max(round(size),1)
|
||||
if type(path) ==str :
|
||||
buffer_info,(width, height) = self._image.load(path,invert)
|
||||
elif type(path) ==bytes:
|
||||
buffer_info,(width, height) = self._image.load_py(path,invert)
|
||||
else:
|
||||
raise ValueError("invalid input")
|
||||
|
||||
self.bitmap((buffer_info,(width, height)), x, y, size, color)
|
||||
|
||||
def bitmap(self,buffer, x=0, y=0, size=1, color=WHITE):
|
||||
"""Graphic model display(buffer,(width,height))"""
|
||||
buffer_info,(width,height)=buffer
|
||||
if x < -width*size or x >= self.width or y < -height*size or y >= self.height:
|
||||
return #Limit reasonable display area
|
||||
color_buffer = bytearray(0)
|
||||
byteWidth = int((width + 7) / 8)
|
||||
for j in range(height):
|
||||
for i in range(width):
|
||||
if buffer_info[int(j * byteWidth + i / 8)] & (0x80 >> (i & 7)):
|
||||
self.fill_rect(x+i*size, y+j*size, size, size, color)
|
||||
|
||||
def _take_buffer(self,strs,space,size=1):
|
||||
'''Get character lattice information first'''
|
||||
font_buffer=[]
|
||||
font_len=0
|
||||
for c in strs:
|
||||
buffer=self._font.chardata(c)
|
||||
font_buffer.append(buffer)
|
||||
font_len=font_len+buffer[1][0]*size+space
|
||||
return font_len,font_buffer
|
||||
|
||||
def shows(self,data,x=0,y=0,size=1,space=0,center=False, color=WHITE):
|
||||
"""Display character"""
|
||||
if data:
|
||||
size=max(round(size),1)
|
||||
font_len,font_buffer=self._take_buffer(str(data),space,size)
|
||||
x=(self.width-font_len+space)//2 if center else x
|
||||
#self.fill_rect(x-1,y-1,font_len+2,font_buffer[0][1][1]*size+2,0)
|
||||
for buffer in font_buffer: #Display character
|
||||
self.bitmap(buffer,x,y,size,color)
|
||||
x=buffer[1][0]*size+x+space
|
||||
|
||||
def frame(self, data, delay=500, size=5, color=WHITE):
|
||||
"""Display one frame per character"""
|
||||
if data:
|
||||
size=max(round(size),1)
|
||||
_,font_buffer=self._take_buffer(str(data),0)
|
||||
for buffer in font_buffer:
|
||||
x=(self.width - buffer[1][0]*size)//2
|
||||
y=(self.height - buffer[1][1]*size)//2
|
||||
self.fill_rect(x-1,y-1,buffer[1][0]*size+2,buffer[1][1]*size+2,0)
|
||||
self.bitmap(buffer,x,y,size,color)
|
||||
#self.show()
|
||||
time.sleep_ms(delay)
|
||||
|
||||
def scroll(self, data, y=0, size=1, space=0, speed=5, color=WHITE):
|
||||
"""Scrolling characters"""
|
||||
if data:
|
||||
size=max(round(size),1)
|
||||
font_len,font_buffer=self._take_buffer(str(data),space,size)
|
||||
for i in range(font_len-space+self.width):
|
||||
x=-i+self.width
|
||||
self.fill_rect(x-1,y-1,self.width-x+2,font_buffer[0][1][1]*size+2,0)
|
||||
for buffer in font_buffer:
|
||||
self.bitmap(buffer,x,y,size,color)
|
||||
x=buffer[1][0]*size+x+space
|
||||
time.sleep_ms(speed)
|
||||
Reference in New Issue
Block a user