Update SSD1331 drivers, fix color bug in 16 bit version. Doc improvements.

pull/8/head
Peter Hinch 2020-12-16 08:06:47 +00:00
rodzic c9b8990de8
commit 8c30e2fbec
5 zmienionych plików z 134 dodań i 85 usunięć

Wyświetl plik

@ -61,10 +61,11 @@ i2c = machine.I2C(scl=pscl, sda=psda)
If the SD card is to be used, the official `scdard.py` driver should be
employed. This may be found
[here](https://github.com/micropython/micropython/tree/master/drivers/sdcard).
Note that `sdtest.py` initialises the SPI bus before accessing the SD card.
This is necessary because the display drivers use a high baudrate unsupported
by SD cards. Ensure applications do this before the first SD card access and
before subsequent ones if the display has been refreshed.
It is necessary to initialise the SPI bus before accessing the SD card. This is
because the display drivers use a high baudrate unsupported by SD cards. Ensure
applications do this before the first SD card access and before subsequent ones
if the display has been refreshed. See
[sdtest.py](https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdtest.py).
# Notes on OLED displays
@ -97,4 +98,4 @@ figures. I have not seen figures for the Adafruit displays.
Options are to blank the display when not required, or to design screens where
the elements are occasionally moved slightly to preserve individual pixels.
###### [Main README](../README.md)
###### [Main README](./README.md)

Wyświetl plik

@ -42,7 +42,7 @@ from drivers.ssd1351.ssd1351 import SSD1351 as SSD # Choose device driver
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)
spi = machine.SPI(2, baudrate=10_000_000) # baudrate depends on display chip
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst, 96) # Create a display instance
```
@ -109,6 +109,10 @@ For further information see the GUI README
This driver was tested on Adafruit 1.5 and 1.27 inch displays.
The `color_setup.py` file should initialise the SPI bus with a baudrate of
20_000_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster.
#### SSD1351 Constructor args:
* `spi` An SPI bus instance.
* `pincs` An initialised output pin. Initial value should be 1.
@ -143,7 +147,9 @@ s[x + 1] 2nd byte sent g4 g3 g2 r7 r6 r5 r4 r3
# 3. Drivers for SSD1331
See [Adafruit 0.96" OLED display](https://www.adafruit.com/product/684).
See [Adafruit 0.96" OLED display](https://www.adafruit.com/product/684). Most
of the demos assume a larger screen and will fail. The `color96.py` demo is
written for this display.
There are two versions. Both are cross-platform.
* `ssd1331.py` Uses 8 bit rrrgggbb color.
@ -151,10 +157,15 @@ There are two versions. Both are cross-platform.
The `ssd1331_16bit` version requires 12KiB of RAM for the frame buffer, while
the standard version needs only 6KiB. For the GUI the standard version works
well because text and controls are normally drawn with saturated colors.
well because text and controls are normally drawn with a limited range of
colors, most of which are saturated.
The 16 bit version provides greatly improved results when rendering images.
The `color_setup.py` file should initialise the SPI bus with a baudrate of
6_666_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster.
#### SSD1331 Constructor args:
* `spi` An SPI bus instance.
* `pincs` An initialised output pin. Initial value should be 1.
@ -162,9 +173,17 @@ The 16 bit version provides greatly improved results when rendering images.
* `pinrs` An initialised output pin. Initial value should be 1.
* `height=64` Display dimensions in pixels.
* `width=96`
This driver initialises the SPI clock rate and polarity as required by the
device. The device can support clock rates of upto 6.66MHz.
* `init_spi=False` This optional arg enables flexible options in configuring
the SPI bus. The default assumes exclusive access to the bus with
`color_setup.py` initialising it. Those settings will be left in place. If a
callback function is passed, it will be called prior to each SPI bus write:
this is for shared bus applications. The callback will receive a single arg
being the SPI bus instance. In normal use it will be a one-liner or lambda
initialising the bus. A minimal example is this function:
```python
def spi_init(spi):
spi.init(baudrate=6_666_000) # Data sheet: max is 150ns
```
# 4. Drivers for ST7735R
@ -182,6 +201,10 @@ the supported Adafruit displays differ in their initialisation settings.
If your Chinese display doesn't work with my drivers you are on your own: I
can't support hardware I don't possess.
The `color_setup.py` file should initialise the SPI bus with a baudrate of
12_000_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster.
#### ST7735R Constructor args:
* `spi` An initialised SPI bus instance. The device can support clock rates of
upto 15MHz.
@ -209,6 +232,10 @@ def spi_init(spi):
Adafruit make several displays using this chip, for example
[this 3.2 inch unit](https://www.adafruit.com/product/1743).
The `color_setup.py` file should initialise the SPI bus with a baudrate of
10_000_000. Args `polarity`, `phase`, `bits`, `firstbit` are defaults. Hard or
soft SPI may be used but hard may be faster. See note on overclocking below.
#### ILI9341 Constructor args:
* `spi` An initialised SPI bus instance. The device can support clock rates of
upto 10MHz.
@ -255,10 +282,27 @@ An application using this should call `refresh(ssd, True)` once at the start,
then launch the `do_refresh` method. After that, no calls to `refresh` should
be made. See `gui/demos/scale_ili.py`.
Another option to reduce blocking is overclocking the SPI bus.
#### Overclocking SPI
The ILI9341 datasheet section 19.3.4 specifies a minimum clock cycle time of
100ns for write cycles. It seems that every man and his dog overclocks this,
even the normally conservative Adafruit
[use 24MHz](https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/python-usage)
and [rdagger](https://github.com/rdagger/micropython-ili9341/blob/master/demo_fonts.py)
uses 40MHz. I have successfully run my display at 40MHz. My engineering
training makes me baulk at exceeding datasheet limits but the choice is yours.
###### [Contents](./DRIVERS.md#contents)
# 6. Drivers for sharp displays
These displays have characteristics which mean that they are best suited to
micropower applications. Inevitably this means that deployment is more involved
than the other supported units. This doc provides some background information
on their use.
These monochrome SPI displays exist in three variants from Adafruit.
1. [2.7 inch 400x240 pixels](https://www.adafruit.com/product/4694)
2. [1.3 inch 144x168](https://www.adafruit.com/product/3502)

Wyświetl plik

@ -0,0 +1,36 @@
# color_setup.py Customise for your hardware config
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# As written, supports:
# Adafruit 1.5" 128*128 OLED display: https://www.adafruit.com/product/1431
# Adafruit 1.27" 128*96 display https://www.adafruit.com/product/1673
# Edit the driver import for other displays.
# 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.
# SSD1331 drivers are cross-platform.
# WIRING (Adafruit pin nos and names with Pyboard pins).
# Pyb SSD
# 3v3 Vin (10)
# Gnd Gnd (11)
# X1 DC (3 DC)
# X2 CS (5 OC OLEDCS)
# X3 Rst (4 R RESET)
# X6 CLK (2 CL SCK)
# X8 DATA (1 SI MOSI)
import machine
import gc
from drivers.ssd1331.ssd1331 import SSD1331 as SSD
pdc = machine.Pin('X1', machine.Pin.OUT_PP, value=0)
pcs = machine.Pin('X2', machine.Pin.OUT_PP, value=1)
prst = machine.Pin('X3', machine.Pin.OUT_PP, value=1)
spi = machine.SPI(1, baudrate=6_666_000)
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst) # Create a display instance

Wyświetl plik

@ -3,25 +3,8 @@
# The MIT License (MIT)
# Copyright (c) 2018 Peter Hinch
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Copyright (c) Peter Hinch 2018-2020
# Released under the MIT license see LICENSE
# Show command
# 0x15, 0, 0x5f, 0x75, 0, 0x3f Col 0-95 row 0-63
@ -52,8 +35,9 @@ import gc
import sys
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
# Keep 0,0 on STM as testing was done in that mode.
_bs = 0 if sys.platform == 'esp32' else 1 # SPI bus state
# Mode 0, 0 works on ESP and STM
# Data sheet SPI spec: 150ns min clock period 6.66MHz
class SSD1331(framebuf.FrameBuffer):
# Convert r, g, b in range 0-255 to an 8 bit colour value
@ -62,18 +46,17 @@ class SSD1331(framebuf.FrameBuffer):
def rgb(r, g, b):
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
def __init__(self, spi, pincs, pindc, pinrs, height=64, width=96):
self.spi = spi
self.rate = 6660000 # Data sheet: 150ns min clock period
self.pincs = pincs
self.pindc = pindc # 1 = data 0 = cmd
def __init__(self, spi, pincs, pindc, pinrs, height=64, width=96, init_spi=False):
self._spi = spi
self._pincs = pincs
self._pindc = pindc # 1 = data 0 = cmd
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.
self._spi_init = init_spi
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
gc.collect()
self.buffer = bytearray(self.height * self.width)
super().__init__(self.buffer, self.width, self.height, self.mode)
super().__init__(self.buffer, self.width, self.height, mode)
pinrs(0) # Pulse the reset line
utime.sleep_ms(1)
pinrs(1)
@ -85,13 +68,14 @@ class SSD1331(framebuf.FrameBuffer):
self.show()
def _write(self, buf, dc):
self.spi.init(baudrate=self.rate, polarity=_bs, phase=_bs)
self.pincs(1)
self.pindc(dc)
self.pincs(0)
self.spi.write(buf)
self.pincs(1)
self._pincs(1)
self._pindc(dc)
self._pincs(0)
self._spi.write(buf)
self._pincs(1)
def show(self, _cmd=b'\x15\x00\x5f\x75\x00\x3f'): # Pre-allocate
if self._spi_init: # A callback was passed
self._spi_init(spi) # Bus may be shared
self._write(_cmd, 0)
self._write(self.buffer, 1)

Wyświetl plik

@ -1,27 +1,8 @@
# SSD1331.py MicroPython driver for Adafruit 0.96" OLED display
# https://www.adafruit.com/product/684
# The MIT License (MIT)
# Copyright (c) 2019 Peter Hinch
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Copyright (c) Peter Hinch 2019-2020
# Released under the MIT license see LICENSE
# Show command
# 0x15, 0, 0x5f, 0x75, 0, 0x3f Col 0-95 row 0-63
@ -49,35 +30,37 @@
import framebuf
import utime
import gc
import sys
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
# Keep 0,0 on STM as testing was done in that mode.
_bs = 0 if sys.platform == 'esp32' else 1 # SPI bus state
# Mode 0, 0 works on ESP and STM
# Data sheet SPI spec: 150ns min clock period 6.66MHz
class SSD1331(framebuf.FrameBuffer):
# Convert r, g, b in range 0-255 to a 16 bit colour value RGB565
# acceptable to hardware: rrrrrggggggbbbbb
# LS byte of 16 bit result is shifted out 1st
@staticmethod
def rgb(r, g, b):
return ((r & 0xf8) << 5) | ((g & 0x1c) << 11) | (b & 0xf8) | ((g & 0xe0) >> 5)
return ((b & 0xf8) << 5) | ((g & 0x1c) << 11) | (r & 0xf8) | ((g & 0xe0) >> 5)
def __init__(self, spi, pincs, pindc, pinrs, height=64, width=96):
self.spi = spi
self.rate = 6660000 # Data sheet: 150ns min clock period
self.pincs = pincs
self.pindc = pindc # 1 = data 0 = cmd
def __init__(self, spi, pincs, pindc, pinrs, height=64, width=96, init_spi=False):
self._spi = spi
self._pincs = pincs
self._pindc = pindc # 1 = data 0 = cmd
self.height = height # Required by Writer class
self.width = width
# Save color mode for use by writer_gui (blit)
self.mode = framebuf.RGB565
self._spi_init = init_spi
mode = framebuf.RGB565
gc.collect()
self.buffer = bytearray(self.height * self.width * 2)
super().__init__(self.buffer, self.width, self.height, self.mode)
super().__init__(self.buffer, self.width, self.height, mode)
pinrs(0) # Pulse the reset line
utime.sleep_ms(1)
pinrs(1)
utime.sleep_ms(1)
if self._spi_init: # A callback was passed
self._spi_init(spi) # Bus may be shared
self._write(b'\xae\xa0\x72\xa1\x00\xa2\x00\xa4\xa8\x3f\xad\x8e\xb0'\
b'\x0b\xb1\x31\xb3\xf0\x8a\x64\x8b\x78\x8c\x64\xbb\x3a\xbe\x3e\x87'\
b'\x06\x81\x91\x82\x50\x83\x7d\xaf', 0)
@ -85,13 +68,14 @@ class SSD1331(framebuf.FrameBuffer):
self.show()
def _write(self, buf, dc):
self.spi.init(baudrate=self.rate, polarity=_bs, phase=_bs)
self.pincs(1)
self.pindc(dc)
self.pincs(0)
self.spi.write(buf)
self.pincs(1)
self._pincs(1)
self._pindc(dc)
self._pincs(0)
self._spi.write(buf)
self._pincs(1)
def show(self, _cmd=b'\x15\x00\x5f\x75\x00\x3f'): # Pre-allocate
if self._spi_init: # A callback was passed
self._spi_init(spi) # Bus may be shared
self._write(_cmd, 0)
self._write(self.buffer, 1)