kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
ST7735 replace offset arg with display.
rodzic
2849c49499
commit
67d423b760
64
DRIVERS.md
64
DRIVERS.md
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Ładowanie…
Reference in New Issue