ST7735 replace offset arg with display.

pull/10/head
Peter Hinch 2021-04-26 07:53:24 +01:00
rodzic 2849c49499
commit 67d423b760
4 zmienionych plików z 84 dodań i 67 usunięć

Wyświetl plik

@ -380,18 +380,19 @@ colors. The resultant buffer size for the Adafruit displays is 28800 bytes. See
[Color handling](./DRIVERS.md#11-color-handling) for the implications of 4-bit
color.
[Tested display](https://www.adafruit.com/product/4313). The Adafruit
[1.54 inch](https://www.adafruit.com/product/3787) has identical resolution and
uses the same CircuitPython driver so can be expected to work.
[Tested display: Adafruit 1.3 inch](https://www.adafruit.com/product/4313). The
Adafruit [1.54 inch](https://www.adafruit.com/product/3787) has identical
resolution and uses the same CircuitPython driver so can be expected to work.
The driver also supports the
[TTGO T-Display](http://www.lilygo.cn/claprod_view.aspx?TypeId=62&Id=1274).
This is an inexpensive ESP32 with a 135x240 color TFT display.
The `color_setup.py` file should initialise the SPI bus with a baudrate of
30_000_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster. 30MHz is a conservative value: see
below. An example file for the Pi Pico is in `color_setup/ssd7789.py`.
Note to existing users: the `disp_mode` args now behave as expected. This may
mean changing the constructor arg in your `color_setup.py`.
#### ST7789 Constructor args:
* `spi` An initialised SPI bus instance. The chip supports clock rates of upto
62.5MHz (datasheet table 6). I have tested 60MHz. High speeds are sensitive to
@ -403,17 +404,30 @@ mean changing the constructor arg in your `color_setup.py`.
`height` and `width` values: this ensures that `nano-gui` gets the correct
aspect ratio.
* `width=240`
* `disp_mode=0` By default the driver operates in landscape mode. This arg
enables portrait mode and other configurations. See below.
* `disp_mode=LANDSCAPE` This arg enables portrait mode and other
configurations. See below.
* `init_spi=False` For shared SPI bus applications. See note below.
* `offset=(0, 0, 0)` This is intended for display hardware where the display
hardware's coordinate system is offset relative to the chip's RAM origin and
the case where the display hardware is configured in portrait mode. Elements
are `(x, y, p)` where `x` and `y` (being positive integers) represent the RAM
offset. `p==1` indicates display hardware which is in portrait mode. This
currently only applies to the TTGO T-Display. In practice, when other display
hardware is supported, this doc will specify the values to be used. Adafruit
uses the `(0, 0, 0)` default, TTGO uses `(52, 40, 1)`.
* `display=GENERIC` The `display` arg is an opaque type defining the display
hardware. Current options (exported by the driver) are `GENERIC` for Adafruit
displays and `TDISPLAY` for the TTGO board.
#### Constants exported by the driver
The `color_setup.py` file should invoke the driver as follows:
```python
from drivers.st7789.st7789_4bit import *
SSD = ST7789
```
The following constants are available:
Orientation (values for `disp_mode`):
`LANDSCAPE` Normal display, text is parallel to long axis.
`PORTRAIT` Text is parallel to short axis.
`USD` Upside down rendering.
`REFLECT` Mirror image rendering.
Display types (values for `display`):
`GENERIC` For Adafruit displays.
`TDISPLAY` For the TTGO T-Display.
### init_spi
@ -432,20 +446,18 @@ def spi_init(spi):
#### Display mode
This is provided mainly to support asymmetrical displays. It also enables the
Adafruit display image to be rotated.
Adafruit display image to be rotated. Any of the orientation constants listed
above may be applied, and multiple options may be combined using the bitwise-or
`|` operator.
When choosing `LANDSCAPE` or `PORTRAIT` mode it is essential that `height` and
`width` constructor args match the mode.
The driver exports the following constants:
```python
LANDSCAPE = 0 # Normal display
PORTRAIT = 0x20 # Rotate 90°
REFLECT = 0x40 # Swap pixels left-right
USD = 0x80 # Upside down: swap pixels top-bottom
```
For non-standard modes these may be combined using the bitwise-or `|` operator.
The following example `color_setup.py` is for Pi Pico and produces an upside
down portrait display.
```python
from drivers.st7789.st7789_4bit import ST7789 as SSD, PORTRAIT, USD, REFLECT, LANDSCAPE
from drivers.st7789.st7789_4bit import *
SSD = ST7789
pdc = Pin(13, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(14, Pin.OUT, value=1)

Wyświetl plik

@ -3,11 +3,6 @@
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa
# Notes: Peter Hinch April 2021
# UNDER DEVELOPMENT. This file and the ST7789 driver may change.
# These settings produce a landscape mode display with top left
# adjacent to pin 36.
# Supports:
# TTGO T-Display 1.14" 135*240(Pixel) based on ST7789V
# http://www.lilygo.cn/claprod_view.aspx?TypeId=62&Id=1274
@ -46,7 +41,8 @@ BUTTON2 = 0 # left of the USB connector
from machine import Pin, SPI, ADC
import gc
from drivers.st7789.st7789_4bit import ST7789 as SSD, PORTRAIT, USD, REFLECT, LANDSCAPE
from drivers.st7789.st7789_4bit import *
SSD = ST7789
pdc = Pin(TFT_DC, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(TFT_CS, Pin.OUT, value=1)
@ -57,12 +53,10 @@ gc.collect() # Precaution before instantiating framebuf
# Conservative low baudrate. Can go to 62.5MHz.
spi = SPI(1, 30_000_000, sck=Pin(TFT_SCLK), mosi=Pin(TFT_MOSI))
# Display config. Tweak for TTGO.
OFFSET = (52, 40, 1)
# Right way up landscape: defined as top left adjacent to pin 36
ssd = SSD(spi, height=135, width=240, dc=pdc, cs=pcs, rst=prst, disp_mode=LANDSCAPE, offset=OFFSET)
ssd = SSD(spi, height=135, width=240, dc=pdc, cs=pcs, rst=prst, disp_mode=LANDSCAPE, display=TDISPLAY)
# Normal portrait display: consistent with TTGO logo at top
# ssd = SSD(spi, height=240, width=135, dc=pdc, cs=pcs, rst=prst, disp_mode=PORTRAIT, offset=OFFSET)
# ssd = SSD(spi, height=240, width=135, dc=pdc, cs=pcs, rst=prst, disp_mode=PORTRAIT, display=TDISPLAY)
# optional
# b1 = Pin(BUTTON1, Pin.IN)

Wyświetl plik

@ -26,7 +26,8 @@
from machine import Pin, SPI
import gc
from drivers.st7789.st7789_4bit import ST7789 as SSD, PORTRAIT, USD, REFLECT, LANDSCAPE
from drivers.st7789.st7789_4bit import *
SSD = ST7789
pdc = Pin(13, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(14, Pin.OUT, value=1)

Wyświetl plik

@ -7,6 +7,7 @@
# Adafruit 1.3" 240x240 Wide Angle TFT LCD Display with MicroSD - ST7789
# https://www.adafruit.com/product/4313
# TTGO T-Display
# Based on
# Adfruit https://github.com/adafruit/Adafruit_CircuitPython_ST7789/blob/master/adafruit_st7789.py
# Also see st7735r_4bit.py for other source acknowledgements
@ -20,16 +21,14 @@ import gc
import micropython
import uasyncio as asyncio
# d7..d5 of MADCTL determine rotation/orientation datasheet P124, P231
# d5 = MV row/col exchange
# d6 = MX col addr order
# d7 = MY page addr order
# These constants are also used as user specifiers for orientation. However
# mapping is required between user value and that presented to hardware.
# User orientation constants
LANDSCAPE = 0 # Default
PORTRAIT = 0x20
REFLECT = 0x40
USD = 0x80
REFLECT = 1
USD = 2
PORTRAIT = 4
# Display types
GENERIC = (0, 0, 0)
TDISPLAY = (52, 40, 1)
@micropython.viper
def _lcopy(dest:ptr8, source:ptr8, lut:ptr8, length:int):
@ -61,24 +60,28 @@ class ST7789(framebuf.FrameBuffer):
# rst and cs are active low, SPI is mode 0
def __init__(self, spi, cs, dc, rst, height=240, width=240,
disp_mode=0, init_spi=False, offset=(0, 0, 0)):
disp_mode=LANDSCAPE, init_spi=False, display=GENERIC):
if not 0 <= disp_mode <= 7:
raise ValueError('Invalid display mode:', disp_mode)
if not display in (GENERIC, TDISPLAY):
raise ValueError('Invalid display type.')
self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns)
self._rst = rst # Pins
self._dc = dc
self._cs = cs
self.height = height # Required by Writer class
self.width = width
self._offset = offset
self._offset = display[:2] # display arg is (x, y, orientation)
orientation = display[2] # where x, y is the RAM offset
self._spi_init = init_spi # Possible user callback
self._lock = asyncio.Lock()
mode = framebuf.GS4_HMSB # Use 4bit greyscale.
gc.collect()
#buf = bytearray(height * width // 2)
buf = bytearray(height * -(-width // 2)) # Ceiling division for odd widths
self._mvb = memoryview(buf)
super().__init__(buf, width, height, mode)
self._linebuf = bytearray(self.width * 2) # 16 bit color out
self._init(disp_mode, offset[2])
self._init(disp_mode, orientation)
self.show()
# Hardware reset
@ -112,7 +115,7 @@ class ST7789(framebuf.FrameBuffer):
# Initialise the hardware. Blocks 163ms. Adafruit have various sleep delays
# where I can find no requirement in the datasheet. I removed them with
# other redundant code.
def _init(self, disp_mode, orientation):
def _init(self, user_mode, orientation):
self._hwreset() # Hardware reset. Blocks 3ms
if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared
@ -126,41 +129,48 @@ class ST7789(framebuf.FrameBuffer):
cmd(b'\x20') # INVOFF Adafruit turn inversion on. This driver fixes .rgb
cmd(b'\x13') # NORON Normal display mode
# Display modes correspond to values expected by hardware. Sometimes
# these have to be applied in combination to achieve what the user wants.
# Table entries map user request onto what is required. idx values:
# Table maps user request onto hardware values. index values:
# 0 Normal
# 1 Reflect
# 2 USD
# 3 USD reflect
# Followed by same for LANDSCAPE
if orientation: # Display hardware is portrait mode
disp_mode ^= PORTRAIT
idx = disp_mode >> 6 if disp_mode & PORTRAIT else (disp_mode >> 6) + 4
disp_mode = (0x60, 0xe0, 0xa0, 0x20, 0, 0x40, 0xc0, 0x80)[idx]
if not orientation:
user_mode ^= PORTRAIT
# Hardware mappings
# d7..d5 of MADCTL determine rotation/orientation datasheet P124, P231
# d5 = MV row/col exchange
# d6 = MX col addr order
# d7 = MY page addr order
# LANDSCAPE = 0
# PORTRAIT = 0x20
# REFLECT = 0x40
# USD = 0x80
mode = (0x60, 0xe0, 0xa0, 0x20, 0, 0x40, 0xc0, 0x80)[user_mode]
# Set display window depending on mode, .height and .width.
self.set_window(disp_mode)
wcd(b'\x36', int.to_bytes(disp_mode, 1, 'little'))
self.set_window(mode)
wcd(b'\x36', int.to_bytes(mode, 1, 'little'))
cmd(b'\x29') # DISPON. Adafruit then delay 500ms.
# Define the mapping between RAM and the display.
# Datasheet section 8.12 p124.
def set_window(self, mode):
portrait, reflect, usd = 0x20, 0x40, 0x80
rht = 320
rwd = 240 # RAM ht and width
wht = self.height # Window (framebuf) dimensions.
wwd = self.width # In portrait mode wht > wwd
if mode & PORTRAIT:
if mode & portrait:
xoff = self._offset[1] # x and y transposed
yoff = self._offset[0]
xs = xoff
xe = wwd + xoff - 1
ys = yoff # y start
ye = wht + yoff - 1 # y end
if mode & REFLECT:
if mode & reflect:
ys = rwd - wht - yoff
ye = rwd - yoff - 1
if mode & USD:
if mode & usd:
xs = rht - wwd - xoff
xe = rht - xoff - 1
else: # LANDSCAPE
@ -170,17 +180,17 @@ class ST7789(framebuf.FrameBuffer):
xe = wwd + xoff - 1
ys = yoff # y start
ye = wht + yoff - 1 # y end
if mode & USD:
if mode & usd:
ys = rht - wht - yoff
ye = rht - yoff - 1
if mode & REFLECT:
if mode & reflect:
xs = rwd - wwd - xoff
xe = rwd - xoff - 1
# Col address set.
self._wcd(b'\x2a', int.to_bytes(xs, 2, 'big') + int.to_bytes(xe, 2, 'big'))
self._wcd(b'\x2a', int.to_bytes((xs << 16) + xe, 4, 'big'))
# Row address set
self._wcd(b'\x2b', int.to_bytes(ys, 2, 'big') + int.to_bytes(ye, 2, 'big'))
self._wcd(b'\x2b', int.to_bytes((ys << 16) + ye, 4, 'big'))
#@micropython.native # Made virtually no difference to timing.
def show(self): # Blocks for 83ms @60MHz SPI