Initial commit
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
"""
|
||||
Copyright (c) 2020, 2021 Russ Hughes
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice and is licensed under the same terms:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Ivan Belokobylskiy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
The driver is based on devbis' st7789py_mpy module from
|
||||
https://github.com/devbis/st7789py_mpy.
|
||||
|
||||
This driver adds support for:
|
||||
|
||||
- 320x240, 240x240 and 135x240 pixel displays
|
||||
- Display rotation
|
||||
- Hardware based scrolling
|
||||
- Drawing text using 8 and 16 bit wide bitmap fonts with heights that are
|
||||
multiples of 8. Included are 12 bitmap fonts derived from classic pc
|
||||
BIOS text mode fonts.
|
||||
- Drawing text using converted TrueType fonts.
|
||||
- Drawing converted bitmaps
|
||||
|
||||
"""
|
||||
|
||||
import time
|
||||
from micropython import const
|
||||
import ustruct as struct
|
||||
|
||||
# commands
|
||||
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)
|
||||
|
||||
_ENCODE_PIXEL = ">H"
|
||||
_ENCODE_POS = ">HH"
|
||||
_DECODE_PIXEL = ">BBB"
|
||||
|
||||
_BUFFER_SIZE = const(256)
|
||||
|
||||
_BIT7 = const(0x80)
|
||||
_BIT6 = const(0x40)
|
||||
_BIT5 = const(0x20)
|
||||
_BIT4 = const(0x10)
|
||||
_BIT3 = const(0x08)
|
||||
_BIT2 = const(0x04)
|
||||
_BIT1 = const(0x02)
|
||||
_BIT0 = const(0x01)
|
||||
|
||||
# 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)]
|
||||
|
||||
# MADCTL ROTATIONS[rotation % 4]
|
||||
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(_ENCODE_POS, x, y)
|
||||
|
||||
|
||||
def _encode_pixel(color):
|
||||
"""Encode a pixel color into bytes."""
|
||||
return struct.pack(_ENCODE_PIXEL, color)
|
||||
|
||||
|
||||
class ST7789():
|
||||
"""
|
||||
ST7789 driver class
|
||||
|
||||
Args:
|
||||
spi (spi): spi object **Required**
|
||||
width (int): display width **Required**
|
||||
height (int): display height **Required**
|
||||
reset (pin): reset pin
|
||||
dc (pin): dc pin **Required**
|
||||
cs (pin): cs pin
|
||||
backlight(pin): backlight pin
|
||||
rotation (int): display rotation
|
||||
- 0-Portrait
|
||||
- 1-Landscape
|
||||
- 2-Inverted Portrait
|
||||
- 3-Inverted Landscape
|
||||
"""
|
||||
def __init__(self, spi, width, height, reset=None, dc=None,
|
||||
cs=None, backlight=None, rotation=0):
|
||||
"""
|
||||
Initialize display.
|
||||
"""
|
||||
if height != 240 or width not in [320, 240, 135]:
|
||||
raise ValueError(
|
||||
"Unsupported display. 320x240, 240x240 and 135x240 are supported."
|
||||
)
|
||||
|
||||
if dc is None:
|
||||
raise ValueError("dc pin is required.")
|
||||
|
||||
self._display_width = self.width = width
|
||||
self._display_height = self.height = height
|
||||
self.xstart = 0
|
||||
self.ystart = 0
|
||||
self.spi = spi
|
||||
self.reset = reset
|
||||
self.dc = dc
|
||||
self.cs = cs
|
||||
self.backlight = backlight
|
||||
self._rotation = rotation % 4
|
||||
|
||||
self.hard_reset()
|
||||
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)
|
||||
if backlight is not None:
|
||||
backlight.value(1)
|
||||
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 hard_reset(self):
|
||||
"""
|
||||
Hard reset display.
|
||||
"""
|
||||
if self.cs:
|
||||
self.cs.off()
|
||||
if self.reset:
|
||||
self.reset.on()
|
||||
time.sleep_ms(50)
|
||||
if self.reset:
|
||||
self.reset.off()
|
||||
time.sleep_ms(50)
|
||||
if self.reset:
|
||||
self.reset.on()
|
||||
time.sleep_ms(150)
|
||||
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.
|
||||
|
||||
Args:
|
||||
value (bool): if True enable sleep mode. if False disable sleep
|
||||
mode
|
||||
"""
|
||||
if value:
|
||||
self._write(ST7789_SLPIN)
|
||||
else:
|
||||
self._write(ST7789_SLPOUT)
|
||||
|
||||
def inversion_mode(self, value):
|
||||
"""
|
||||
Enable or disable display inversion mode.
|
||||
|
||||
Args:
|
||||
value (bool): if True enable inversion mode. if False disable
|
||||
inversion mode
|
||||
"""
|
||||
if value:
|
||||
self._write(ST7789_INVON)
|
||||
else:
|
||||
self._write(ST7789_INVOFF)
|
||||
|
||||
def _set_color_mode(self, mode):
|
||||
"""
|
||||
Set display color mode.
|
||||
|
||||
Args:
|
||||
mode (int): color mode
|
||||
COLOR_MODE_65K, COLOR_MODE_262K, COLOR_MODE_12BIT,
|
||||
COLOR_MODE_16BIT, COLOR_MODE_18BIT, COLOR_MODE_16M
|
||||
"""
|
||||
self._write(ST7789_COLMOD, bytes([mode & 0x77]))
|
||||
|
||||
def rotation(self, rotation):
|
||||
"""
|
||||
Set display rotation.
|
||||
|
||||
Args:
|
||||
rotation (int):
|
||||
- 0-Portrait
|
||||
- 1-Landscape
|
||||
- 2-Inverted Portrait
|
||||
- 3-Inverted Landscape
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
start (int): column start address
|
||||
end (int): column end address
|
||||
"""
|
||||
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.
|
||||
|
||||
Args:
|
||||
start (int): row start address
|
||||
end (int): row end address
|
||||
"""
|
||||
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.
|
||||
|
||||
Args:
|
||||
x0 (int): column start address
|
||||
y0 (int): row start address
|
||||
x1 (int): column end address
|
||||
y1 (int): row end address
|
||||
"""
|
||||
self._set_columns(x0, x1)
|
||||
self._set_rows(y0, y1)
|
||||
self._write(ST7789_RAMWR)
|
||||
|
||||
|
||||
def blit_buffer(self, buffer, x, y, width, height):
|
||||
"""
|
||||
Copy buffer to display at the given location.
|
||||
|
||||
Args:
|
||||
buffer (bytes): Data to copy to display
|
||||
x (int): Top left corner x coordinate
|
||||
Y (int): Top left corner y coordinate
|
||||
width (int): Width
|
||||
height (int): Height
|
||||
"""
|
||||
self._set_window(x, y, x + width - 1, y + height - 1)
|
||||
self._write(None, buffer)
|
||||
Reference in New Issue
Block a user