From 62ff95dbd3e22ee46e3044b99dcff8576054446b Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Wed, 25 Nov 2020 14:25:25 +0000 Subject: [PATCH] st7735r driver works on 160 wide display only. --- README.md | 19 ++++- drivers/st7735r/st7735r.py | 146 +++++++++++++++++++++++++++++++++++++ st7735r_setup.py | 39 ++++++++++ 3 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 drivers/st7735r/st7735r.py create mode 100644 st7735r_setup.py diff --git a/README.md b/README.md index a037b3d..311428e 100644 --- a/README.md +++ b/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: diff --git a/drivers/st7735r/st7735r.py b/drivers/st7735r/st7735r.py new file mode 100644 index 0000000..4e6b204 --- /dev/null +++ b/drivers/st7735r/st7735r.py @@ -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> +# +# dest: +# + +@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) diff --git a/st7735r_setup.py b/st7735r_setup.py new file mode 100644 index 0000000..57e8327 --- /dev/null +++ b/st7735r_setup.py @@ -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