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 handling](./DRIVERS.md#11-color-handling) for the implications of 4-bit
color. color.
[Tested display](https://www.adafruit.com/product/4313). The Adafruit [Tested display: Adafruit 1.3 inch](https://www.adafruit.com/product/4313). The
[1.54 inch](https://www.adafruit.com/product/3787) has identical resolution and Adafruit [1.54 inch](https://www.adafruit.com/product/3787) has identical
uses the same CircuitPython driver so can be expected to work. 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 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 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 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`. 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: #### ST7789 Constructor args:
* `spi` An initialised SPI bus instance. The chip supports clock rates of upto * `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 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 `height` and `width` values: this ensures that `nano-gui` gets the correct
aspect ratio. aspect ratio.
* `width=240` * `width=240`
* `disp_mode=0` By default the driver operates in landscape mode. This arg * `disp_mode=LANDSCAPE` This arg enables portrait mode and other
enables portrait mode and other configurations. See below. configurations. See below.
* `init_spi=False` For shared SPI bus applications. See note 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 * `display=GENERIC` The `display` arg is an opaque type defining the display
hardware's coordinate system is offset relative to the chip's RAM origin and hardware. Current options (exported by the driver) are `GENERIC` for Adafruit
the case where the display hardware is configured in portrait mode. Elements displays and `TDISPLAY` for the TTGO board.
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 #### Constants exported by the driver
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 The `color_setup.py` file should invoke the driver as follows:
uses the `(0, 0, 0)` default, TTGO uses `(52, 40, 1)`. ```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 ### init_spi
@ -432,20 +446,18 @@ def spi_init(spi):
#### Display mode #### Display mode
This is provided mainly to support asymmetrical displays. It also enables the 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 The following example `color_setup.py` is for Pi Pico and produces an upside
down portrait display. down portrait display.
```python ```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 pdc = Pin(13, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(14, Pin.OUT, value=1) pcs = Pin(14, Pin.OUT, value=1)

Wyświetl plik

@ -3,11 +3,6 @@
# Released under the MIT License (MIT). See LICENSE. # Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2021 Peter Hinch, Ihor Nehrutsa # 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: # Supports:
# TTGO T-Display 1.14" 135*240(Pixel) based on ST7789V # TTGO T-Display 1.14" 135*240(Pixel) based on ST7789V
# http://www.lilygo.cn/claprod_view.aspx?TypeId=62&Id=1274 # 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 from machine import Pin, SPI, ADC
import gc 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 pdc = Pin(TFT_DC, Pin.OUT, value=0) # Arbitrary pins
pcs = Pin(TFT_CS, Pin.OUT, value=1) 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. # Conservative low baudrate. Can go to 62.5MHz.
spi = SPI(1, 30_000_000, sck=Pin(TFT_SCLK), mosi=Pin(TFT_MOSI)) 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 # 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 # 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 # optional
# b1 = Pin(BUTTON1, Pin.IN) # b1 = Pin(BUTTON1, Pin.IN)

Wyświetl plik

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