kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
st7735r driver works on 160 wide display only.
rodzic
33c91aa3d8
commit
62ff95dbd3
19
README.md
19
README.md
|
@ -53,7 +53,8 @@ wiring details, pin names and hardware issues.
|
|||
3.7 [Class Textbox](./README.md#37-class-textbox) Scrolling text display.
|
||||
4. [Device drivers](./README.md#4-device-drivers) Device driver compatibility
|
||||
requirements (these are minimal).
|
||||
5. [ESP8266](./README.md#5-esp8266) This can work.
|
||||
5. [ESP8266](./README.md#5-esp8266) This can work. Contains information on
|
||||
minimising the RAM and flash footprints of the GUI.
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
|
@ -159,6 +160,10 @@ Installation comprises copying the `gui` and `drivers` directories, with their
|
|||
contents, plus a hardware configuration file, to the target. The directory
|
||||
structure on the target must match that in the repo.
|
||||
|
||||
In the interests of conserving RAM, supplied drivers support only the
|
||||
functionality required by the GUI. More fully featured drivers may better suit
|
||||
other applications. See [section 4](./README.md#4-device-drivers).
|
||||
|
||||
Filesystem space may be conserved by copying only the required driver from
|
||||
`drivers`, but the directory path to that file must be retained. For example,
|
||||
for SSD1351 displays only the following are actually required:
|
||||
|
@ -785,8 +790,14 @@ the oldest (topmost) being discarded as required.
|
|||
|
||||
# 4. Device drivers
|
||||
|
||||
Device drivers capable of supporting `nanogui` can be extremely simple: see the
|
||||
`drivers/sharp/sharp.py` for a minimal example.
|
||||
Device drivers capable of supporting `nanogui` can be extremely simple: see
|
||||
`drivers/sharp/sharp.py` for a minimal example. It should be noted that the
|
||||
supplied device drivers are designed purely to support nanogui. To conserve RAM
|
||||
they provide for the transfer of an external frame buffer to the device and
|
||||
little else. Such a transfer typically takes a few tens of milliseconds. Many
|
||||
driver chips support graphics primitives in hardware. In performance orientated
|
||||
applications such as games, drivers using these capabilities will be faster
|
||||
than those provided here.
|
||||
|
||||
For a driver to support `nanogui` it must be subclassed from
|
||||
`framebuf.FrameBuffer` and provide `height` and `width` bound variables being
|
||||
|
@ -800,7 +811,7 @@ hardware.
|
|||
For color drivers, to conserve RAM it is suggested that 8-bit color is used
|
||||
for the `framebuf`. If the hardware does not support this, conversion to the
|
||||
supported color space needs to be done "on the fly" as per the SSD1351 driver.
|
||||
Since this is likely to be slow, consider using native, viper or assembler.
|
||||
To maximise update speed consider using native, viper or assembler.
|
||||
|
||||
Color drivers should have a static method converting rgb(255, 255, 255) to a
|
||||
form acceptable to the driver. For 8-bit rrrgggbb this can be:
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
# st7735r.py Driver for ST7735R LCD displays for nano-gui
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2018-2020 Peter Hinch
|
||||
|
||||
# Supported displays
|
||||
# Adfruit 1.8' Color TFT LCD display with MicroSD Card Breakout:
|
||||
# https://www.adafruit.com/product/358
|
||||
# Adafruit 1.44' Color TFT LCD Display with MicroSD Card breakout:
|
||||
# https://www.adafruit.com/product/2088
|
||||
|
||||
# Based on
|
||||
# https://github.com/adafruit/Adafruit_CircuitPython_ST7735R/blob/master/adafruit_st7735r.py
|
||||
# https://github.com/GuyCarver/MicroPython/blob/master/lib/ST7735.py
|
||||
# https://github.com/boochow/MicroPython-ST7735
|
||||
|
||||
from time import sleep_ms
|
||||
import framebuf
|
||||
import gc
|
||||
import micropython
|
||||
|
||||
# Datasheet para 8.4 scl write cycle 66ns == 15MHz
|
||||
|
||||
|
||||
# _lcopy: copy a line in 8 bit format to one in 12 bit RGB444. para 9.8.20.
|
||||
# 2 bytes become 3 in destination. Source format:
|
||||
# < D7 D6 D5 D4 D3 D2 D1 D0>
|
||||
# <R02 R01 R00 G02 G01 G00 B01 B00> <R12 R11 R10 G12 G11 G10 B11 B10>
|
||||
# dest:
|
||||
# <R02 R01 R00 0 G02 G01 G00 0> <B01 B00 0 0 R12 R11 R10 0> <G12 G11 G10 0 B11 B10 0 0>
|
||||
|
||||
@micropython.viper
|
||||
def _lcopy(dest:ptr8, source:ptr8, length:int):
|
||||
n = 0
|
||||
for x in range(0, length, 2):
|
||||
c = source[x]
|
||||
d = source[x + 1]
|
||||
dest[n] = (c & 0xe0) | ((c & 0x1c) >> 1) # R0 G0
|
||||
n += 1
|
||||
dest[n] = ((c & 3) << 6) | ((d & 0xe0) >> 4) # B0 R1
|
||||
n += 1
|
||||
dest[n] = ((d & 0x1c) << 3) | ((d & 3) << 2) # G1 B1
|
||||
n += 1
|
||||
|
||||
class ST7735R(framebuf.FrameBuffer):
|
||||
# Convert r, g, b in range 0-255 to an 8 bit colour value
|
||||
# rrrgggbb. Converted to 12 bit on the fly.
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
|
||||
|
||||
# rst and cs are active low, SPI is mode 0
|
||||
def __init__(self, spi, cs, dc, rst, height=128, width=128):
|
||||
self._spi = spi
|
||||
self._rst = rst # Pins
|
||||
self._dc = dc
|
||||
self._cs = cs
|
||||
self.height = height # Required by Writer class
|
||||
self.width = width
|
||||
# Save color mode for use by writer_gui (blit)
|
||||
self.mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
|
||||
gc.collect()
|
||||
self.buffer = bytearray(self.height * self.width)
|
||||
self._mvb = memoryview(self.buffer)
|
||||
super().__init__(self.buffer, self.width, self.height, self.mode)
|
||||
self._linebuf = bytearray(int(self.width * 3 // 2)) # 12 bit color out
|
||||
self._init()
|
||||
self.show()
|
||||
|
||||
# Hardware reset
|
||||
def _hwreset(self):
|
||||
self._dc(0)
|
||||
self._rst(1)
|
||||
sleep_ms(1)
|
||||
self._rst(0)
|
||||
sleep_ms(1)
|
||||
self._rst(1)
|
||||
sleep_ms(1)
|
||||
|
||||
# Write a command, a bytes instance (in practice 1 byte).
|
||||
def _wcmd(self, buf):
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(buf)
|
||||
self._cs(1)
|
||||
|
||||
# Write a command followed by a data arg.
|
||||
def _wcd(self, c, d):
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(c)
|
||||
self._cs(1)
|
||||
self._dc(1)
|
||||
self._cs(0)
|
||||
self._spi.write(d)
|
||||
self._cs(1)
|
||||
|
||||
# Initialise the hardware. Blocks 500ms.
|
||||
def _init(self):
|
||||
self._hwreset() # Hardware reset. Blocks 3ms
|
||||
cmd = self._wcmd
|
||||
wcd = self._wcd
|
||||
cmd(b'\x01') # SW reset datasheet specifies > 120ms
|
||||
sleep_ms(150)
|
||||
cmd(b'\x11') # SLPOUT
|
||||
sleep_ms(256) # Adafruit delay (datsheet 120ms)
|
||||
wcd(b'\xb1', b'\x01\x2C\x2D') # FRMCTRL1
|
||||
wcd(b'\xb2', b'\x01\x2C\x2D') # FRMCTRL2
|
||||
wcd(b'\xb3', b'\x01\x2C\x2D\x01\x2C\x2D') # FRMCTRL3
|
||||
wcd(b'\xb4', b'\x07') # INVCTR line inversion
|
||||
|
||||
wcd(b'\xc0', b'\xa2\x02\x84') # PWCTR1 GVDD = 4.7V, 1.0uA
|
||||
wcd(b'\xc1', b'\xc5') # PWCTR2 VGH=14.7V, VGL=-7.35V
|
||||
wcd(b'\xc2', b'\x0a\x00') # PWCTR3 Opamp current small, Boost frequency
|
||||
wcd(b'\xc3', b'\x8a\x2a') # PWCTR4
|
||||
wcd(b'\xc4', b'\x8a\xee') # PWCTR5
|
||||
wcd(b'\xc5', b'\x0e') # VMCTR1 VCOMH = 4V, VOML = -1.1V NOTE I make VCOM == -0.775V
|
||||
|
||||
cmd(b'\x20') # INVOFF
|
||||
# d7..d5 of MADCTL determine rotation/orientation
|
||||
wcd(b'\x36', b'\x20') # MADCTL: RGB landscape mode
|
||||
wcd(b'\x3a', b'\x03') # COLMOD 12 bit
|
||||
wcd(b'\xe0', b'\x02\x1c\x07\x12\x37\x32\x29\x2d\x29\x25\x2B\x39\x00\x01\x03\x10') # GMCTRP1 Gamma
|
||||
wcd(b'\xe1', b'\x03\x1d\x07\x06\x2E\x2C\x29\x2D\x2E\x2E\x37\x3F\x00\x00\x02\x10') # GMCTRN1
|
||||
|
||||
wcd(b'\x2a', int.to_bytes(self.width, 4, 'big')) # CASET column address 0 start, 160/128 end
|
||||
wcd(b'\x2b', int.to_bytes(self.height, 4, 'big')) # RASET
|
||||
|
||||
cmd(b'\x13') # NORON
|
||||
sleep_ms(10)
|
||||
cmd(b'\x29') # DISPON
|
||||
sleep_ms(100)
|
||||
|
||||
def show(self): # Blocks 36ms on Pyboard D at stock frequency (160*128)
|
||||
wd = self.width
|
||||
ht = self.height
|
||||
lb = self._linebuf
|
||||
buf = self._mvb
|
||||
self._dc(0)
|
||||
self._cs(0)
|
||||
self._spi.write(b'\x2c') # RAMWR
|
||||
self._dc(1)
|
||||
for start in range(wd * (ht - 1), -1, - wd): # For each line
|
||||
_lcopy(lb, buf[start :], wd) # Copy and map colors (68us)
|
||||
self._spi.write(lb)
|
||||
self._cs(1)
|
|
@ -0,0 +1,39 @@
|
|||
# st7735r_setup.py Customise for your hardware config
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# As written, supports:
|
||||
# Adfruit 1.8' Color TFT LCD display with MicroSD Card Breakout:
|
||||
# https://www.adafruit.com/product/358
|
||||
# Adafruit 1.44' Color TFT LCD Display with MicroSD Card breakout:
|
||||
# https://www.adafruit.com/product/2088
|
||||
|
||||
# Demo of initialisation procedure designed to minimise risk of memory fail
|
||||
# when instantiating the frame buffer. The aim is to do this as early as
|
||||
# possible before importing other modules.
|
||||
|
||||
# WIRING (Adafruit pin nos and names).
|
||||
# Pyb SSD
|
||||
# 3v3 Vin (10)
|
||||
# Gnd Gnd (11)
|
||||
# Y1 DC (3 DC)
|
||||
# Y2 CS (5 OC OLEDCS)
|
||||
# Y3 Rst (4 R RESET)
|
||||
# Y6 CLK (2 CL SCK)
|
||||
# Y8 DATA (1 SI MOSI)
|
||||
|
||||
import machine
|
||||
import gc
|
||||
|
||||
from drivers.st7735r.st7735r import ST7735R as SSD
|
||||
|
||||
height = 128
|
||||
width = 128
|
||||
|
||||
pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
|
||||
pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
|
||||
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||
spi = machine.SPI(2, baudrate=12_000_000)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height, width) # Create a display instance
|
Ładowanie…
Reference in New Issue