PR7682 improvements: drivers, writer.py, docs.

pull/24/head
Peter Hinch 2021-08-29 11:50:03 +01:00
rodzic 31368b751c
commit dbc4f4689b
17 zmienionych plików z 106 dodań i 41 usunięć

Wyświetl plik

@ -1085,6 +1085,22 @@ long as `.rgb()` and the "on the fly" converter match, this is arbitrary.
The `Writer` (monochrome) or `CWriter` (color) classes and the `nanogui` module
should then work automatically.
For color displays the following provides a substantial performance boost when
rendering text.
```python
from drivers.boolpalette import BoolPalette
```
and, in `__init__.py`:
```python
mode = framebuf.GS4_HMSB # The format to be used by the driver
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width // 2) # Computation must match the format
super().__init__(buf, self.width, self.height, mode)
```
Assuming firmware dated after 26th Aug 2021, a fast C blit will be used to
render glyphs instead of Python code.
The following script is useful for testing color display drivers after
configuring `color_setup.py`. It draws squares at the extreme corners of the
display and a corner to corner diagonal.

Wyświetl plik

@ -49,6 +49,7 @@ wiring details, pin names and hardware issues.
1.1 [Change log](./README.md#11-change-log)
1.2 [Description](./README.md#12-description)
1.3 [Quick start](./README.md#13-quick-start)
1.4 [A performance boost](./README.md#14-a-performance-boost)
2. [Files and Dependencies](./README.md#2-files-and-dependencies)
2.1 [Files](./README.md#21-files)
     2.1.1 [Core files](./README.md#211-core-files)
@ -93,8 +94,7 @@ on ESP8266 is possible but frozen bytecode must be used owing to its restricted
RAM.
As of 14th March 2021 it runs on the Raspberry Pi Pico; on that target firmware
must be of that date or later. The `color15` demo fails because the firmware
lacks `uos.urandom()` but hopefully it will be fixed soon.
must be of that date or later.
It uses synchronous code but is compatible with `uasyncio`. Some demo programs
illustrate this. Code is standard MicroPython, but some device drivers use the
@ -108,10 +108,12 @@ displays:
* [SSD1963 large displays](https://github.com/peterhinch/micropython-tft-gui)
For historical reasons and to ensure consistency, code and documentation for
my GUI's employ the American spelling of `color`.
my GUI's employ the American spelling of `color`.
## 1.1 Change log
26 Aug 2021 Support [PR7682](https://github.com/micropython/micropython/pull/7682)
for fast text rendering.
25 Apr 2021 Support TTGO T-Display.
26 Mar 2021 Add ST7789. Alter uasyncio support on ili9341.
14 Mar 2021 Tested on Pi Pico.
@ -204,12 +206,19 @@ OLED as per `color_setup.py`, move to the root directory of the repo and run
Note also that the `gui.demos.aclock.py` demo comprises 38 lines of actual
code. This stuff is easier than you might think.
## 1.4 A performance boost
As of Aug 2021 color displays can benefit from a substantial performance boost
in rendering text. To take advantage of this, firmware should be dated after
26 Aug 21. The display driver and GUI core files should be updated. Ensure that
the new file `drivers/boolpalette.py` exists on the target hardware.
###### [Contents](./README.md#contents)
# 2. Files and Dependencies
Firmware should be V1.13 or later. On the Pi Pico firmware should be V1.15 or
later.
later. For fast text rendering use a daily build or V1.17 or later.
Installation comprises copying the `gui` and `drivers` directories, with their
contents, plus a hardware configuration file, to the target. The directory
@ -248,10 +257,6 @@ The `gui/core` directory contains the GUI core and its principal dependencies:
* `writer.py` Module for rendering Python fonts.
* `fplot.py` The graph plotting module.
* `colors.py` Color constants.
* `framebuf_utils.mpy` Accelerator for the `CWriter` class. This optional file
is compiled for STM hardware. It is specific to Pyboards (1.x and D) and will
be ignored on other ports. Details may be found
[here](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/WRITER.md#224-a-performance-boost).
###### [Contents](./README.md#contents)
@ -348,10 +353,6 @@ to check for newer versions:
* [writer.py](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/writer.py)
Provides text rendering of Python font files.
Optional feature:
* An STM32 implementation of
[this optimisation](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/WRITER.md#224-a-performance-boost).
A copy of the official driver for OLED displays using the SSD1306 chip is
provided. The official file is here:
* [SSD1306 driver](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py).

Wyświetl plik

@ -0,0 +1,20 @@
# boolpalette.py Implement BoolPalette class
# This is a 2-value color palette for rendering monochrome glyphs to color
# FrameBuffer instances. Supports destinations with up to 16 bit color.
# Copyright (c) Peter Hinch 2021
# Released under the MIT license see LICENSE
import framebuf
class BoolPalette(framebuf.FrameBuffer):
def __init__(self, mode):
buf = bytearray(4) # OK for <= 16 bit color
super().__init__(buf, 2, 1, mode)
def fg(self, color): # Set foreground color
self.pixel(1, 0, color)
def bg(self, color):
self.pixel(0, 0, color)

Wyświetl plik

@ -13,6 +13,7 @@ from time import sleep_ms
import gc
import framebuf
import uasyncio as asyncio
from drivers.boolpalette import BoolPalette
@micropython.viper
def _lcopy(dest:ptr16, source:ptr8, lut:ptr16, length:int):
@ -49,6 +50,7 @@ class ILI9341(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS4_HMSB
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width // 2)
self._mvb = memoryview(buf)

Wyświetl plik

@ -33,6 +33,8 @@ import framebuf
import utime
import gc
import sys
from drivers.boolpalette import BoolPalette
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
# Mode 0, 0 works on ESP and STM
@ -54,6 +56,7 @@ class SSD1331(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -30,6 +30,7 @@
import framebuf
import utime
import gc
from drivers.boolpalette import BoolPalette
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
@ -52,6 +53,7 @@ class SSD1331(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.RGB565
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width * 2)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -16,6 +16,7 @@ import utime
import gc
import micropython
from uctypes import addressof
from drivers.boolpalette import BoolPalette
# Timings with standard emitter
# 1.86ms * 128 lines = 240ms. copy dominates: show() took 272ms
@ -90,6 +91,7 @@ class SSD1351(framebuf.FrameBuffer):
self.height = height # Required by Writer class
self.width = width
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -15,6 +15,7 @@ import utime
import gc
import micropython
from uctypes import addressof
from drivers.boolpalette import BoolPalette
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
@ -59,6 +60,7 @@ class SSD1351(framebuf.FrameBuffer):
self.height = height # Required by Writer class
self.width = width
mode = framebuf.RGB565
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width * 2)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -16,6 +16,7 @@ import utime
import gc
import micropython
from uctypes import addressof
from drivers.boolpalette import BoolPalette
# https://github.com/peterhinch/micropython-nano-gui/issues/2
# The ESP32 does not work reliably in SPI mode 1,1. Waveforms look correct.
@ -87,6 +88,7 @@ class SSD1351(framebuf.FrameBuffer):
self.width = width
self.spi_init = init_spi
mode = framebuf.GS4_HMSB # Use 4bit greyscale.
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width // 2)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -17,6 +17,7 @@ import utime
import gc
import micropython
from uctypes import addressof
from drivers.boolpalette import BoolPalette
import sys
# https://github.com/peterhinch/micropython-nano-gui/issues/2
@ -77,6 +78,7 @@ class SSD1351(framebuf.FrameBuffer):
self.height = height # Required by Writer class
self.width = width
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
self.buffer = bytearray(self.height * self.width)
super().__init__(self.buffer, self.width, self.height, mode)

Wyświetl plik

@ -20,6 +20,7 @@ from time import sleep_ms
import framebuf
import gc
import micropython
from drivers.boolpalette import BoolPalette
# Datasheet para 8.4 scl write cycle 66ns == 15MHz
@ -60,6 +61,7 @@ class ST7735R(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * width)
self._mvb = memoryview(buf)

Wyświetl plik

@ -20,6 +20,7 @@ from time import sleep_ms
import framebuf
import gc
import micropython
from drivers.boolpalette import BoolPalette
# Datasheet para 8.4 scl write cycle 66ns == 15MHz
@ -58,6 +59,7 @@ class ST7735R(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width)
self._mvb = memoryview(buf)

Wyświetl plik

@ -20,6 +20,7 @@ from time import sleep_ms
import framebuf
import gc
import micropython
from drivers.boolpalette import BoolPalette
# Datasheet para 8.4 scl write cycle 66ns == 15MHz
@ -61,6 +62,7 @@ class ST7735R(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS4_HMSB # Use 4bit greyscale.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(self.height * self.width // 2)
self._mvb = memoryview(buf)

Wyświetl plik

@ -20,6 +20,7 @@ from time import sleep_ms
import framebuf
import gc
import micropython
from drivers.boolpalette import BoolPalette
# Datasheet para 8.4 scl write cycle 66ns == 15MHz
@ -61,6 +62,7 @@ class ST7735R(framebuf.FrameBuffer):
self.width = width
self._spi_init = init_spi
mode = framebuf.GS4_HMSB # Use 4bit greyscale.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * width // 2)
self._mvb = memoryview(buf)

Wyświetl plik

@ -21,6 +21,7 @@ import framebuf
import gc
import micropython
import uasyncio as asyncio
from drivers.boolpalette import BoolPalette
# User orientation constants
LANDSCAPE = 0 # Default
@ -73,6 +74,7 @@ class ST7789(framebuf.FrameBuffer):
self._spi_init = init_spi # Possible user callback
self._lock = asyncio.Lock()
mode = framebuf.GS4_HMSB # Use 4bit greyscale.
self.palette = BoolPalette(mode)
gc.collect()
buf = bytearray(height * -(-width // 2)) # Ceiling division for odd widths
self._mvb = memoryview(buf)

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,9 +1,10 @@
# writer.py Implements the Writer class.
# Handles colour, word wrap and tab stops
# V0.40 Jan 2021 Improved handling of word wrap and line clip. Upside-down
# V0.4.3 Aug 2021 Support for fast blit to color displays (PR7682).
# V0.4.0 Jan 2021 Improved handling of word wrap and line clip. Upside-down
# rendering no longer supported: delegate to device driver.
# V0.35 Sept 2020 Fast rendering option for color displays
# V0.3.5 Sept 2020 Fast rendering option for color displays
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2019-2021 Peter Hinch
@ -14,35 +15,34 @@
# Timings based on a 20 pixel high proportional font, run on a pyboard V1.0.
# Using CWriter's slow rendering: _printchar 9.5ms typ, 13.5ms max.
# Using Writer's fast rendering: _printchar 115μs min 480μs typ 950μs max.
# CWriter on Pyboard D SF2W at standard clock rate
# Fast method 500-600μs typical, up to 1.07ms on larger fonts
# Revised fast method 691μs avg, up to 2.14ms on larger fonts
# Slow method 2700μs typical, up to 11ms on larger fonts
import framebuf
from uctypes import bytearray_at, addressof
from sys import platform
from sys import implementation
import os
__version__ = (0, 4, 2)
__version__ = (0, 4, 3)
fast_mode = platform == 'pyboard'
if fast_mode:
def buildcheck(device):
if not hasattr(device, 'palette'):
return False
i0, i1, _ = implementation[1]
if i0 > 1 or i1 > 16:
return True
# After release of V1.17 require that build. Until then check for date.
# TODO simplify this once V1.17 is released.
try:
try:
from framebuf_utils import render
except ImportError: # May be running in GUI. Try relative import.
try:
from .framebuf_utils import render
except ImportError:
fast_mode = False
except ValueError:
fast_mode = False
if not fast_mode:
print('Ignoring missing or invalid framebuf_utils.mpy.')
datestring = os.uname()[3]
date = datestring.split(' on')[1]
date = date.lstrip()[:10]
idate = tuple([int(x) for x in date.split('-')])
return idate >= (2021, 8, 25)
except AttributeError:
return False
fast_mode = False # False for mono displays although actually these render fast
class DisplayState():
def __init__(self):
self.text_row = 0
@ -264,12 +264,14 @@ class Writer():
def setcolor(self, *_):
return self.fgcolor, self.bgcolor
# Writer for colour displays or upside down rendering
# Writer for colour displays.
class CWriter(Writer):
def __init__(self, device, font, fgcolor=None, bgcolor=None, verbose=True):
super().__init__(device, font, verbose)
global fast_mode
fast_mode = buildcheck(device)
if bgcolor is not None: # Assume monochrome.
self.bgcolor = bgcolor
if fgcolor is not None:
@ -285,11 +287,12 @@ class CWriter(Writer):
if self.glyph is None:
return # All done
buf = bytearray_at(addressof(self.glyph), len(self.glyph))
fbc = framebuf.FrameBuffer(buf, self.char_width, self.char_height, self.map)
fgcolor = self.bgcolor if invert else self.fgcolor
bgcolor = self.fgcolor if invert else self.bgcolor
# render clips a glyph if outside bounds of destination
render(self.device, fbc, s.text_col, s.text_row, fgcolor, bgcolor)
fbc = framebuf.FrameBuffer(buf, self.clip_width, self.char_height, self.map)
palette = self.device.palette
palette.bg(self.fgcolor if invert else self.bgcolor)
palette.fg(self.bgcolor if invert else self.fgcolor)
self.device.blit(fbc, s.text_col, s.text_row, -1, palette)
s.text_col += self.char_width
self.cpos += 1