kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
Plot module added. Release 0.1
rodzic
82be06767f
commit
ca99637fb1
477
README.md
477
README.md
|
@ -1,2 +1,475 @@
|
|||
# micropython-nano-gui
|
||||
A lightweight MicroPython GUI library for display drivers based on framebuf class
|
||||
A lightweight and minimal MicroPython GUI library for display drivers based on
|
||||
the `framebuf` class. With the exception of the Nokia 5110, such drivers are
|
||||
currently for color and monochrome OLED displays. This is coincidental.
|
||||
|
||||
These images don't do justice to the OLED displays which are visually
|
||||
impressive with bright colors and extreme contrast. For some reason they are
|
||||
quite hard to photograph.
|
||||
![Image](images/IMG_2885.png)
|
||||
One of the demos running on an Adafruit 1.27 inch OLED. The colors change
|
||||
dynamically with low values showing green, intermediate yellow and high red. In
|
||||
reality the colors are vivid: the right hand meter bar and LED are a brilliant
|
||||
yellow and the centre one is vivid green.
|
||||
|
||||
![Image](images/IMG_2887.png)
|
||||
The Dial object.
|
||||
|
||||
There is an optional [graph plotting module](./plot/FPLOT.md) for basic
|
||||
Cartesian and polar plots, also realtime plotting including time series.
|
||||
|
||||
Notes on [Adafruit and other OLED displays](./drivers/ADAFRUIT.md) including
|
||||
wiring details, pin names and hardware issues.
|
||||
|
||||
**NOTE** 20 Sep 2018 Under development. I am now reasonably happy with the API
|
||||
but I can't yet promise no changes. There may, of course, be bugs...
|
||||
|
||||
# Contents
|
||||
|
||||
1. [Introduction](./README.md#1-introduction)
|
||||
2. [Files and Dependencies](./README.md#2-files-and-dependencies)
|
||||
2.1 [Dependencies](./README.md#21-dependencies)
|
||||
2.2.1 [Monochrome use](./README.md#211-monochrome-use)
|
||||
2.2.2 [Color use](./README.md#222-color-use)
|
||||
3. [The nanogui module](./README.md#3-the-nanogui-module)
|
||||
3.1 [Initialisation](./README.md#31-initialisation) Initial setup and refresh method.
|
||||
3.2 [Label class](./README.md#32-label-class) Dynamic text at any screen location.
|
||||
3.3 [Meter class](./README.md#33-meter-class) A vertical panel meter.
|
||||
3.4 [LED class](./README.md#34-led-class) Virtual LED of any color.
|
||||
3.5 [Dial and Pointer classes](./README.md#35-dial-and-pointer-classes) Clock
|
||||
or compass style display of one or more pointers.
|
||||
4. [Device drivers](./README.md#4-device-drivers) Device driver compatibility
|
||||
requirements (these are minimal).
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
This library provides a limited set of GUI objects (widgets) for displays whose
|
||||
display driver is subclassed from the `framebuf` class. Examples are:
|
||||
|
||||
* The official [SSD1306 driver](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py).
|
||||
* The [PCD8544/Nokia 5110](https://github.com/mcauser/micropython-pcd8544.git).
|
||||
* The [Adafruit 0.96 inch color OLED](https://www.adafruit.com/product/684)
|
||||
with [this driver](https://github.com/peterhinch/micropython-nano-gui/tree/master/drivers/ssd1331).
|
||||
* A driver for [Adafruit 1.5 inch OLED](https://www.adafruit.com/product/1431)
|
||||
and [Adafruit 1.27 inch OLED](https://www.adafruit.com/product/1673) may be
|
||||
found [here](./drivers/ssd1351/README.md).
|
||||
|
||||
Widgets are intended for the display of data from physical devices such as
|
||||
sensors. The GUI is display-only: there is no provision for user input. This
|
||||
is because there are no `frmebuf`- based display drivers for screens with a
|
||||
touch overlay. Authors of applications requiring input should consider my touch
|
||||
GUI's for the official lcd160cr or for SSD1963 based displays.
|
||||
|
||||
Widgets are drawn using graphics primitives rather than icons to minimise RAM
|
||||
usage. It also enables them to be effciently rendered at arbitrary scale on
|
||||
devices with restricted processing power.
|
||||
|
||||
Owing to RAM requirements and limitations on communication speed, `framebuf`
|
||||
based display drivers are intended for physically small displays with limited
|
||||
numbers of pixels. The widgets are designed for displays as small as 0.96
|
||||
inches: this involves some compromises. They aim to maximise the information
|
||||
on screen by offering the option of dynamically changing colors.
|
||||
|
||||
Copying the contents of the frame buffer to the display is relatively slow. The
|
||||
time depends on the size of the frame buffer and the interface speed, but the
|
||||
latency may be too high for applications such as games. For example the time to
|
||||
update a 128*128*8 color ssd1351 display on a Pyboard 1.0 is 41ms.
|
||||
|
||||
Drivers based on `framebuf` must allocate contiguous RAM for the buffer. To
|
||||
avoid 'out of memory' errors it is best to instantiate the display early,
|
||||
possibly before importing many other modules. The `aclock.py` and `alevel.py`
|
||||
demos illustrate this.
|
||||
|
||||
# 2. Files and Dependencies
|
||||
|
||||
## 2.1 Files
|
||||
|
||||
* `nanogui.py` The library.
|
||||
* `mono_test.py` Tests/demos using the official SSD1306 library for a
|
||||
monochrome 128*64 OLED display.
|
||||
* `color96.py` Tests/demos for the Adafruit 0.96 inch color OLED.
|
||||
* `color15.py` Similar for Adafruit 1.27 inch and 1.5 inch color OLEDs. Edit
|
||||
the `height = 96` line as per the comment for the larger display.
|
||||
|
||||
Demos for Adafruit 1.27 inch and 1.5 inch color OLEDs. Edit the `height = 96`
|
||||
line as per the code comment for the larger display.
|
||||
* `aclock.py` Analog clock demo.
|
||||
* `alevel.py` Spirit level using Pyboard accelerometer.
|
||||
|
||||
Sample fonts created by [font_to_py.py](https://github.com/peterhinch/micropython-font-to-py.git):
|
||||
* `arial10.py`
|
||||
* `courier20.py`
|
||||
* `font6.py`
|
||||
* `freesans20.py`
|
||||
|
||||
## 2.2 Dependencies
|
||||
|
||||
All applicatons require a device driver for the display in use plus any Python
|
||||
font files in use. The following is required by all applications:
|
||||
|
||||
* [writer.py](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/writer.py)
|
||||
Provides text rendering.
|
||||
|
||||
### 2.2.1 Monochrome use
|
||||
|
||||
OLED displays using the SSD1306 chip require:
|
||||
* [ssd1306_setup.py](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/ssd1306_setup.py)
|
||||
Contains wiring information.
|
||||
* The official [SSD1306 driver](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py).
|
||||
|
||||
Displays based on the PCD8544 chip require:
|
||||
* [PCD8544/Nokia 5110](https://github.com/mcauser/micropython-pcd8544.git)
|
||||
|
||||
### 2.2.2 Color use
|
||||
|
||||
Supported displays amd their drivers are listed below:
|
||||
|
||||
* [Adafruit 0.96 inch color OLED](https://github.com/peterhinch/micropython-nano-gui/tree/master/drivers/ssd1331).
|
||||
Driver for SSD1331 controller.
|
||||
* [Adafruit 1.5 and 1.27 inch color OLEDs](./drivers/ssd1351/README.md)
|
||||
Driver for SSD1351 controller.
|
||||
|
||||
Test script for Adafruit 1.5 and 1.27 inch color OLED displays. It's a good
|
||||
idea to paste this at the REPL to ensure the display is working before
|
||||
progressing to the GUI. Remember to change `height` if using the 1.5 inch
|
||||
display.
|
||||
```python
|
||||
import machine
|
||||
from ssd1351 import SSD1351 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)
|
||||
ssd = SSD(spi, pcs, pdc, prst, height=96) # Ensure height is correct (96/128)
|
||||
ssd.fill(0)
|
||||
ssd.line(0, 0, 127, 95, ssd.rgb(0, 255, 0)) # Green diagonal corner-to-corner
|
||||
ssd.rect(0, 0, 15, 15, ssd.rgb(255, 0, 0)) # Red square at top left
|
||||
ssd.show()
|
||||
```
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
||||
# 3. The nanogui module
|
||||
|
||||
This supports widgets whose text components are drawn using the `Writer`
|
||||
(monochrome) or `CWriter` (colour) classes. Upside down rendering is not
|
||||
supported: attempts to specify it will produce unexpected results.
|
||||
|
||||
Widgets are drawn at specific locations on screen and are incompatible with the
|
||||
display of scrolling text: they are therefore not intended for use with the
|
||||
`Writer.printstring` method. The coordinates of a widget are those of its top
|
||||
left corner. If a border is specified, this is drawn outside of the limits of
|
||||
the widgets with a margin of 2 pixels. If the widget is placed at [row, col]
|
||||
the top left hand corner of the border is at [row-2, col-2].
|
||||
|
||||
When a widget is drawn or updated (typically with its `value` method) it is not
|
||||
immediately displayed. To update the display `nanogui.refresh` is called: this
|
||||
ensures that the `framebuf` contents are updated before copying the contents to
|
||||
the display. This postponement is for performance reasons and to provide the
|
||||
appearance of a rapid update.
|
||||
|
||||
## 3.1 Initialisation
|
||||
|
||||
The GUI is initialised in the following stages. The aim is to allocate the
|
||||
`framebuf` before importing other modules. This is intended to reduce the risk
|
||||
of memory failures when instantiating a large framebuf in an application which
|
||||
imports multiple modules.
|
||||
|
||||
Firstly set the display height and import the driver:
|
||||
```python
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display. Set to 128 for 1.5 inch
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD # Import the display driver
|
||||
```
|
||||
Then set up the bus (SPI or I2C) and instantiate the display. At this point the
|
||||
framebuffer is created:
|
||||
```python
|
||||
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)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
```
|
||||
Finally import `nanogui` modules and initialise the display. Import any other
|
||||
modules required by the application. For each font to be used import the
|
||||
Python font and create a `CWriter` instance (for monochrome displays a `Writer`
|
||||
is used):
|
||||
```python
|
||||
from nanogui import Label, Dial, Pointer, refresh # Whatever you need
|
||||
refresh(ssd) # Initialise and clear display.
|
||||
|
||||
from writer import CWriter # Import other modules
|
||||
import arial10 # Font
|
||||
GREEN = SSD.rgb(0, 255, 0) # Define colors
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
BLACK = 0
|
||||
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
# Instantiate any CWriters to be used (one for each font)
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
```
|
||||
|
||||
The `nanogui.refresh` method takes two args:
|
||||
1. `device` The display instance (supports multiple displays).
|
||||
2. `clear=False` If set `True` the display will be blanked; it is also
|
||||
blanked when a device is refreshed for the first time.
|
||||
|
||||
It should be called after instantiating the display, and again whenever the
|
||||
physical display is to be updated.
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
||||
## 3.2 Label class
|
||||
|
||||
This supports applications where text is to be rendered at specific screen
|
||||
locations.
|
||||
|
||||
Text can be static or dynamic. In the case of dynamic text the background is
|
||||
cleared to ensure that short strings cleanly replace longer ones.
|
||||
|
||||
Labels can be displayed with an optional single pixel border.
|
||||
|
||||
Colors are handled flexibly. By default the colors used are those of the
|
||||
`Writer` instance, however they can be changed dynamically; this might be used
|
||||
to warn of overrange or underrange values.
|
||||
|
||||
Constructor args:
|
||||
1. `writer` The `Writer` instance (font and screen) to use.
|
||||
2. `row` Location on screen.
|
||||
3. `col`
|
||||
4. `text` If a string is passed it is displayed: typically used for static
|
||||
text. If an integer is passed it is interpreted as the maximum text length
|
||||
in pixels; typically obtained from `writer.stringlen('-99.99')`. Nothing is
|
||||
dsplayed until `.value()` is called. Intended for dynamic text fields.
|
||||
5. `invert=False` Display in inverted or normal style.
|
||||
6. `fgcolor=None` Optionally override the `Writer` colors.
|
||||
7. `bgcolor=None`
|
||||
8. `bdcolor=False` If `False` no border is displayed. If `None` a border is
|
||||
shown in the `Writer` forgeround color. If a color is passed, it is used.
|
||||
|
||||
The constructor displays the string at the required location.
|
||||
|
||||
Methods:
|
||||
1. `value` Redraws the label. This takes the following args:
|
||||
* `text=None` The text to display. If `None` displays last value.
|
||||
* ` invert=False` If true, show inverse text.
|
||||
* `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
* `bgcolor=None` Background color, as per foreground.
|
||||
* `bdcolor=None` Border color. As per above except that if `False` is
|
||||
passed, no border is displayed. This clears a previously drawn border.
|
||||
Returns the current text string.
|
||||
2. `show` No args. (Re)draws the label. Primarily for internal use by GUI.
|
||||
|
||||
If populating a label would cause it to extend beyond the screen boundary a
|
||||
warning is printed at the console. The label may appear at an unexpected place.
|
||||
The following is a complete "Hello world" script.
|
||||
```python
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display. Set to 128 for 1.5 inch
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD # Import the display driver
|
||||
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)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
from nanogui import Label, refresh
|
||||
refresh(ssd) # Initialise and clear display.
|
||||
from writer import CWriter # Import other modules
|
||||
import freesans20 # Font
|
||||
GREEN = SSD.rgb(0, 255, 0) # Define colors
|
||||
BLACK = 0
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, freesans20, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
# End of boilerplate code. This is our application:
|
||||
Label(wri, 2, 2, 'Hello world!')
|
||||
refresh(ssd)
|
||||
```
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
||||
## 3.3 Meter class
|
||||
|
||||
This provides a vertical linear meter display of values scaled between 0.0 and
|
||||
1.0.
|
||||
|
||||
Constructor positional args:
|
||||
1. `writer` The `Writer` instance (font and screen) to use.
|
||||
2. `row` Location on screen.
|
||||
3. `col`
|
||||
Keyword only args:
|
||||
4. `height=50` Height of meter.
|
||||
5. `width=10` Width.
|
||||
6. `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
7. `bgcolor=None` Background color, as per foreground.
|
||||
8. `ptcolor=None` Color of meter pointer or bar. Default is foreground color.
|
||||
9. `bdcolor=False` If `False` no border is displayed. If `None` a border is
|
||||
shown in the `Writer` forgeround color. If a color is passed, it is used.
|
||||
10. `divisions=5` No. of gradutions to show.
|
||||
11. `label=None` A text string will cause a `Label` to be drawn below the
|
||||
meter. An integer will create a `Label` of that width for later use.
|
||||
12. `style=Meter.LINE` The pointer is a horizontal line. `Meter.BAR` causes a
|
||||
vertical bar to be displayed.
|
||||
13. `legends=None` If a tuple of strings is passed, `Label` instances will be
|
||||
displayed to the right hand side of the meter, starting at the bottom. E.G.
|
||||
`('0.0', '0.5', '1.0')`
|
||||
14. `value=None` Initial value. If `None` the meter will not be drawn until
|
||||
its `value()` method is called.
|
||||
|
||||
Methods:
|
||||
1. `value` Args: `n=None, color=None`.
|
||||
* `n` should be a float in range 0 to 1.0. Causes the meter to be updated.
|
||||
Out of range values are constrained. If `None` is passed the meter is not
|
||||
updated.
|
||||
* `color` Updates the color of the bar or line if a value is also passed.
|
||||
`None` causes no change.
|
||||
Returns the current value.
|
||||
2. `text` Updates the label if present (otherwise throws a `ValueError`). Args:
|
||||
* `text=None` The text to display. If `None` displays last value.
|
||||
* ` invert=False` If true, show inverse text.
|
||||
* `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
* `bgcolor=None` Background color, as per foreground.
|
||||
* `bdcolor=None` Border color. As per above except that if `False` is
|
||||
passed, no border is displayed. This clears a previously drawn border.
|
||||
3. `show` No args. (Re)draws the meter. Primarily for internal use by GUI.
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
||||
## 3.4 LED class
|
||||
|
||||
This is a virtual LED whose color may be altered dynamically.
|
||||
|
||||
Constructor positional args:
|
||||
1. `writer` The `Writer` instance (font and screen) to use.
|
||||
2. `row` Location on screen.
|
||||
3. `col`
|
||||
Keyword only args:
|
||||
4. `height=12` Height of LED.
|
||||
5. `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
6. `bgcolor=None` Background color, as per foreground.
|
||||
7. `bdcolor=False` If `False` no border is displayed. If `None` a border is
|
||||
shown in the `Writer` forgeround color. If a color is passed, it is used.
|
||||
8. `label=None` A text string will cause a `Label` to be drawn below the
|
||||
LED. An integer will create a `Label` of that width for later use.
|
||||
|
||||
Methods:
|
||||
1. `color` arg `c=None` Change the LED color to `c`. If `c` is `None` the LED
|
||||
is turned off (rendered in the background color).
|
||||
2. `text` Updates the label if present (otherwise throws a `ValueError`). Args:
|
||||
* `text=None` The text to display. If `None` displays last value.
|
||||
* ` invert=False` If true, show inverse text.
|
||||
* `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
* `bgcolor=None` Background color, as per foreground.
|
||||
* `bdcolor=None` Border color. As per above except that if `False` is
|
||||
passed, no border is displayed. This clears a previously drawn border.
|
||||
3. `show` No args. (Re)draws the LED. Primarily for internal use by GUI.
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
||||
## 3.5 Dial and Pointer classes
|
||||
|
||||
A dial is a circular analogue clock style display showing a set of pointers. To
|
||||
use, the `Dial` is instantiated then one or more `Pointer` objects are
|
||||
instantiated and assigned to it. The `Pointer.value` method enables the `Dial`
|
||||
to be updated, with the length, angle and color being dynamically variable.
|
||||
Pointer values are complex numbers.
|
||||
|
||||
Constructor positional args:
|
||||
1. `writer` The `Writer` instance (font and screen) to use.
|
||||
2. `row` Location on screen.
|
||||
3. `col`
|
||||
Keyword only args:
|
||||
4. `height=50` Height and width of dial.
|
||||
5. `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
6. `bgcolor=None` Background color, as per foreground.
|
||||
7. `bdcolor=False` If `False` no border is displayed. If `None` a border is
|
||||
shown in the `Writer` forgeround color. If a color is passed, it is used.
|
||||
8. `ticks=4` No. of gradutions to show.
|
||||
9. `label=None` A text string will cause a `Label` to be drawn below the
|
||||
meter. An integer will create a `Label` of that width for later use.
|
||||
10. `style=Dial.CLOCK` Pointers are drawn from the centre of the circle as per
|
||||
the hands of a clock. `Dial.COMPASS` causes pointers to be drawn as arrows
|
||||
centred on the control's centre. Arrow tail chevrons are suppressed for very
|
||||
short pointers.
|
||||
11. `pip=None` Draws a central dot. A color may be passed, otherwise the
|
||||
foreground color will be used. If `False` is passed, no pip will be drawn. The
|
||||
pip is suppressed if the shortest pointer would be hard to see.
|
||||
|
||||
When a `Pointer` is instantiated it is assigned to the `Dial` by the `Pointer`
|
||||
constructor.
|
||||
|
||||
The `Pointer` class:
|
||||
|
||||
Constructor arg:
|
||||
1. `dial` The `Dial` instance on which it is to be dsplayed.
|
||||
|
||||
Methods:
|
||||
1. `value` Args:
|
||||
* `v=None` The value is a complex number. If its magnitude exceeds unity it
|
||||
is reduced (preserving phase) to constrain it to the boundary of the unit
|
||||
circle.
|
||||
* `color=None` By default the pointer is rendered in the foreground color
|
||||
of the parent `Dial`. Otherwise the passed color is used.
|
||||
2. `text` Updates the label if present (otherwise throws a `ValueError`). Args:
|
||||
* `text=None` The text to display. If `None` displays last value.
|
||||
* ` invert=False` If true, show inverse text.
|
||||
* `fgcolor=None` Foreground color: if `None` the `Writer` default is used.
|
||||
* `bgcolor=None` Background color, as per foreground.
|
||||
* `bdcolor=None` Border color. As per above except that if `False` is
|
||||
passed, no border is displayed. This clears a previously drawn border.
|
||||
3. `show` No args. (Re)draws the control. Primarily for internal use by GUI.
|
||||
|
||||
Typical usage (`ssd` is the device and `wri` is the current `Writer`):
|
||||
```python
|
||||
def clock(ssd, wri):
|
||||
# Border in Writer foreground color:
|
||||
dial = Dial(wri, 5, 5, ticks = 12, bdcolor=None)
|
||||
hrs = Pointer(dial)
|
||||
mins = Pointer(dial)
|
||||
hrs.value(0 + 0.7j, RED)
|
||||
mins.value(0 + 0.9j, YELLOW)
|
||||
dm = cmath.exp(-1j * cmath.pi / 30) # Rotate by 1 minute
|
||||
dh = cmath.exp(-1j * cmath.pi / 1800) # Rotate hours by 1 minute
|
||||
# Twiddle the hands: see clock.py for an actual clock
|
||||
for _ in range(80):
|
||||
utime.sleep_ms(200)
|
||||
mins.value(mins.value() * dm, RED)
|
||||
hrs.value(hrs.value() * dh, YELLOW)
|
||||
refresh(ssd)
|
||||
```
|
||||
|
||||
# 4. Device drivers
|
||||
|
||||
For a driver to support `nanogui` it must be subclassed from `framebuf` and
|
||||
provide `height` and `width` bound variables defining the display size in
|
||||
pixels. This is all that is required for monochrome drivers.
|
||||
|
||||
For color drivers, to conserve RAM it is suggested that 8-bit color is used
|
||||
for the `framebuf`. If the hardware does not support this, conversion to the
|
||||
supported color space needs to be done "on the fly" as per the SSD1351 driver.
|
||||
Since this is likely to be slow, consider using native, viper or assembler.
|
||||
|
||||
Color drivers should have a static method converting rgb(255, 255, 255) to a
|
||||
form acceptable to the driver. For 8-bit rrrgggbb this can be:
|
||||
```python
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
|
||||
```
|
||||
This should be amended if the hardware uses a different 8-bit format.
|
||||
|
||||
The `Writer` (monochrome) or `CWriter` (color) classes and the `nanogui` module
|
||||
should then work automatically.
|
||||
|
||||
If a display uses I2C note that owing to
|
||||
[this issue](https://github.com/micropython/micropython/pull/4020) soft I2C
|
||||
may be required, depending on the detailed specification of the chip.
|
||||
|
||||
###### [Contents](./README.md#contents)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# aclock.py Test/demo program for Adafruit ssd1351-based OLED displays
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
|
||||
# WIRING
|
||||
# Pyb SSD
|
||||
# 3v3 Vin
|
||||
# Gnd Gnd
|
||||
# X1 DC
|
||||
# X2 CS
|
||||
# X3 Rst
|
||||
# X6 CLK
|
||||
# X8 DATA
|
||||
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display
|
||||
# height = 128 # 1.5 inch 128*128 display
|
||||
|
||||
# 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.
|
||||
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware
|
||||
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)
|
||||
gc.collect() # Precaution before instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
from nanogui import Dial, Pointer, refresh, Label
|
||||
refresh(ssd) # Initialise and clear display.
|
||||
|
||||
# Now import other modules
|
||||
import cmath
|
||||
import utime
|
||||
from writer import CWriter
|
||||
|
||||
# Font for CWriter
|
||||
import arial10
|
||||
|
||||
GREEN = SSD.rgb(0, 255, 0)
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
BLACK = 0
|
||||
|
||||
def aclock():
|
||||
uv = lambda phi : cmath.rect(1, phi) # Return a unit vector of phase phi
|
||||
pi = cmath.pi
|
||||
days = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
|
||||
'Sunday')
|
||||
months = ('Jan', 'Feb', 'March', 'April', 'May', 'June', 'July',
|
||||
'Aug', 'Sept', 'Oct', 'Nov', 'Dec')
|
||||
# Instantiate CWriter
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
|
||||
# Instantiate displayable objects
|
||||
dial = Dial(wri, 2, 2, height = 75, ticks = 12, bdcolor=None, label=120, pip=False) # Border in fg color
|
||||
lbltim = Label(wri, 5, 85, 35)
|
||||
hrs = Pointer(dial)
|
||||
mins = Pointer(dial)
|
||||
secs = Pointer(dial)
|
||||
|
||||
hstart = 0 + 0.7j # Pointer lengths and position at top
|
||||
mstart = 0 + 0.92j
|
||||
sstart = 0 + 0.92j
|
||||
while True:
|
||||
t = utime.localtime()
|
||||
hrs.value(hstart * uv(-t[3]*pi/6 - t[4]*pi/360), YELLOW)
|
||||
mins.value(mstart * uv(-t[4] * pi/30), YELLOW)
|
||||
secs.value(sstart * uv(-t[5] * pi/30), RED)
|
||||
lbltim.value('{:02d}.{:02d}.{:02d}'.format(t[3], t[4], t[5]))
|
||||
dial.text('{} {} {} {}'.format(days[t[6]], t[2], months[t[1] - 1], t[0]))
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
|
||||
aclock()
|
|
@ -0,0 +1,91 @@
|
|||
# alevel.py Test/demo program for Adafruit ssd1351-based OLED displays
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
|
||||
# WIRING
|
||||
# Pyb SSD
|
||||
# 3v3 Vin
|
||||
# Gnd Gnd
|
||||
# X1 DC
|
||||
# X2 CS
|
||||
# X3 Rst
|
||||
# X6 CLK
|
||||
# X8 DATA
|
||||
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display
|
||||
# height = 128 # 1.5 inch 128*128 display
|
||||
|
||||
# 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.
|
||||
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware
|
||||
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)
|
||||
gc.collect() # Precaution befor instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
from nanogui import Dial, Pointer, refresh
|
||||
refresh(ssd) # Initialise and clear display.
|
||||
|
||||
# Now import other modules
|
||||
|
||||
import utime
|
||||
import pyb
|
||||
from writer import CWriter
|
||||
import arial10 # Font
|
||||
|
||||
GREEN = SSD.rgb(0, 255, 0)
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
BLACK = 0
|
||||
|
||||
|
||||
def main():
|
||||
print('alevel test is running.')
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
acc = pyb.Accel()
|
||||
dial = Dial(wri, 5, 5, height = 75, ticks = 12, bdcolor=None,
|
||||
label='Tilt Pyboard', style = Dial.COMPASS, pip=YELLOW) # Border in fg color
|
||||
ptr = Pointer(dial)
|
||||
scale = 1/40
|
||||
while True:
|
||||
x, y, z = acc.filtered_xyz()
|
||||
# Depending on relative alignment of display and Pyboard this line may
|
||||
# need changing: swap x and y or change signs so arrow points in direction
|
||||
# board is tilted.
|
||||
ptr.value(-y*scale + 1j*x*scale, YELLOW)
|
||||
refresh(ssd)
|
||||
utime.sleep_ms(200)
|
||||
|
||||
main()
|
|
@ -0,0 +1,139 @@
|
|||
# Code generated by font-to-py.py.
|
||||
# Font: Arial.ttf
|
||||
version = '0.25'
|
||||
|
||||
def height():
|
||||
return 10
|
||||
|
||||
def max_width():
|
||||
return 11
|
||||
|
||||
def hmap():
|
||||
return True
|
||||
|
||||
def reverse():
|
||||
return False
|
||||
|
||||
def monospaced():
|
||||
return False
|
||||
|
||||
def min_ch():
|
||||
return 32
|
||||
|
||||
def max_ch():
|
||||
return 126
|
||||
|
||||
_font =\
|
||||
b'\x06\x00\x70\x88\x08\x10\x20\x20\x00\x20\x00\x00\x03\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x80\x80\x80\x80\x80\x80'\
|
||||
b'\x00\x80\x00\x00\x04\x00\xa0\xa0\xa0\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x06\x00\x28\x28\xf8\x50\x50\xf8\xa0\xa0\x00\x00\x06\x00\x70\xa8'\
|
||||
b'\xa0\x70\x28\x28\xa8\x70\x20\x00\x0a\x00\x62\x00\x94\x00\x94\x00'\
|
||||
b'\x68\x00\x0b\x00\x14\x80\x14\x80\x23\x00\x00\x00\x00\x00\x07\x00'\
|
||||
b'\x30\x48\x48\x30\x50\x8c\x88\x74\x00\x00\x02\x00\x80\x80\x80\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x04\x00\x20\x40\x80\x80\x80\x80\x80\x80'\
|
||||
b'\x40\x20\x04\x00\x80\x40\x20\x20\x20\x20\x20\x20\x40\x80\x04\x00'\
|
||||
b'\x40\xe0\x40\xa0\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x20\x20'\
|
||||
b'\xf8\x20\x20\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x80'\
|
||||
b'\x80\x80\x04\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x03\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x03\x00\x20\x20\x40\x40'\
|
||||
b'\x40\x40\x80\x80\x00\x00\x06\x00\x70\x88\x88\x88\x88\x88\x88\x70'\
|
||||
b'\x00\x00\x06\x00\x20\x60\xa0\x20\x20\x20\x20\x20\x00\x00\x06\x00'\
|
||||
b'\x70\x88\x08\x08\x10\x20\x40\xf8\x00\x00\x06\x00\x70\x88\x08\x30'\
|
||||
b'\x08\x08\x88\x70\x00\x00\x06\x00\x10\x30\x50\x50\x90\xf8\x10\x10'\
|
||||
b'\x00\x00\x06\x00\x78\x40\x80\xf0\x08\x08\x88\x70\x00\x00\x06\x00'\
|
||||
b'\x70\x88\x80\xf0\x88\x88\x88\x70\x00\x00\x06\x00\xf8\x10\x10\x20'\
|
||||
b'\x20\x40\x40\x40\x00\x00\x06\x00\x70\x88\x88\x70\x88\x88\x88\x70'\
|
||||
b'\x00\x00\x06\x00\x70\x88\x88\x88\x78\x08\x88\x70\x00\x00\x03\x00'\
|
||||
b'\x00\x00\x80\x00\x00\x00\x00\x80\x00\x00\x03\x00\x00\x00\x80\x00'\
|
||||
b'\x00\x00\x00\x80\x80\x80\x06\x00\x00\x00\x08\x70\x80\x70\x08\x00'\
|
||||
b'\x00\x00\x06\x00\x00\x00\x00\xf8\x00\xf8\x00\x00\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x80\x70\x08\x70\x80\x00\x00\x00\x06\x00\x70\x88\x08\x10'\
|
||||
b'\x20\x20\x00\x20\x00\x00\x0b\x00\x1f\x00\x60\x80\x4d\x40\x93\x40'\
|
||||
b'\xa2\x40\xa2\x40\xa6\x80\x9b\x00\x40\x40\x3f\x80\x08\x00\x10\x28'\
|
||||
b'\x28\x28\x44\x7c\x82\x82\x00\x00\x07\x00\xf8\x84\x84\xfc\x84\x84'\
|
||||
b'\x84\xf8\x00\x00\x07\x00\x38\x44\x80\x80\x80\x80\x44\x38\x00\x00'\
|
||||
b'\x07\x00\xf0\x88\x84\x84\x84\x84\x88\xf0\x00\x00\x06\x00\xf8\x80'\
|
||||
b'\x80\xf8\x80\x80\x80\xf8\x00\x00\x06\x00\xf8\x80\x80\xf0\x80\x80'\
|
||||
b'\x80\x80\x00\x00\x08\x00\x38\x44\x82\x80\x8e\x82\x44\x38\x00\x00'\
|
||||
b'\x07\x00\x84\x84\x84\xfc\x84\x84\x84\x84\x00\x00\x02\x00\x80\x80'\
|
||||
b'\x80\x80\x80\x80\x80\x80\x00\x00\x05\x00\x10\x10\x10\x10\x10\x90'\
|
||||
b'\x90\x60\x00\x00\x07\x00\x84\x88\x90\xb0\xd0\x88\x88\x84\x00\x00'\
|
||||
b'\x06\x00\x80\x80\x80\x80\x80\x80\x80\xf8\x00\x00\x08\x00\x82\xc6'\
|
||||
b'\xc6\xaa\xaa\xaa\x92\x92\x00\x00\x07\x00\x84\xc4\xa4\xa4\x94\x94'\
|
||||
b'\x8c\x84\x00\x00\x08\x00\x38\x44\x82\x82\x82\x82\x44\x38\x00\x00'\
|
||||
b'\x06\x00\xf0\x88\x88\x88\xf0\x80\x80\x80\x00\x00\x08\x00\x38\x44'\
|
||||
b'\x82\x82\x82\x9a\x44\x3e\x00\x00\x07\x00\xf8\x84\x84\xf8\x90\x88'\
|
||||
b'\x88\x84\x00\x00\x07\x00\x78\x84\x80\x60\x18\x04\x84\x78\x00\x00'\
|
||||
b'\x06\x00\xf8\x20\x20\x20\x20\x20\x20\x20\x00\x00\x07\x00\x84\x84'\
|
||||
b'\x84\x84\x84\x84\x84\x78\x00\x00\x08\x00\x82\x82\x44\x44\x28\x28'\
|
||||
b'\x10\x10\x00\x00\x0b\x00\x84\x20\x8a\x20\x4a\x40\x4a\x40\x51\x40'\
|
||||
b'\x51\x40\x20\x80\x20\x80\x00\x00\x00\x00\x07\x00\x84\x48\x48\x30'\
|
||||
b'\x30\x48\x48\x84\x00\x00\x08\x00\x82\x44\x44\x28\x10\x10\x10\x10'\
|
||||
b'\x00\x00\x07\x00\x7c\x08\x10\x10\x20\x20\x40\xfc\x00\x00\x03\x00'\
|
||||
b'\xc0\x80\x80\x80\x80\x80\x80\x80\x80\xc0\x03\x00\x80\x80\x40\x40'\
|
||||
b'\x40\x40\x20\x20\x00\x00\x03\x00\xc0\x40\x40\x40\x40\x40\x40\x40'\
|
||||
b'\x40\xc0\x05\x00\x20\x50\x50\x88\x00\x00\x00\x00\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xfc\x00\x04\x00\x80\x40\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x70\x88\x78\x88\x98\xe8'\
|
||||
b'\x00\x00\x06\x00\x80\x80\xb0\xc8\x88\x88\xc8\xb0\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x70\x88\x80\x80\x88\x70\x00\x00\x06\x00\x08\x08\x68\x98'\
|
||||
b'\x88\x88\x98\x68\x00\x00\x06\x00\x00\x00\x70\x88\xf8\x80\x88\x70'\
|
||||
b'\x00\x00\x04\x00\x20\x40\xe0\x40\x40\x40\x40\x40\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x68\x98\x88\x88\x98\x68\x08\xf0\x06\x00\x80\x80\xb0\xc8'\
|
||||
b'\x88\x88\x88\x88\x00\x00\x02\x00\x80\x00\x80\x80\x80\x80\x80\x80'\
|
||||
b'\x00\x00\x02\x00\x40\x00\x40\x40\x40\x40\x40\x40\x40\x80\x05\x00'\
|
||||
b'\x80\x80\x90\xa0\xc0\xe0\xa0\x90\x00\x00\x02\x00\x80\x80\x80\x80'\
|
||||
b'\x80\x80\x80\x80\x00\x00\x08\x00\x00\x00\xbc\xd2\x92\x92\x92\x92'\
|
||||
b'\x00\x00\x06\x00\x00\x00\xf0\x88\x88\x88\x88\x88\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x70\x88\x88\x88\x88\x70\x00\x00\x06\x00\x00\x00\xb0\xc8'\
|
||||
b'\x88\x88\xc8\xb0\x80\x80\x06\x00\x00\x00\x68\x98\x88\x88\x98\x68'\
|
||||
b'\x08\x08\x04\x00\x00\x00\xa0\xc0\x80\x80\x80\x80\x00\x00\x06\x00'\
|
||||
b'\x00\x00\x70\x88\x60\x10\x88\x70\x00\x00\x03\x00\x40\x40\xe0\x40'\
|
||||
b'\x40\x40\x40\x60\x00\x00\x06\x00\x00\x00\x88\x88\x88\x88\x98\x68'\
|
||||
b'\x00\x00\x06\x00\x00\x00\x88\x88\x50\x50\x20\x20\x00\x00\x0a\x00'\
|
||||
b'\x00\x00\x00\x00\x88\x80\x94\x80\x55\x00\x55\x00\x22\x00\x22\x00'\
|
||||
b'\x00\x00\x00\x00\x06\x00\x00\x00\x88\x50\x20\x20\x50\x88\x00\x00'\
|
||||
b'\x06\x00\x00\x00\x88\x88\x50\x50\x20\x20\x20\x40\x06\x00\x00\x00'\
|
||||
b'\xf8\x10\x20\x20\x40\xf8\x00\x00\x04\x00\x20\x40\x40\x40\x80\x40'\
|
||||
b'\x40\x40\x40\x20\x02\x00\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80'\
|
||||
b'\x04\x00\x80\x40\x40\x40\x20\x40\x40\x40\x40\x80\x06\x00\x00\x00'\
|
||||
b'\x00\xe8\xb0\x00\x00\x00\x00\x00'
|
||||
|
||||
_index =\
|
||||
b'\x00\x00\x0c\x00\x0c\x00\x18\x00\x18\x00\x24\x00\x24\x00\x30\x00'\
|
||||
b'\x30\x00\x3c\x00\x3c\x00\x48\x00\x48\x00\x5e\x00\x5e\x00\x6a\x00'\
|
||||
b'\x6a\x00\x76\x00\x76\x00\x82\x00\x82\x00\x8e\x00\x8e\x00\x9a\x00'\
|
||||
b'\x9a\x00\xa6\x00\xa6\x00\xb2\x00\xb2\x00\xbe\x00\xbe\x00\xca\x00'\
|
||||
b'\xca\x00\xd6\x00\xd6\x00\xe2\x00\xe2\x00\xee\x00\xee\x00\xfa\x00'\
|
||||
b'\xfa\x00\x06\x01\x06\x01\x12\x01\x12\x01\x1e\x01\x1e\x01\x2a\x01'\
|
||||
b'\x2a\x01\x36\x01\x36\x01\x42\x01\x42\x01\x4e\x01\x4e\x01\x5a\x01'\
|
||||
b'\x5a\x01\x66\x01\x66\x01\x72\x01\x72\x01\x7e\x01\x7e\x01\x8a\x01'\
|
||||
b'\x8a\x01\x96\x01\x96\x01\xac\x01\xac\x01\xb8\x01\xb8\x01\xc4\x01'\
|
||||
b'\xc4\x01\xd0\x01\xd0\x01\xdc\x01\xdc\x01\xe8\x01\xe8\x01\xf4\x01'\
|
||||
b'\xf4\x01\x00\x02\x00\x02\x0c\x02\x0c\x02\x18\x02\x18\x02\x24\x02'\
|
||||
b'\x24\x02\x30\x02\x30\x02\x3c\x02\x3c\x02\x48\x02\x48\x02\x54\x02'\
|
||||
b'\x54\x02\x60\x02\x60\x02\x6c\x02\x6c\x02\x78\x02\x78\x02\x84\x02'\
|
||||
b'\x84\x02\x90\x02\x90\x02\x9c\x02\x9c\x02\xa8\x02\xa8\x02\xb4\x02'\
|
||||
b'\xb4\x02\xca\x02\xca\x02\xd6\x02\xd6\x02\xe2\x02\xe2\x02\xee\x02'\
|
||||
b'\xee\x02\xfa\x02\xfa\x02\x06\x03\x06\x03\x12\x03\x12\x03\x1e\x03'\
|
||||
b'\x1e\x03\x2a\x03\x2a\x03\x36\x03\x36\x03\x42\x03\x42\x03\x4e\x03'\
|
||||
b'\x4e\x03\x5a\x03\x5a\x03\x66\x03\x66\x03\x72\x03\x72\x03\x7e\x03'\
|
||||
b'\x7e\x03\x8a\x03\x8a\x03\x96\x03\x96\x03\xa2\x03\xa2\x03\xae\x03'\
|
||||
b'\xae\x03\xba\x03\xba\x03\xc6\x03\xc6\x03\xd2\x03\xd2\x03\xde\x03'\
|
||||
b'\xde\x03\xea\x03\xea\x03\xf6\x03\xf6\x03\x02\x04\x02\x04\x0e\x04'\
|
||||
b'\x0e\x04\x1a\x04\x1a\x04\x26\x04\x26\x04\x32\x04\x32\x04\x3e\x04'\
|
||||
b'\x3e\x04\x54\x04\x54\x04\x60\x04\x60\x04\x6c\x04\x6c\x04\x78\x04'\
|
||||
b'\x78\x04\x84\x04\x84\x04\x90\x04\x90\x04\x9c\x04\x9c\x04\xa8\x04'\
|
||||
|
||||
_mvfont = memoryview(_font)
|
||||
|
||||
def get_ch(ch):
|
||||
ordch = ord(ch)
|
||||
ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32
|
||||
idx_offs = 4 * (ordch - 32)
|
||||
offset = int.from_bytes(_index[idx_offs : idx_offs + 2], 'little')
|
||||
next_offs = int.from_bytes(_index[idx_offs + 2 : idx_offs + 4], 'little')
|
||||
width = int.from_bytes(_font[offset:offset + 2], 'little')
|
||||
return _mvfont[offset + 2:next_offs], 10, width
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
# color15.py Test/demo program for Adafruit ssd1351-based OLED displays
|
||||
# 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
|
||||
# For wiring details see drivers/ADAFRUIT.md in this repo.
|
||||
|
||||
# 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.
|
||||
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display
|
||||
# height = 128 # 1.5 inch 128*128 display
|
||||
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware and framebuf before importing modules
|
||||
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)
|
||||
gc.collect() # Precaution befor instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
|
||||
import cmath
|
||||
import utime
|
||||
import uos
|
||||
from writer import Writer, CWriter
|
||||
from nanogui import Label, Meter, LED, Dial, Pointer, refresh
|
||||
|
||||
# Fonts
|
||||
import arial10, freesans20
|
||||
|
||||
GREEN = SSD.rgb(0, 255, 0)
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
BLACK = 0
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
|
||||
def meter():
|
||||
print('Meter test.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
color = lambda v : RED if v > 0.7 else YELLOW if v > 0.5 else GREEN
|
||||
txt = lambda v : 'ovr' if v > 0.7 else 'high' if v > 0.5 else 'ok'
|
||||
m0 = Meter(wri, 5, 2, divisions = 4, ptcolor=YELLOW,
|
||||
label='left', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||
l0 = LED(wri, ssd.height - 16 - wri.height, 2, bdcolor=YELLOW, label ='over')
|
||||
m1 = Meter(wri, 5, 50, divisions = 4, ptcolor=YELLOW,
|
||||
label='right', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||
l1 = LED(wri, ssd.height - 16 - wri.height, 50, bdcolor=YELLOW, label ='over')
|
||||
m2 = Meter(wri, 5, 98, divisions = 4, ptcolor=YELLOW,
|
||||
label='bass', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||
l2 = LED(wri, ssd.height - 16 - wri.height, 98, bdcolor=YELLOW, label ='over')
|
||||
steps = 10
|
||||
for n in range(steps):
|
||||
v = int.from_bytes(uos.urandom(3),'little')/16777216
|
||||
m0.value(v, color(v))
|
||||
l0.color(color(v))
|
||||
l0.text(txt(v), fgcolor=color(v))
|
||||
v = n/steps
|
||||
m1.value(v, color(v))
|
||||
l1.color(color(v))
|
||||
l1.text(txt(v), fgcolor=color(v))
|
||||
v = 1 - n/steps
|
||||
m2.value(v, color(v))
|
||||
l2.color(color(v))
|
||||
l2.text(txt(v), fgcolor=color(v))
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
|
||||
|
||||
def multi_fields(t):
|
||||
print('Dynamic labels.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
nfields = []
|
||||
dy = wri.height + 6
|
||||
y = 2
|
||||
col = 15
|
||||
width = wri.stringlen('99.99')
|
||||
for txt in ('X:', 'Y:', 'Z:'):
|
||||
Label(wri, y, 0, txt) # Use wri default colors
|
||||
nfields.append(Label(wri, y, col, width, bdcolor=None)) # Specify a border, color TBD
|
||||
y += dy
|
||||
|
||||
end = utime.ticks_add(utime.ticks_ms(), t * 1000)
|
||||
while utime.ticks_diff(end, utime.ticks_ms()) > 0:
|
||||
for field in nfields:
|
||||
value = int.from_bytes(uos.urandom(3),'little')/167772
|
||||
overrange = None if value < 70 else YELLOW if value < 90 else RED
|
||||
field.value('{:5.2f}'.format(value), fgcolor = overrange, bdcolor = overrange)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
Label(wri, 0, 64, ' OK ', True, fgcolor = RED)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
|
||||
def vari_fields():
|
||||
print('Variable label styles.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
wri_large = CWriter(ssd, freesans20, GREEN, BLACK, verbose=False)
|
||||
wri_large.set_clip(True, True, False)
|
||||
Label(wri_large, 0, 0, 'Text')
|
||||
Label(wri_large, 20, 0, 'Border')
|
||||
width = wri_large.stringlen('Yellow')
|
||||
lbl_text = Label(wri_large, 0, 65, width)
|
||||
lbl_bord = Label(wri_large, 20, 65, width)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('Red')
|
||||
lbl_var = Label(wri_large, 50, 2, '25.46', fgcolor=RED, bdcolor=RED)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('Yellow')
|
||||
lbl_var.value(bdcolor=YELLOW)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('None')
|
||||
lbl_var.value(bdcolor=False)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Yellow')
|
||||
lbl_bord.value('None')
|
||||
lbl_var.value(fgcolor=YELLOW)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Blue')
|
||||
lbl_bord.value('Green')
|
||||
lbl_var.value('18.99', fgcolor=BLUE, bdcolor=GREEN)
|
||||
Label(wri, ssd.height - wri.height - 2, 0, 'Done', fgcolor=RED)
|
||||
refresh(ssd)
|
||||
|
||||
def clock(x):
|
||||
print('Clock test.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
lbl = Label(wri, 5, 85, 'Clock')
|
||||
dial = Dial(wri, 5, 5, height = 75, ticks = 12, bdcolor=None, label=50) # Border in fg color
|
||||
hrs = Pointer(dial)
|
||||
mins = Pointer(dial)
|
||||
hrs.value(0 + 0.7j, RED)
|
||||
mins.value(0 + 0.9j, YELLOW)
|
||||
dm = cmath.rect(1, -cmath.pi/30) # Rotate by 1 minute (CW)
|
||||
dh = cmath.rect(1, -cmath.pi/1800) # Rotate hours by 1 minute
|
||||
for n in range(x):
|
||||
refresh(ssd)
|
||||
utime.sleep_ms(200)
|
||||
mins.value(mins.value() * dm, YELLOW)
|
||||
hrs.value(hrs.value() * dh, RED)
|
||||
dial.text('ticks: {}'.format(n))
|
||||
lbl.value('Done')
|
||||
|
||||
def compass(x):
|
||||
print('Compass test.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
dial = Dial(wri, 5, 5, height = 75, bdcolor=None, label=50, style = Dial.COMPASS)
|
||||
bearing = Pointer(dial)
|
||||
bearing.value(0 + 1j, RED)
|
||||
dh = cmath.rect(1, -cmath.pi/30) # Rotate by 6 degrees CW
|
||||
for n in range(x):
|
||||
utime.sleep_ms(200)
|
||||
bearing.value(bearing.value() * dh, RED)
|
||||
refresh(ssd)
|
||||
|
||||
print('Color display test is running.')
|
||||
clock(70)
|
||||
compass(70)
|
||||
meter()
|
||||
multi_fields(t = 10)
|
||||
vari_fields()
|
||||
print('Test complete.')
|
|
@ -0,0 +1,133 @@
|
|||
# color96.py Test/demo program for ssd1331 Adafruit 0.96" OLED display
|
||||
# https://www.adafruit.com/product/684
|
||||
# For wiring details see drivers/ADAFRUIT.md in this repo.
|
||||
|
||||
# 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.
|
||||
|
||||
import machine
|
||||
import gc
|
||||
from 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)
|
||||
gc.collect()
|
||||
ssd = SSD(spi, pcs, pdc, prst) # Create a display instance
|
||||
|
||||
from nanogui import Label, Meter, LED, refresh
|
||||
refresh(ssd)
|
||||
# Fonts
|
||||
import arial10
|
||||
from writer import Writer, CWriter
|
||||
import utime
|
||||
import uos
|
||||
|
||||
GREEN = SSD.rgb(0, 255, 0)
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
BLACK = 0
|
||||
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
|
||||
def meter():
|
||||
print('meter')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
m = Meter(wri, 5, 2, height = 45, divisions = 4, ptcolor=YELLOW,
|
||||
label='level', style=Meter.BAR, legends=('0.0', '0.5', '1.0'))
|
||||
l = LED(wri, 5, 40, bdcolor=YELLOW, label ='over')
|
||||
steps = 10
|
||||
for _ in range(steps):
|
||||
v = int.from_bytes(uos.urandom(3),'little')/16777216
|
||||
m.value(v)
|
||||
l.color(GREEN if v < 0.5 else RED)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
refresh(ssd)
|
||||
|
||||
|
||||
def multi_fields(t):
|
||||
print('multi_fields')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
nfields = []
|
||||
dy = wri.height + 6
|
||||
y = 2
|
||||
col = 15
|
||||
width = wri.stringlen('99.99')
|
||||
for txt in ('X:', 'Y:', 'Z:'):
|
||||
Label(wri, y, 0, txt) # Use wri default colors
|
||||
nfields.append(Label(wri, y, col, width, bdcolor=None)) # Specify a border, color TBD
|
||||
y += dy
|
||||
|
||||
end = utime.ticks_add(utime.ticks_ms(), t * 1000)
|
||||
while utime.ticks_diff(end, utime.ticks_ms()) > 0:
|
||||
for field in nfields:
|
||||
value = int.from_bytes(uos.urandom(3),'little')/167772
|
||||
overrange = None if value < 70 else YELLOW if value < 90 else RED
|
||||
field.value('{:5.2f}'.format(value), fgcolor = overrange, bdcolor = overrange)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
Label(wri, 0, 64, ' OK ', True, fgcolor = RED)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
|
||||
def vari_fields():
|
||||
print('vari_fields')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
Label(wri, 0, 0, 'Text:')
|
||||
Label(wri, 20, 0, 'Border:')
|
||||
width = wri.stringlen('Yellow')
|
||||
lbl_text = Label(wri, 0, 40, width)
|
||||
lbl_bord = Label(wri, 20, 40, width)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('Red')
|
||||
lbl_var = Label(wri, 40, 2, '25.46', fgcolor=RED, bdcolor=RED)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('Yellow')
|
||||
lbl_var.value(bdcolor=YELLOW)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Red')
|
||||
lbl_bord.value('None')
|
||||
lbl_var.value(bdcolor=False)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Yellow')
|
||||
lbl_bord.value('None')
|
||||
lbl_var.value(fgcolor=YELLOW)
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
lbl_text.value('Blue')
|
||||
lbl_bord.value('Green')
|
||||
lbl_var.value('18.99', fgcolor=BLUE, bdcolor=GREEN)
|
||||
refresh(ssd)
|
||||
|
||||
print('Color display test is running.')
|
||||
meter()
|
||||
multi_fields(t = 10)
|
||||
vari_fields()
|
||||
print('Test complete.')
|
|
@ -0,0 +1,308 @@
|
|||
# Code generated by font-to-py.py.
|
||||
# Font: Courier Prime.ttf
|
||||
version = '0.2'
|
||||
|
||||
def height():
|
||||
return 20
|
||||
|
||||
def max_width():
|
||||
return 14
|
||||
|
||||
def hmap():
|
||||
return True
|
||||
|
||||
def reverse():
|
||||
return False
|
||||
|
||||
def monospaced():
|
||||
return True
|
||||
|
||||
def min_ch():
|
||||
return 32
|
||||
|
||||
def max_ch():
|
||||
return 126
|
||||
|
||||
_font =\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x7c\x00\xfe\x00\xc7\x00\xc3\x00\x03\x00'\
|
||||
b'\x07\x00\x1e\x00\x18\x00\x18\x00\x18\x00\x3c\x00\x3c\x00\x18\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x60\x00\x60\x00\x60\x00'\
|
||||
b'\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00\x00\x00\x60\x00\xf0\x00'\
|
||||
b'\xf0\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\xe6\x00\xe6\x00\x66\x00\x66\x00\x66\x00\x66\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x03\x30\x02\x20'\
|
||||
b'\x02\x20\x06\x60\x3f\xf8\x3f\xf8\x0c\xc0\x08\x80\x7f\xf0\xff\xf0'\
|
||||
b'\x19\x80\x11\x00\x33\x00\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x0c\x00\x0c\x00\x3d\x80\x7f\x80\xcd\x80\xcc\x80'\
|
||||
b'\xec\x00\x7f\x00\x0f\x80\x0c\xc0\xcc\xc0\xcd\xc0\xff\x80\xcf\x00'\
|
||||
b'\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x38\x00\xfe\x18\xc6\x30\xc6\x60\xfe\xc0\x39\x80\x03\x00'\
|
||||
b'\x06\x70\x1d\xfc\x39\x8c\x71\x8c\x61\xfc\x00\x70\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1e\x00\x3f\x00'\
|
||||
b'\x63\x00\x63\x00\x60\x00\x30\x00\x31\xc0\x49\xc0\xc7\x00\xc3\x00'\
|
||||
b'\xe3\x00\x7f\xc0\x39\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00'\
|
||||
b'\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x04\x00\x0e\x00'\
|
||||
b'\x18\x00\x30\x00\x30\x00\x60\x00\x60\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\x60\x00\x60\x00\x30\x00\x38\x00\x1c\x00'\
|
||||
b'\x0e\x00\x04\x00\x0e\x00\x40\x00\xe0\x00\x30\x00\x18\x00\x18\x00'\
|
||||
b'\x0c\x00\x0c\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00'\
|
||||
b'\x0c\x00\x0c\x00\x18\x00\x38\x00\x70\x00\xe0\x00\x80\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x0c\x00\x0c\x00\x0c\x00\xed\xc0\x7f\x80\x0c\x00'\
|
||||
b'\x1e\x00\x33\x00\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\xff\xc0\xff\xc0\x0c\x00\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x70\x00'\
|
||||
b'\x60\x00\x60\x00\xc0\x00\xc0\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\xc0'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00'\
|
||||
b'\xf0\x00\xf0\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\xc0\x01\x80\x01\x80\x03\x00\x03\x00\x02\x00\x06\x00'\
|
||||
b'\x04\x00\x0c\x00\x0c\x00\x18\x00\x18\x00\x30\x00\x30\x00\x20\x00'\
|
||||
b'\x60\x00\x40\x00\xc0\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x1e\x00\x3f\x00\x61\x80\xe1\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc0\xc0\xe1\xc0\x61\x80\x3f\x00\x1e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x0c\x00\x7c\x00\xec\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x3e\x00\xff\x00\xc3\x80\xc1\x80\x01\x80\x01\x00'\
|
||||
b'\x02\x00\x04\x00\x08\x00\x11\x80\x21\x80\xff\x80\xff\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3e\x00'\
|
||||
b'\x7f\x00\x61\x80\x61\x80\x01\x80\x1f\x00\x1f\x00\x03\x80\x01\x80'\
|
||||
b'\x01\x80\xc3\x80\xff\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\x03\x00\x07\x00\x0b\x00\x1b\x00'\
|
||||
b'\x13\x00\x23\x00\x63\x00\xff\xc0\xff\xe0\x03\x00\x03\x00\x0f\xc0'\
|
||||
b'\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x7f\x80\x7f\x80\x60\x00\x60\x00\x7e\x00\x7f\x00\x63\x80'\
|
||||
b'\x01\x80\x01\x80\x01\x80\xc3\x80\xff\x00\x3c\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x07\x80\x1f\x80'\
|
||||
b'\x3c\x00\x70\x00\x60\x00\xcf\x00\xff\x80\xe1\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\x61\xc0\x7f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\xff\x80\xff\x80\xc1\x80\xc1\x00\x03\x00'\
|
||||
b'\x02\x00\x06\x00\x06\x00\x0c\x00\x0c\x00\x08\x00\x18\x00\x10\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x1e\x00\x3f\x00\x61\x80\x61\x80\x73\x80\x3f\x00\x7f\x00\xe3\x80'\
|
||||
b'\xc1\x80\xc1\x80\xe3\x80\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3e\x00\x7f\x80\xe1\x80'\
|
||||
b'\xc0\xc0\xc0\xc0\xe1\xc0\x7f\xc0\x3c\xc0\x01\x80\x03\x80\x0f\x00'\
|
||||
b'\x7e\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\xf0\x00\xf0\x00'\
|
||||
b'\x60\x00\x00\x00\x00\x00\x60\x00\xf0\x00\xf0\x00\x60\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x30\x00\x78\x00\x78\x00\x30\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x78\x00\x70\x00\x60\x00\x60\x00\xc0\x00\xc0\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x60\x01\xe0\x07\x80'\
|
||||
b'\x1e\x00\x78\x00\xe0\x00\x78\x00\x0e\x00\x03\x80\x00\xe0\x00\x40'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00'\
|
||||
b'\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x80\x00'\
|
||||
b'\xe0\x00\x38\x00\x0e\x00\x03\xc0\x00\xc0\x03\x80\x0e\x00\x38\x00'\
|
||||
b'\xe0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x7c\x00\xfe\x00\xc7\x00\xc3\x00\x03\x00'\
|
||||
b'\x07\x00\x1e\x00\x18\x00\x18\x00\x18\x00\x3c\x00\x3c\x00\x18\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0f\x80\x1f\xc0\x38\xe0\x66\xf0\x6f\xb0\xcd\x30\xd9\x30'\
|
||||
b'\xd9\x30\xdb\x70\xdf\xe0\x6c\xc0\x70\x00\x3f\xc0\x0f\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3f\x00\x3f\x00\x07\x80'\
|
||||
b'\x0c\x80\x0c\x80\x18\xc0\x18\x40\x3f\xe0\x3f\xe0\x30\x60\x60\x30'\
|
||||
b'\xf8\xf8\xf8\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\xff\x00\xff\xc0\x30\xc0\x30\xc0\x30\xc0\x3f\x80'\
|
||||
b'\x3f\xc0\x30\xe0\x30\x60\x30\x60\x30\xe0\xff\xc0\xff\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1f\x60'\
|
||||
b'\x3f\xe0\x70\xe0\x60\x60\xc0\x60\xc0\x40\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\x60\x00\x70\x60\x3f\xe0\x1f\x80\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\xff\x00\xff\x80\x31\xc0\x30\xe0'\
|
||||
b'\x30\x60\x30\x60\x30\x60\x30\x60\x30\x60\x30\xe0\x31\xc0\xff\x80'\
|
||||
b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\xff\xe0\xff\xe0\x30\x60\x33\x60\x33\x00\x3f\x00\x3f\x00'\
|
||||
b'\x33\x00\x33\x00\x30\x60\x30\x60\xff\xe0\xff\xe0\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xe0\xff\xe0'\
|
||||
b'\x30\x60\x33\x60\x33\x00\x3f\x00\x3f\x00\x33\x00\x33\x00\x30\x00'\
|
||||
b'\x30\x00\xfe\x00\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x1e\xc0\x3f\xc0\x71\xc0\x60\xc0\xc0\xc0'\
|
||||
b'\xc0\x00\xc0\x00\xc7\xf0\xc7\xf0\x60\xc0\x70\xc0\x3f\xc0\x1f\x80'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\xfd\xf8\xfd\xf8\x30\x60\x30\x60\x30\x60\x3f\xe0\x3f\xe0\x30\x60'\
|
||||
b'\x30\x60\x30\x60\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x3f\xf8\x3f\xf8\x01\x80\x01\x80\x01\x80\x01\x80'\
|
||||
b'\x81\x80\xc1\x80\xc1\x80\xc1\x80\xc3\x80\x7f\x00\x3e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xfc\xf0'\
|
||||
b'\xfc\xf0\x30\x40\x31\x80\x33\x00\x34\x00\x3f\x00\x31\x80\x30\x80'\
|
||||
b'\x30\xc0\x30\x40\xfc\x70\xfc\x30\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\x7f\x00\xff\x00\x18\x00\x18\x00'\
|
||||
b'\x18\x00\x18\x00\x18\x00\x18\x30\x18\x30\x18\x30\x18\x30\x7f\xf0'\
|
||||
b'\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\xf0\xf0\xf0\xf0\x70\xe0\x79\xe0\x69\x60\x69\x60\x6f\x60'\
|
||||
b'\x66\x60\x66\x60\x66\x60\x60\x60\xf9\xf0\xf9\xf0\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xf0\xf8\xf8\xf8'\
|
||||
b'\x3c\x30\x34\x30\x32\x30\x32\x30\x31\x30\x31\x30\x30\xb0\x30\x70'\
|
||||
b'\x30\x70\x7c\x30\x7c\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0\x70\xe0\x60\x60\xc0\x30'\
|
||||
b'\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x60\x60\x70\xe0\x3f\xc0\x1f\x80'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x7f\xc0\xff\xe0\x18\x70\x18\x30\x18\x30\x18\x70\x1f\xe0\x1f\xc0'\
|
||||
b'\x18\x00\x18\x00\x18\x00\x7f\x00\xff\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0\x70\xe0'\
|
||||
b'\x60\x60\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x60\x60\x70\xe0'\
|
||||
b'\x3f\xc0\x0f\x80\x18\x20\x3f\xe0\x3f\xc0\x20\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\xff\x80\xff\xc0\x30\xe0\x30\x60\x30\xe0\x3f\xc0'\
|
||||
b'\x3f\x00\x31\x80\x30\xc0\x30\xc0\x30\x60\xfc\x78\xfc\x38\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3c\xc0'\
|
||||
b'\x7f\xc0\xe1\xc0\xc0\xc0\xc0\x00\x70\x00\x1f\x00\x01\x80\x00\xc0'\
|
||||
b'\xc0\xc0\xe1\xc0\xff\x80\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\xcc\xc0\xcc\xc0'\
|
||||
b'\xcc\xc0\xcc\xc0\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x7f\x80'\
|
||||
b'\x7f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\xfc\xfc\xfc\xfc\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30'\
|
||||
b'\x30\x30\x30\x30\x30\x30\x38\x70\x1f\xe0\x0f\xc0\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xfc\x7c\xfc\x7c'\
|
||||
b'\x30\x30\x30\x30\x10\x20\x18\x60\x18\x40\x0c\xc0\x0c\xc0\x04\x80'\
|
||||
b'\x07\x80\x07\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\xf8\x7c\xf8\x7c\x60\x18\x63\x18\x23\x10'\
|
||||
b'\x23\x90\x37\x90\x37\xb0\x34\xb0\x3c\xf0\x1c\xe0\x18\x60\x18\x60'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\xf9\xf0\xf9\xf0\x30\xc0\x19\x80\x1f\x80\x0f\x00\x06\x00\x0f\x00'\
|
||||
b'\x19\x80\x30\xc0\x60\x60\xf9\xf0\xf9\xf0\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xf9\xf0\xf9\xf0\x30\xc0'\
|
||||
b'\x30\xc0\x19\x80\x0f\x00\x0f\x00\x06\x00\x06\x00\x06\x00\x06\x00'\
|
||||
b'\x3f\xc0\x3f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\xff\xc0\xff\xc0\xc1\x80\xc3\x00\x02\x00\x04\x00'\
|
||||
b'\x0c\x00\x18\x00\x10\xc0\x20\xc0\x40\xc0\xff\xc0\xff\xc0\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\xfc\x00\xfc\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfc\x00\xfc\x00'\
|
||||
b'\x00\x00\x0e\x00\xc0\x00\x40\x00\x60\x00\x20\x00\x30\x00\x30\x00'\
|
||||
b'\x18\x00\x18\x00\x0c\x00\x0c\x00\x04\x00\x06\x00\x02\x00\x03\x00'\
|
||||
b'\x03\x00\x01\x80\x01\x80\x00\xc0\x00\x00\x00\x00\x0e\x00\xfc\x00'\
|
||||
b'\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\xfc\x00\xfc\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x18\x00\x18\x00'\
|
||||
b'\x3c\x00\x24\x00\x66\x00\xc6\x00\xc3\x00\x83\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\xff\xfc\xff\xfc\x00\x00\x00\x00\x00\x00\x0e\x00\xe0\x00\xf8\x00'\
|
||||
b'\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x3e\x00\xff\x00\xc1\x80\x3f\x80\x7f\x80\xc1\x80\xc1\x80\xc3\x80'\
|
||||
b'\xff\x80\x7c\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\xf0\x00\xf0\x00\x30\x00\x30\x00\x30\x00\x37\xc0\x3f\xe0\x38\x60'\
|
||||
b'\x30\x30\x30\x30\x30\x30\x30\x30\x38\x60\xff\xe0\xf3\xc0\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x1f\x60\x7f\xe0\x70\xe0\xc0\x60\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\x70\x60\x7f\xc0\x1f\x80\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x03\xc0\x03\xc0\x00\xc0\x00\xc0\x00\xc0\x3e\xc0'\
|
||||
b'\x7f\xc0\x61\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61\xc0\x7f\xf0'\
|
||||
b'\x3c\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x7f\x80\x60\xc0\xff\xc0'\
|
||||
b'\xff\xc0\xc0\x00\xc0\x00\x60\xc0\x7f\xc0\x1f\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x0f\x80\x1f\xc0\x38\x40\x30\x00'\
|
||||
b'\x30\x00\xff\x80\xff\x80\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00'\
|
||||
b'\x30\x00\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x78\x7f\xf8'\
|
||||
b'\x60\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\x60\xe0\x7f\xe0\x1e\x60'\
|
||||
b'\x00\x60\x20\xe0\x3f\xc0\x1f\x80\x00\x00\x0e\x00\xf0\x00\xf0\x00'\
|
||||
b'\x30\x00\x30\x00\x30\x00\x37\xc0\x3f\xe0\x3c\x60\x38\x60\x30\x60'\
|
||||
b'\x30\x60\x30\x60\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x0c\x00\x0c\x00\x0c\x00\x00\x00\x00\x00'\
|
||||
b'\x7c\x00\x7c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x03\x00\x03\x00\x03\x00\x00\x00\x00\x00\x7f\x00\x7f\x00\x03\x00'\
|
||||
b'\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00'\
|
||||
b'\xc3\x00\xfe\x00\x3c\x00\x00\x00\x0e\x00\xf0\x00\xf0\x00\x30\x00'\
|
||||
b'\x30\x00\x30\x00\x33\xe0\x33\xe0\x31\x80\x33\x00\x34\x00\x3a\x00'\
|
||||
b'\x31\x80\x30\xc0\xfd\xf0\xfd\xf0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x7c\x00\x7c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\xff\xc0'\
|
||||
b'\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x70\xff\xf8\x3b\x98\x33\x18'\
|
||||
b'\x33\x18\x33\x18\x33\x18\x33\x18\xfb\x9c\xfb\x9c\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\xf3\xc0\xf7\xe0\x38\x60\x38\x60\x30\x60\x30\x60\x30\x60'\
|
||||
b'\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0'\
|
||||
b'\x70\xe0\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x70\xe0\x3f\xc0\x1f\x80'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xf7\xc0\xff\xe0\x38\x60\x30\x30\x30\x30'\
|
||||
b'\x30\x30\x30\x30\x38\x60\x3f\xe0\x37\xc0\x30\x00\x30\x00\xfc\x00'\
|
||||
b'\xfc\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x3e\xf0\x7f\xf0\x61\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61\xc0'\
|
||||
b'\x7f\xc0\x3c\xc0\x00\xc0\x00\xc0\x03\xf0\x03\xf0\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xc0\xf7\xe0\x3e\x40'\
|
||||
b'\x38\x00\x30\x00\x30\x00\x30\x00\x30\x00\xff\x00\xff\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x7d\x80\xff\x80\xc1\x80\xc0\x80\x7c\x00\x03\x80'\
|
||||
b'\xc0\xc0\xe1\xc0\xff\xc0\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\x30\x00\x30\x00\x30\x00\x30\x00\xff\x80'\
|
||||
b'\xff\x80\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\xc0\x1f\xc0'\
|
||||
b'\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xe0\xf1\xe0\x30\x60\x30\x60'\
|
||||
b'\x30\x60\x30\x60\x30\xe0\x30\xe0\x3f\xf8\x1e\x78\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\xf8\xf0\xf9\xf0\x20\x60\x30\xc0\x10\xc0\x18\x80\x09\x80'\
|
||||
b'\x0d\x00\x0f\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3c\xf8\x3c'\
|
||||
b'\x63\x18\x63\x18\x27\x90\x37\xb0\x34\xb0\x1c\xe0\x1c\xe0\x18\x60'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xf9\xf0\xf9\xf0\x30\xc0\x19\x80\x0f\x00'\
|
||||
b'\x0f\x00\x19\x80\x30\xc0\xf9\xf0\xf9\xf0\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\xf8\xf0\xf9\xf0\x30\x60\x30\xc0\x18\xc0\x19\x80\x0d\x80\x07\x00'\
|
||||
b'\x07\x00\x06\x00\x0c\x00\x1c\x00\x78\x00\x70\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\x80\xc1\x00'\
|
||||
b'\xc2\x00\x04\x00\x18\x00\x30\xc0\x60\xc0\xff\xc0\xff\xc0\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x06\x00\x0e\x00\x18\x00'\
|
||||
b'\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x38\x00\xf0\x00\xf0\x00'\
|
||||
b'\x38\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x1e\x00\x0e\x00'\
|
||||
b'\x00\x00\x0e\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0e\x00\xc0\x00'\
|
||||
b'\xe0\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x38\x00'\
|
||||
b'\x1e\x00\x1e\x00\x38\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00'\
|
||||
b'\xf0\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x78\x40\xff\xc0\x87\x80\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
|
||||
_index =\
|
||||
b'\x00\x00\x2a\x00\x54\x00\x7e\x00\xa8\x00\xd2\x00\xfc\x00\x26\x01'\
|
||||
b'\x50\x01\x7a\x01\xa4\x01\xce\x01\xf8\x01\x22\x02\x4c\x02\x76\x02'\
|
||||
b'\xa0\x02\xca\x02\xf4\x02\x1e\x03\x48\x03\x72\x03\x9c\x03\xc6\x03'\
|
||||
b'\xf0\x03\x1a\x04\x44\x04\x6e\x04\x98\x04\xc2\x04\xec\x04\x16\x05'\
|
||||
b'\x40\x05\x6a\x05\x94\x05\xbe\x05\xe8\x05\x12\x06\x3c\x06\x66\x06'\
|
||||
b'\x90\x06\xba\x06\xe4\x06\x0e\x07\x38\x07\x62\x07\x8c\x07\xb6\x07'\
|
||||
b'\xe0\x07\x0a\x08\x34\x08\x5e\x08\x88\x08\xb2\x08\xdc\x08\x06\x09'\
|
||||
b'\x30\x09\x5a\x09\x84\x09\xae\x09\xd8\x09\x02\x0a\x2c\x0a\x56\x0a'\
|
||||
b'\x80\x0a\xaa\x0a\xd4\x0a\xfe\x0a\x28\x0b\x52\x0b\x7c\x0b\xa6\x0b'\
|
||||
b'\xd0\x0b\xfa\x0b\x24\x0c\x4e\x0c\x78\x0c\xa2\x0c\xcc\x0c\xf6\x0c'\
|
||||
b'\x20\x0d\x4a\x0d\x74\x0d\x9e\x0d\xc8\x0d\xf2\x0d\x1c\x0e\x46\x0e'\
|
||||
b'\x70\x0e\x9a\x0e\xc4\x0e\xee\x0e\x18\x0f\x42\x0f\x6c\x0f\x96\x0f'\
|
||||
b'\xc0\x0f'
|
||||
|
||||
_mvfont = memoryview(_font)
|
||||
|
||||
def _chr_addr(ordch):
|
||||
offset = 2 * (ordch - 32)
|
||||
return int.from_bytes(_index[offset:offset + 2], 'little')
|
||||
|
||||
def get_ch(ch):
|
||||
ordch = ord(ch)
|
||||
ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32
|
||||
offset = _chr_addr(ordch)
|
||||
width = int.from_bytes(_font[offset:offset + 2], 'little')
|
||||
next_offs = _chr_addr(ordch +1)
|
||||
return _mvfont[offset + 2:next_offs], 20, width
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
# Adafruit and other OLED displays
|
||||
|
||||
###### [Main README](../README.md)
|
||||
|
||||
# SPI Pin names and wiring
|
||||
|
||||
The names used on the Pyboard are the correct names for SPI signals. Some OLED
|
||||
displays use different names. Adafruit use abbreviated names where space is at
|
||||
a premium. The following table shows the correct names followed by others I
|
||||
have seen. The column labelled "Adafruit" references pin numbers on the 1.27
|
||||
and 1.5 inch displays. Pin numbering on the 0.96 inch display differs: pin
|
||||
names are as below (SCK is CLK on this unit).
|
||||
|
||||
Pyboard pins are for SPI(1). Adapt for SPI(2) or other hardware.
|
||||
|
||||
| Pin | Pyboard | Display | Adafruit | Alternative names |
|
||||
|:---:|:-------:|:-------:|:--------:|:---------:|
|
||||
| 3V3 | 3V3 | | Vin (10) | |
|
||||
| Gnd | Gnd | | Gnd (11) | |
|
||||
| X1 | X1 | | DC (3) | |
|
||||
| X2 | X2 | | CS (5) | OC OLEDCS |
|
||||
| X3 | X3 | | Rst (4) | R RESET |
|
||||
| X6 | SCK | SCK | CL (2) | SCK CLK |
|
||||
| X8 | MOSI | MOSI | SI (1) | DATA SI |
|
||||
| X7 | MISO | MISO | SO (7) | MISO (see below) |
|
||||
| X21 | X21 | | SC (6) | SDCS (see below) |
|
||||
|
||||
The last two pins above are specific to Adafruit 1.27 and 1.5 inch displays and
|
||||
only need to be connected if the SD card is to be used. The pin labelled CD on
|
||||
those displays is a card detect signal; it can be ignored. The pin labelled 3Vo
|
||||
is an output: these displays can be powered from +5V.
|
||||
|
||||
Pyboard pins are arbitrary with the exception of MOSI, SCK and MISO. These can
|
||||
be changed if software SPI is used.
|
||||
|
||||
# I2C pin names and wiring
|
||||
|
||||
I2C is generally only available on monochrome displays. Monochrome OLED panels
|
||||
typically use the SSD1306 chip which is
|
||||
[officially supported](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py).
|
||||
At the time of writing (Sept 2018) this works only with software SPI. See
|
||||
[this issue](https://github.com/micropython/micropython/pull/4020). Wiring
|
||||
details:
|
||||
|
||||
| Pin | Pyboard | Display |
|
||||
|:---:|:-------:|:-------:|
|
||||
| 3V3 | 3V3 | Vin |
|
||||
| Gnd | Gnd | Gnd |
|
||||
| Y9 | SCL | CLK |
|
||||
| Y10 | SDA | DATA |
|
||||
|
||||
Typical initialisation on a Pyboard:
|
||||
```python
|
||||
pscl = machine.Pin('Y9', machine.Pin.OPEN_DRAIN)
|
||||
psda = machine.Pin('Y10', machine.Pin.OPEN_DRAIN)
|
||||
i2c = machine.I2C(scl=pscl, sda=psda)
|
||||
```
|
||||
|
||||
# Adafruit - use of the onboard SD card
|
||||
|
||||
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.
|
||||
|
||||
# Hardware note: SPI clock rate
|
||||
|
||||
For performance reasons the drivers for the Adafruit color displays run the SPI
|
||||
bus at a high rate (currently 10.5MHz). Leads should be short and direct. An
|
||||
attempt to use 21MHz failed. The datasheet limit is 20MHz. Whether a 5%
|
||||
overclock caused this is moot: with very short leads or a PCB this might well
|
||||
work. Note that the Pyboard hardware SPI supports only 10.5MHz and 21MHz.
|
||||
|
||||
In practice the 41ms update time is visually fast for most purposes except some
|
||||
games.
|
||||
|
||||
# Notes on OLED displays
|
||||
|
||||
## Power consumption
|
||||
|
||||
The power consumption of OLED displays is roughly proportional to the number
|
||||
and brightness of illuminated pixels. I tested a 1.27 inch Adafruit display
|
||||
running the `clock.py` demo. It consumed 19.7mA. Initial current with screen
|
||||
blank was 3.3mA.
|
||||
|
||||
## Wearout
|
||||
|
||||
OLED displays suffer gradual loss of luminosity over long periods of
|
||||
illumination. Wikipedia refers to 15,000 hours for significant loss, which
|
||||
equates to 1.7 years of 24/7 usage. However it also refers to fabrication
|
||||
techniques which ameliorate this which implies the likelihood of better
|
||||
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)
|
|
@ -0,0 +1,92 @@
|
|||
# SSD1331.py MicroPython driver for Adafruit 0.96" OLED display
|
||||
# https://www.adafruit.com/product/684
|
||||
|
||||
# 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.
|
||||
|
||||
# Show command
|
||||
# 0x15, 0, 0x5f, 0x75, 0, 0x3f Col 0-95 row 0-63
|
||||
|
||||
# Initialisation command
|
||||
# 0xae display off (sleep mode)
|
||||
# 0xa0, 0x32 256 color RGB, horizontal RAM increment
|
||||
# 0xa1, 0x00 Startline row 0
|
||||
# 0xa2, 0x00 Vertical offset 0
|
||||
# 0xa4 Normal display
|
||||
# 0xa8, 0x3f Set multiplex ratio
|
||||
# 0xad, 0x8e Ext supply
|
||||
# 0xb0, 0x0b Disable power save mode
|
||||
# 0xb1, 0x31 Phase period
|
||||
# 0xb3, 0xf0 Oscillator frequency
|
||||
# 0x8a, 0x64, 0x8b, 0x78, 0x8c, 0x64, # Precharge
|
||||
# 0xbb, 0x3a Precharge voltge
|
||||
# 0xbe, 0x3e COM deselect level
|
||||
# 0x87, 0x06 master current attenuation factor
|
||||
# 0x81, 0x91 contrast for all color "A" segment
|
||||
# 0x82, 0x50 contrast for all color "B" segment
|
||||
# 0x83, 0x7d contrast for all color "C" segment
|
||||
# 0xaf Display on
|
||||
|
||||
import framebuf
|
||||
import utime
|
||||
import gc
|
||||
|
||||
class SSD1331(framebuf.FrameBuffer):
|
||||
# Convert r, g, b in range 0-255 to an 8 bit colour value
|
||||
# acceptable to hardware: rrrgggbb
|
||||
@staticmethod
|
||||
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
|
||||
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.
|
||||
gc.collect()
|
||||
self.buffer = bytearray(self.height * self.width)
|
||||
super().__init__(self.buffer, self.width, self.height, self.mode)
|
||||
pinrs(0) # Pulse the reset line
|
||||
utime.sleep_ms(1)
|
||||
pinrs(1)
|
||||
utime.sleep_ms(1)
|
||||
self._write(b'\xae\xa0\x32\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)
|
||||
gc.collect()
|
||||
self.show()
|
||||
|
||||
def _write(self, buf, dc):
|
||||
self.spi.init(baudrate=self.rate, polarity=1, phase=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
|
||||
self._write(_cmd, 0)
|
||||
self._write(self.buffer, 1)
|
|
@ -0,0 +1,20 @@
|
|||
# Drivers for SSD1351
|
||||
|
||||
There are two versions.
|
||||
* `ssd1351.py` This is optimised for STM (e.g. Pyboard) platforms.
|
||||
* `ssd1351_generic.py` Cross-platform version.
|
||||
|
||||
The cross-platform version includes the `micropythn.viper` decorator. If your
|
||||
platform does not support this, comment it out and remove the type annotations.
|
||||
You may be able to use the native decorator.
|
||||
|
||||
If the platform supports the viper emitter performance should still be good: on
|
||||
a Pyboard V1 this driver perorms a refresh of a 128*128 color display in 47ms.
|
||||
The STM version is faster but not by a large margin: a refresh takes 41ms. 32ms
|
||||
of these figures is consumed by the data transfer over the SPI interface.
|
||||
|
||||
If the viper and native decorators are unsupported a screen redraw takes 272ms
|
||||
(on Pyboard 1.0) which is visibly slow.
|
||||
|
||||
This driver was tested on official Adafruit 1.5 and 1.27 inch displays, also a
|
||||
Chinese 1.5 inch unit.
|
|
@ -0,0 +1,160 @@
|
|||
# SSD1351.py MicroPython driver for Adafruit color OLED displays.
|
||||
# STM (Pyboard etc) version. Display refresh takes 41ms on Pyboard V1.0
|
||||
|
||||
# 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
|
||||
# For wiring details see drivers/ADAFRUIT.md in this repo.
|
||||
|
||||
# This driver is based on the Adafruit C++ library for Arduino
|
||||
# https://github.com/adafruit/Adafruit-SSD1351-library.git
|
||||
|
||||
# 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.
|
||||
|
||||
import framebuf
|
||||
import utime
|
||||
import gc
|
||||
import micropython
|
||||
from uctypes import addressof
|
||||
|
||||
# Timings with standard emitter
|
||||
# 1.86ms * 128 lines = 240ms. copy dominates: show() took 272ms
|
||||
# Buffer transfer time = 272-240 = 32ms which accords with expected:
|
||||
# 128*128*2/10500000 = 31.2ms (2 bytes/pixel, baudrate = 10.5MHz)
|
||||
# With assembler .show() takes 41ms
|
||||
|
||||
# Copy a buffer with 8 bit rrrgggbb pixels to a buffer of 16 bit pixels.
|
||||
@micropython.asm_thumb
|
||||
def _lcopy(r0, r1, r2): # r0 dest, r1 source, r2 no. of bytes
|
||||
label(LOOP)
|
||||
ldrb(r3, [r1, 0]) # Get source byte to r3, r5, r6
|
||||
mov(r5, r3)
|
||||
mov(r6, r3)
|
||||
mov(r4, 3)
|
||||
and_(r3, r4)
|
||||
mov(r4, 6)
|
||||
lsl(r3, r4)
|
||||
mov(r4, 0x1c)
|
||||
and_(r5, r4)
|
||||
mov(r4, 2)
|
||||
lsr(r5, r4)
|
||||
orr(r3, r5)
|
||||
strb(r3, [r0, 0])
|
||||
mov(r4, 0xe0)
|
||||
and_(r6, r4)
|
||||
mov(r4, 2)
|
||||
lsr(r6, r4)
|
||||
strb(r6, [r0, 1])
|
||||
add(r0, 2)
|
||||
add(r1, 1)
|
||||
sub(r2, 1)
|
||||
bne(LOOP)
|
||||
|
||||
# Initialisation commands in cmd_init:
|
||||
# 0xfd, 0x12, 0xfd, 0xb1, # Unlock command mode
|
||||
# 0xae, # display off (sleep mode)
|
||||
# 0xb3, 0xf1, # clock div
|
||||
# 0xca, 0x7f, # mux ratio
|
||||
# 0xa0, 0x74, # setremap 0x74
|
||||
# 0x15, 0, 0x7f, # setcolumn
|
||||
# 0x75, 0, 0x7f, # setrow
|
||||
# 0xa1, 0, # set display start line
|
||||
# 0xa2, 0, # displayoffset
|
||||
# 0xb5, 0, # setgpio
|
||||
# 0xab, 1, # functionselect: serial interface, internal Vdd regulator
|
||||
# 0xb1, 0x32, # Precharge
|
||||
# 0xbe, 0x05, # vcommh
|
||||
# 0xa6, # normaldisplay
|
||||
# 0xc1, 0xc8, 0x80, 0xc8, # contrast abc
|
||||
# 0xc7, 0x0f, # Master contrast
|
||||
# 0xb4, 0xa0, 0xb5, 0x55, # set vsl (see datasheet re ext circuit)
|
||||
# 0xb6, 1, # Precharge 2
|
||||
# 0xaf, # Display on
|
||||
|
||||
# SPI baudrate: Pyboard can produce 10.5MHz or 21MHz. Datasheet gives max of 20MHz.
|
||||
# Attempt to use 21MHz failed but might work on a PCB or with very short leads.
|
||||
class SSD1351(framebuf.FrameBuffer):
|
||||
# Convert r, g, b in range 0-255 to an 8 bit colour value
|
||||
# acceptable to hardware: rrrgggbb
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
|
||||
|
||||
def __init__(self, spi, pincs, pindc, pinrs, height=128, width=128):
|
||||
if height not in (96, 128):
|
||||
raise ValueError('Unsupported height {}'.format(height))
|
||||
self.spi = spi
|
||||
self.rate = 11000000 # See baudrate note above.
|
||||
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.
|
||||
gc.collect()
|
||||
self.buffer = bytearray(self.height * self.width)
|
||||
super().__init__(self.buffer, self.width, self.height, self.mode)
|
||||
self.linebuf = bytearray(self.width * 2)
|
||||
pinrs(0) # Pulse the reset line
|
||||
utime.sleep_ms(1)
|
||||
pinrs(1)
|
||||
utime.sleep_ms(1)
|
||||
# See above comment to explain this allocation-saving gibberish.
|
||||
self._write(b'\xfd\x12\xfd\xb1\xae\xb3\xf1\xca\x7f\xa0\x74'\
|
||||
b'\x15\x00\x7f\x75\x00\x7f\xa1\x00\xa2\x00\xb5\x00\xab\x01'\
|
||||
b'\xb1\x32\xbe\x05\xa6\xc1\xc8\x80\xc8\xc7\x0f'\
|
||||
b'\xb4\xa0\xb5\x55\xb6\x01\xaf', 0)
|
||||
self.show()
|
||||
gc.collect()
|
||||
|
||||
def _write(self, buf, dc):
|
||||
self.spi.init(baudrate=self.rate, polarity=1, phase=1)
|
||||
self.pincs(1)
|
||||
self.pindc(dc)
|
||||
self.pincs(0)
|
||||
self.spi.write(buf)
|
||||
self.pincs(1)
|
||||
|
||||
# Write lines from the framebuf out of order to match the mapping of the
|
||||
# SSD1351 RAM to the OLED device.
|
||||
def show(self):
|
||||
lb = self.linebuf
|
||||
buf = self.buffer
|
||||
self._write(b'\x5c', 0) # Enable data write
|
||||
if self.height == 128:
|
||||
for l in range(128):
|
||||
l0 = (95 - l) % 128 # 95 94 .. 1 0 127 126 .. 96
|
||||
start = l0 * self.width
|
||||
_lcopy(lb, addressof(buf) + start, self.width)
|
||||
self._write(lb, 1) # Send a line
|
||||
else:
|
||||
for l in range(128):
|
||||
if l < 64:
|
||||
start = (63 -l) * self.width # 63 62 .. 1 0
|
||||
_lcopy(lb, addressof(buf) + start, self.width)
|
||||
self._write(lb, 1) # Send a line
|
||||
elif l < 96: # This is daft but I can't get setrow to work
|
||||
self._write(lb, 1) # Let RAM counter increase
|
||||
else:
|
||||
start = (191 - l) * self.width # 127 126 .. 95
|
||||
_lcopy(lb, addressof(buf) + start, self.width)
|
||||
self._write(lb, 1) # Send a line
|
|
@ -0,0 +1,141 @@
|
|||
# SSD1351_generic.py MicroPython driver for Adafruit color OLED displays.
|
||||
# This is cross-platform. It lacks STM optimisations and is slower than the
|
||||
# standard version.
|
||||
|
||||
# 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
|
||||
# For wiring details see drivers/ADAFRUIT.md in this repo.
|
||||
|
||||
# This driver is based on the Adafruit C++ library for Arduino
|
||||
# https://github.com/adafruit/Adafruit-SSD1351-library.git
|
||||
# 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.
|
||||
|
||||
import framebuf
|
||||
import utime
|
||||
import gc
|
||||
import micropython
|
||||
from uctypes import addressof
|
||||
|
||||
# Timings with standard emitter
|
||||
# 1.86ms * 128 lines = 240ms. copy dominates: show() took 272ms
|
||||
# Buffer transfer time = 272-240 = 32ms which accords with expected:
|
||||
# 128*128*2/10500000 = 31.2ms (2 bytes/pixel, baudrate = 10.5MHz)
|
||||
# With viper emitter show() takes 47ms vs 41ms for assembler.
|
||||
|
||||
@micropython.viper
|
||||
def _lcopy(dest:ptr8, source:ptr8, length:int):
|
||||
n = 0
|
||||
for x in range(length):
|
||||
c = source[x]
|
||||
dest[n] = ((c & 3) << 6) | ((c & 0x1c) >> 2) # Blue green
|
||||
n += 1
|
||||
dest[n] = (c & 0xe0) >> 3 # Red
|
||||
n += 1
|
||||
|
||||
# Initialisation commands in cmd_init:
|
||||
# 0xfd, 0x12, 0xfd, 0xb1, # Unlock command mode
|
||||
# 0xae, # display off (sleep mode)
|
||||
# 0xb3, 0xf1, # clock div
|
||||
# 0xca, 0x7f, # mux ratio
|
||||
# 0xa0, 0x74, # setremap 0x74
|
||||
# 0x15, 0, 0x7f, # setcolumn
|
||||
# 0x75, 0, 0x7f, # setrow
|
||||
# 0xa1, 0, # set display start line
|
||||
# 0xa2, 0, # displayoffset
|
||||
# 0xb5, 0, # setgpio
|
||||
# 0xab, 1, # functionselect: serial interface, internal Vdd regulator
|
||||
# 0xb1, 0x32, # Precharge
|
||||
# 0xbe, 0x05, # vcommh
|
||||
# 0xa6, # normaldisplay
|
||||
# 0xc1, 0xc8, 0x80, 0xc8, # contrast abc
|
||||
# 0xc7, 0x0f, # Master contrast
|
||||
# 0xb4, 0xa0, 0xb5, 0x55, # set vsl (see datasheet re ext circuit)
|
||||
# 0xb6, 1, # Precharge 2
|
||||
# 0xaf, # Display on
|
||||
|
||||
class SSD1351(framebuf.FrameBuffer):
|
||||
# Convert r, g, b in range 0-255 to an 8 bit colour value
|
||||
# acceptable to hardware: rrrgggbb
|
||||
@staticmethod
|
||||
def rgb(r, g, b):
|
||||
return (r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6)
|
||||
|
||||
def __init__(self, spi, pincs, pindc, pinrs, height=128, width=128):
|
||||
if height not in (96, 128):
|
||||
raise ValueError('Unsupported height {}'.format(height))
|
||||
self.spi = spi
|
||||
self.rate = 20000000 # Data sheet: should support 20MHz
|
||||
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.
|
||||
gc.collect()
|
||||
self.buffer = bytearray(self.height * self.width)
|
||||
super().__init__(self.buffer, self.width, self.height, self.mode)
|
||||
self.linebuf = bytearray(self.width * 2)
|
||||
pinrs(0) # Pulse the reset line
|
||||
utime.sleep_ms(1)
|
||||
pinrs(1)
|
||||
utime.sleep_ms(1)
|
||||
# See above comment to explain this allocation-saving gibberish.
|
||||
self._write(b'\xfd\x12\xfd\xb1\xae\xb3\xf1\xca\x7f\xa0\x74'\
|
||||
b'\x15\x00\x7f\x75\x00\x7f\xa1\x00\xa2\x00\xb5\x00\xab\x01'\
|
||||
b'\xb1\x32\xbe\x05\xa6\xc1\xc8\x80\xc8\xc7\x0f'\
|
||||
b'\xb4\xa0\xb5\x55\xb6\x01\xaf', 0)
|
||||
gc.collect()
|
||||
self.show()
|
||||
|
||||
def _write(self, buf, dc):
|
||||
self.spi.init(baudrate=self.rate, polarity=1, phase=1)
|
||||
self.pincs(1)
|
||||
self.pindc(dc)
|
||||
self.pincs(0)
|
||||
self.spi.write(buf)
|
||||
self.pincs(1)
|
||||
|
||||
# Write lines from the framebuf out of order to match the mapping of the
|
||||
# SSD1351 RAM to the OLED device.
|
||||
def show(self):
|
||||
lb = self.linebuf
|
||||
buf = memoryview(self.buffer)
|
||||
self._write(b'\x5c', 0) # Enable data write
|
||||
if self.height == 128:
|
||||
for l in range(128):
|
||||
l0 = (95 - l) % 128 # 95 94 .. 1 0 127 126...
|
||||
start = l0 * self.width
|
||||
_lcopy(lb, buf[start : start + self.width], self.width)
|
||||
self._write(lb, 1) # Send a line
|
||||
else:
|
||||
for l in range(128):
|
||||
if l < 64:
|
||||
start = (63 -l) * self.width
|
||||
_lcopy(lb, buf[start : start + self.width], self.width)
|
||||
self._write(lb, 1) # Send a line
|
||||
elif l < 96: # This is daft but I can't get setrow to work
|
||||
self._write(lb, 1) # Let RAM counter increase
|
||||
else:
|
||||
start = (191 - l) * self.width
|
||||
_lcopy(lb, buf[start : start + self.width], self.width)
|
||||
self._write(lb, 1) # Send a line
|
|
@ -0,0 +1,18 @@
|
|||
# test128_row.py Test for device driver on 96 row display
|
||||
import machine
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware
|
||||
def setup():
|
||||
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)
|
||||
ssd = SSD(spi, pcs, pdc, prst) # Create a display instance
|
||||
return ssd
|
||||
|
||||
ssd = setup()
|
||||
ssd.fill(0)
|
||||
ssd.line(0, 0, 127, 127, ssd.rgb(0, 255, 0))
|
||||
ssd.rect(0, 0, 15, 15, ssd.rgb(255, 0, 0))
|
||||
ssd.show()
|
|
@ -0,0 +1,18 @@
|
|||
# test96.py Test for device driver on 96 row display
|
||||
import machine
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware
|
||||
def setup():
|
||||
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)
|
||||
ssd = SSD(spi, pcs, pdc, prst, height=96) # Create a display instance
|
||||
return ssd
|
||||
|
||||
ssd = setup()
|
||||
ssd.fill(0)
|
||||
ssd.line(0, 0, 127, 95, ssd.rgb(0, 255, 0))
|
||||
ssd.rect(0, 0, 15, 15, ssd.rgb(255, 0, 0))
|
||||
ssd.show()
|
|
@ -0,0 +1,176 @@
|
|||
# Code generated by font-to-py.py.
|
||||
# Font: FreeSans.ttf
|
||||
version = '0.2'
|
||||
|
||||
def height():
|
||||
return 14
|
||||
|
||||
def max_width():
|
||||
return 14
|
||||
|
||||
def hmap():
|
||||
return True
|
||||
|
||||
def reverse():
|
||||
return False
|
||||
|
||||
def monospaced():
|
||||
return False
|
||||
|
||||
def min_ch():
|
||||
return 32
|
||||
|
||||
def max_ch():
|
||||
return 126
|
||||
|
||||
_font =\
|
||||
b'\x08\x00\x00\x78\x8c\x84\x04\x18\x30\x20\x20\x00\x20\x00\x00\x00'\
|
||||
b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x05\x00\x00\x80\x80\x80\x80\x80\x80\x80\x80\x00\x80\x00\x00\x00'\
|
||||
b'\x05\x00\x00\xa0\xa0\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x08\x00\x00\x00\x12\x14\x7f\x24\x24\xfe\x28\x48\x48\x00\x00\x00'\
|
||||
b'\x08\x00\x20\x78\xac\xa4\xa0\xa0\x78\x2c\xa4\xac\x78\x20\x00\x00'\
|
||||
b'\x0c\x00\x00\x00\x70\x80\x89\x00\x89\x00\x8a\x00\x72\x00\x04\xe0'\
|
||||
b'\x05\x10\x09\x10\x09\x10\x10\xe0\x00\x00\x00\x00\x00\x00\x09\x00'\
|
||||
b'\x00\x00\x30\x00\x48\x00\x48\x00\x78\x00\x20\x00\x52\x00\x9e\x00'\
|
||||
b'\x8c\x00\x8e\x00\x73\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x80'\
|
||||
b'\x80\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x20'\
|
||||
b'\x40\x40\x80\x80\x80\x80\x80\x80\x80\x40\x40\x20\x05\x00\x00\x80'\
|
||||
b'\x40\x40\x20\x20\x20\x20\x20\x20\x20\x40\x40\x80\x05\x00\x00\x20'\
|
||||
b'\xf8\x20\x50\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x20\x20\xf8\x20\x20\x20\x00\x00\x00\x04\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x80\x80\x80\x00\x05\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x04\x00\x00\x10'\
|
||||
b'\x10\x20\x20\x20\x40\x40\x40\x80\x80\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\x48\x84\x84\x84\x84\x84\x84\x48\x78\x00\x00\x00\x08\x00\x00\x20'\
|
||||
b'\x60\xe0\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\xcc\x84\x04\x0c\x18\x60\x40\x80\xfc\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\xc4\x84\x04\x38\x04\x04\x84\xcc\x78\x00\x00\x00\x08\x00\x00\x08'\
|
||||
b'\x18\x38\x28\x48\x88\xfc\x08\x08\x08\x00\x00\x00\x08\x00\x00\x7c'\
|
||||
b'\x80\x80\xb8\xcc\x04\x04\x04\x88\x78\x00\x00\x00\x08\x00\x00\x38'\
|
||||
b'\x48\x84\x80\xf8\xcc\x84\x84\x4c\x78\x00\x00\x00\x08\x00\x00\xfc'\
|
||||
b'\x0c\x08\x10\x10\x20\x20\x20\x40\x40\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\x84\x84\x84\x78\xcc\x84\x84\xcc\x78\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\xc8\x84\x84\xcc\x74\x04\x04\x88\x70\x00\x00\x00\x04\x00\x00\x00'\
|
||||
b'\x00\x80\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x04\x00\x00\x00'\
|
||||
b'\x00\x00\x80\x00\x00\x00\x00\x00\x80\x80\x80\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x1c\x70\x80\x60\x1c\x04\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\xfc\x00\xfc\x00\x00\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x00\x00\xe0\x38\x06\x1c\x60\x80\x00\x00\x00\x08\x00\x00\x78'\
|
||||
b'\x8c\x84\x04\x18\x30\x20\x20\x00\x20\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x07\xc0\x18\x60\x20\x10\x43\x48\x84\xc8\x88\xc8\x88\x88\x89\x90'\
|
||||
b'\xc6\xe0\x60\x00\x30\x00\x0f\xc0\x00\x00\x09\x00\x00\x00\x0c\x00'\
|
||||
b'\x1c\x00\x14\x00\x16\x00\x32\x00\x22\x00\x7f\x00\x41\x00\x41\x80'\
|
||||
b'\xc1\x80\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\xfc\x00\x82\x00'\
|
||||
b'\x82\x00\x82\x00\xfc\x00\x86\x00\x82\x00\x82\x00\x86\x00\xfc\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x3c\x00\x42\x00\x41\x00'\
|
||||
b'\x80\x00\x80\x00\x80\x00\x81\x00\xc1\x00\x62\x00\x3c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0a\x00\x00\x00\xfc\x00\x82\x00\x83\x00\x81\x00'\
|
||||
b'\x81\x00\x81\x00\x81\x00\x83\x00\x82\x00\xfc\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x09\x00\x00\x00\xfe\x00\x80\x00\x80\x00\x80\x00\xfc\x00'\
|
||||
b'\x80\x00\x80\x00\x80\x00\x80\x00\xfe\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x08\x00\x00\xfc\x80\x80\x80\xfc\x80\x80\x80\x80\x80\x00\x00\x00'\
|
||||
b'\x0b\x00\x00\x00\x1e\x00\x61\x00\x40\x80\x80\x00\x80\x00\x87\x80'\
|
||||
b'\x80\x80\xc0\x80\x61\x80\x3e\x80\x00\x00\x00\x00\x00\x00\x0a\x00'\
|
||||
b'\x00\x00\x81\x00\x81\x00\x81\x00\x81\x00\xff\x00\x81\x00\x81\x00'\
|
||||
b'\x81\x00\x81\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x80'\
|
||||
b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x07\x00\x00\x04'\
|
||||
b'\x04\x04\x04\x04\x04\x04\x84\x84\x78\x00\x00\x00\x09\x00\x00\x00'\
|
||||
b'\x82\x00\x84\x00\x88\x00\x90\x00\xb0\x00\xd8\x00\x88\x00\x84\x00'\
|
||||
b'\x86\x00\x82\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x80\x80\x80'\
|
||||
b'\x80\x80\x80\x80\x80\x80\xfc\x00\x00\x00\x0c\x00\x00\x00\xc1\x80'\
|
||||
b'\xc1\x80\xc1\x80\xa2\x80\xa2\x80\xa2\x80\x94\x80\x94\x80\x94\x80'\
|
||||
b'\x88\x80\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\xc1\x00\xc1\x00'\
|
||||
b'\xe1\x00\xb1\x00\x91\x00\x89\x00\x8d\x00\x87\x00\x83\x00\x83\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x3e\x00\x63\x00\xc1\x00'\
|
||||
b'\x80\x80\x80\x80\x80\x80\x80\x80\xc1\x00\x63\x00\x3e\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x09\x00\x00\x00\xfc\x00\x86\x00\x82\x00\x82\x00'\
|
||||
b'\x86\x00\xfc\x00\x80\x00\x80\x00\x80\x00\x80\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0b\x00\x00\x00\x3e\x00\x63\x00\xc1\x00\x80\x80\x80\x80'\
|
||||
b'\x80\x80\x80\x80\xc5\x80\x63\x00\x3f\x00\x00\x80\x00\x00\x00\x00'\
|
||||
b'\x0a\x00\x00\x00\xfc\x00\x82\x00\x82\x00\x82\x00\x82\x00\xfc\x00'\
|
||||
b'\x82\x00\x82\x00\x82\x00\x83\x00\x00\x00\x00\x00\x00\x00\x09\x00'\
|
||||
b'\x00\x00\x7c\x00\xc6\x00\x82\x00\xc0\x00\x78\x00\x0e\x00\x02\x00'\
|
||||
b'\x82\x00\xc6\x00\x7c\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00'\
|
||||
b'\xfe\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00'\
|
||||
b'\x10\x00\x10\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x81\x00'\
|
||||
b'\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\xc3\x00'\
|
||||
b'\x3c\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\xc1\x80\x41\x00'\
|
||||
b'\x41\x00\x63\x00\x22\x00\x32\x00\x16\x00\x14\x00\x1c\x00\x08\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc2\x18\x45\x18\x45\x10'\
|
||||
b'\x65\x10\x65\xb0\x28\xa0\x28\xa0\x38\xa0\x38\xe0\x10\x40\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x09\x00\x00\x00\x41\x00\x63\x00\x32\x00\x14\x00'\
|
||||
b'\x0c\x00\x1c\x00\x16\x00\x22\x00\x63\x00\x41\x80\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x09\x00\x00\x00\xc1\x80\x63\x00\x22\x00\x36\x00\x14\x00'\
|
||||
b'\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x09\x00\x00\x00\x7f\x00\x03\x00\x06\x00\x04\x00\x0c\x00\x18\x00'\
|
||||
b'\x30\x00\x20\x00\x40\x00\xff\x00\x00\x00\x00\x00\x00\x00\x04\x00'\
|
||||
b'\x00\xc0\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\xc0\x04\x00'\
|
||||
b'\x00\x80\x80\x40\x40\x40\x20\x20\x20\x10\x10\x00\x00\x00\x04\x00'\
|
||||
b'\x00\xc0\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\xc0\x07\x00'\
|
||||
b'\x00\x20\x60\x50\x90\x88\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x04\x00'\
|
||||
b'\x00\x40\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00'\
|
||||
b'\x00\x00\x00\x78\x84\x04\x04\x7c\x84\x8c\x76\x00\x00\x00\x08\x00'\
|
||||
b'\x00\x80\x80\xb8\xcc\x84\x84\x84\x84\xc8\xb8\x00\x00\x00\x07\x00'\
|
||||
b'\x00\x00\x00\x78\x44\x80\x80\x80\x80\x44\x78\x00\x00\x00\x08\x00'\
|
||||
b'\x00\x02\x02\x3a\x46\x82\x82\x82\x82\x46\x3a\x00\x00\x00\x07\x00'\
|
||||
b'\x00\x00\x00\x3c\x44\x82\xfe\x80\x80\x46\x3c\x00\x00\x00\x04\x00'\
|
||||
b'\x00\x60\x40\xe0\x40\x40\x40\x40\x40\x40\x40\x00\x00\x00\x08\x00'\
|
||||
b'\x00\x00\x00\x3a\x46\x82\x82\x82\x82\x46\x7a\x02\x84\x7c\x08\x00'\
|
||||
b'\x00\x80\x80\xb0\xc8\x88\x88\x88\x88\x88\x88\x00\x00\x00\x03\x00'\
|
||||
b'\x00\x80\x00\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x03\x00'\
|
||||
b'\x00\x40\x00\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\xc0\x07\x00'\
|
||||
b'\x00\x80\x80\x88\x90\xa0\xe0\x90\x98\x88\x8c\x00\x00\x00\x03\x00'\
|
||||
b'\x00\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x0b\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xb7\x00\xcc\x80\x88\x80\x88\x80\x88\x80'\
|
||||
b'\x88\x80\x88\x80\x88\x80\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\xb8\xc4\x84\x84\x84\x84\x84\x84\x00\x00\x00\x07\x00\x00\x00'\
|
||||
b'\x00\x38\x44\x82\x82\x82\x82\x44\x38\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\xb8\xc8\x84\x84\x84\x84\xc8\xb8\x80\x80\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x3a\x46\x82\x82\x82\x82\x46\x7a\x02\x02\x00\x05\x00\x00\x00'\
|
||||
b'\x00\xa0\xc0\x80\x80\x80\x80\x80\x80\x00\x00\x00\x07\x00\x00\x00'\
|
||||
b'\x00\x70\x88\x80\xc0\x70\x08\x88\x70\x00\x00\x00\x04\x00\x00\x00'\
|
||||
b'\x40\xe0\x40\x40\x40\x40\x40\x40\x60\x00\x00\x00\x08\x00\x00\x00'\
|
||||
b'\x00\x84\x84\x84\x84\x84\x84\x8c\x74\x00\x00\x00\x07\x00\x00\x00'\
|
||||
b'\x00\xc6\x44\x44\x6c\x28\x28\x38\x10\x00\x00\x00\x0a\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x8c\x40\xcc\xc0\x4c\x80\x5c\x80\x52\x80\x73\x80'\
|
||||
b'\x33\x00\x33\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x44'\
|
||||
b'\x68\x28\x30\x30\x28\x4c\xc4\x00\x00\x00\x07\x00\x00\x00\x00\xc6'\
|
||||
b'\x44\x44\x6c\x28\x28\x30\x10\x10\x20\x60\x07\x00\x00\x00\x00\x7c'\
|
||||
b'\x0c\x08\x10\x30\x60\x40\xfc\x00\x00\x00\x05\x00\x00\x60\x40\x40'\
|
||||
b'\x40\x40\x40\x80\x40\x40\x40\x40\x40\x60\x04\x00\x00\x80\x80\x80'\
|
||||
b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x05\x00\x00\xc0\x40\x40'\
|
||||
b'\x40\x40\x40\x20\x40\x40\x40\x40\x40\xc0\x07\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x62\x9e\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
_index =\
|
||||
b'\x00\x00\x10\x00\x20\x00\x30\x00\x40\x00\x50\x00\x60\x00\x7e\x00'\
|
||||
b'\x9c\x00\xac\x00\xbc\x00\xcc\x00\xdc\x00\xec\x00\xfc\x00\x0c\x01'\
|
||||
b'\x1c\x01\x2c\x01\x3c\x01\x4c\x01\x5c\x01\x6c\x01\x7c\x01\x8c\x01'\
|
||||
b'\x9c\x01\xac\x01\xbc\x01\xcc\x01\xdc\x01\xec\x01\xfc\x01\x0c\x02'\
|
||||
b'\x1c\x02\x2c\x02\x4a\x02\x68\x02\x86\x02\xa4\x02\xc2\x02\xe0\x02'\
|
||||
b'\xf0\x02\x0e\x03\x2c\x03\x3c\x03\x4c\x03\x6a\x03\x7a\x03\x98\x03'\
|
||||
b'\xb6\x03\xd4\x03\xf2\x03\x10\x04\x2e\x04\x4c\x04\x6a\x04\x88\x04'\
|
||||
b'\xa6\x04\xc4\x04\xe2\x04\x00\x05\x1e\x05\x2e\x05\x3e\x05\x4e\x05'\
|
||||
b'\x5e\x05\x6e\x05\x7e\x05\x8e\x05\x9e\x05\xae\x05\xbe\x05\xce\x05'\
|
||||
b'\xde\x05\xee\x05\xfe\x05\x0e\x06\x1e\x06\x2e\x06\x3e\x06\x5c\x06'\
|
||||
b'\x6c\x06\x7c\x06\x8c\x06\x9c\x06\xac\x06\xbc\x06\xcc\x06\xdc\x06'\
|
||||
b'\xec\x06\x0a\x07\x1a\x07\x2a\x07\x3a\x07\x4a\x07\x5a\x07\x6a\x07'\
|
||||
b'\x7a\x07'
|
||||
|
||||
_mvfont = memoryview(_font)
|
||||
|
||||
def _chr_addr(ordch):
|
||||
offset = 2 * (ordch - 32)
|
||||
return int.from_bytes(_index[offset:offset + 2], 'little')
|
||||
|
||||
def get_ch(ch):
|
||||
ordch = ord(ch)
|
||||
ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32
|
||||
offset = _chr_addr(ordch)
|
||||
width = int.from_bytes(_font[offset:offset + 2], 'little')
|
||||
next_offs = _chr_addr(ordch +1)
|
||||
return _mvfont[offset + 2:next_offs], 14, width
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
# Code generated by font-to-py.py.
|
||||
# Font: FreeSans.ttf
|
||||
version = '0.25'
|
||||
|
||||
def height():
|
||||
return 20
|
||||
|
||||
def max_width():
|
||||
return 20
|
||||
|
||||
def hmap():
|
||||
return True
|
||||
|
||||
def reverse():
|
||||
return False
|
||||
|
||||
def monospaced():
|
||||
return False
|
||||
|
||||
def min_ch():
|
||||
return 32
|
||||
|
||||
def max_ch():
|
||||
return 126
|
||||
|
||||
_font =\
|
||||
b'\x0b\x00\x00\x00\x3c\x00\x7e\x00\xc7\x00\xc3\x00\x03\x00\x03\x00'\
|
||||
b'\x06\x00\x0c\x00\x08\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00'\
|
||||
b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x07\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00'\
|
||||
b'\xc0\xc0\x00\x00\x00\x00\x07\x00\x00\x00\xd8\xd8\xd8\xd8\x90\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x0c\xc0\x08\x80\x08\x80\x7f\xe0\x7f\xe0\x19\x80\x11\x00'\
|
||||
b'\x11\x00\xff\xc0\xff\xc0\x33\x00\x33\x00\x22\x00\x22\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0b\x00\x08\x00\x3e\x00\x7f\x80\xe9\xc0'\
|
||||
b'\xc8\xc0\xc8\xc0\xc8\x00\xe8\x00\x7c\x00\x1f\x80\x09\xc0\x08\xc0'\
|
||||
b'\xc8\xc0\xe9\xc0\x7f\x80\x3e\x00\x08\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x12\x00\x00\x00\x00\x00\x00\x00\x38\x10\x00\x7c\x10\x00\xc6\x20'\
|
||||
b'\x00\xc6\x20\x00\xc6\x40\x00\x7c\xc0\x00\x38\x80\x00\x01\x1e\x00'\
|
||||
b'\x01\x3f\x00\x02\x73\x80\x02\x61\x80\x04\x73\x80\x04\x3f\x00\x08'\
|
||||
b'\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00'\
|
||||
b'\x00\x00\x00\x00\x0e\x00\x1f\x00\x31\x80\x31\x80\x31\x80\x1f\x00'\
|
||||
b'\x1c\x00\x76\x60\xe3\x60\xc1\xc0\xc0\xc0\xe1\xc0\x7f\x60\x3e\x30'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\xc0\xc0\xc0\xc0'\
|
||||
b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00'\
|
||||
b'\x00\x10\x10\x20\x20\x60\x40\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x40\x60'\
|
||||
b'\x20\x30\x10\x18\x07\x00\x00\x40\x40\x20\x20\x30\x10\x18\x18\x18'\
|
||||
b'\x18\x18\x18\x18\x10\x30\x20\x60\x40\xc0\x08\x00\x00\x20\x20\xf8'\
|
||||
b'\x20\x50\x50\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x18\x00\x18\x00\x18\x00\xff\x00\xff\x00\x18\x00\x18\x00\x18\x00'\
|
||||
b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xc0\x40\x40\x80\x00'\
|
||||
b'\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\xf8\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x06\x00\x00\x04'\
|
||||
b'\x0c\x08\x08\x18\x10\x10\x30\x20\x20\x60\x40\x40\xc0\x80\x00\x00'\
|
||||
b'\x00\x00\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00\x63\x00\xe3\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x80\x63\x00'\
|
||||
b'\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x10\x00\x30\x00\xf0\x00\xf0\x00\x30\x00\x30\x00\x30\x00'\
|
||||
b'\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00'\
|
||||
b'\xe3\x80\xc1\x80\x01\x80\x01\x80\x03\x00\x0e\x00\x1c\x00\x30\x00'\
|
||||
b'\x60\x00\xc0\x00\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00\xe3\x80\xc1\x80\x01\x80'\
|
||||
b'\x0f\x00\x0f\x00\x03\x80\x01\x80\x01\x80\xc1\x80\xe3\x80\x7f\x00'\
|
||||
b'\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00'\
|
||||
b'\x06\x00\x06\x00\x0e\x00\x1e\x00\x16\x00\x26\x00\x46\x00\x46\x00'\
|
||||
b'\x86\x00\xff\x00\xff\x00\x06\x00\x06\x00\x06\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x7f\x00\x7f\x00\x60\x00'\
|
||||
b'\x60\x00\xde\x00\xff\x00\xe3\x80\x01\x80\x01\x80\x01\x80\x01\x80'\
|
||||
b'\xc3\x00\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00'\
|
||||
b'\x00\x00\x00\x00\x1e\x00\x3f\x00\x63\x00\x61\x80\xc0\x00\xde\x00'\
|
||||
b'\xff\x00\xe3\x80\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x00\x3e\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\xff\x80'\
|
||||
b'\xff\x80\x01\x00\x03\x00\x02\x00\x06\x00\x04\x00\x0c\x00\x08\x00'\
|
||||
b'\x18\x00\x18\x00\x10\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0b\x00\x00\x00\x00\x00\x1c\x00\x3e\x00\x63\x00\x63\x00'\
|
||||
b'\x63\x00\x3e\x00\x3e\x00\x63\x00\xc1\x80\xc1\x80\xc1\x80\x63\x00'\
|
||||
b'\x7f\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x3e\x00\x7f\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xe3\x80'\
|
||||
b'\x7f\x80\x3d\x80\x01\x80\x03\x00\xe3\x00\x7e\x00\x3c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\xc0\xc0\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x05\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x00\x00\xc0\xc0\x40\x40'\
|
||||
b'\x80\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x40\x01\xc0\x07\x00\x3c\x00\xe0\x00\xe0\x00\x78\x00\x0f\x00'\
|
||||
b'\x03\xc0\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0'\
|
||||
b'\xff\xc0\x00\x00\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\xe0\x00\x78\x00\x0e\x00\x03\xc0\x01\xc0'\
|
||||
b'\x07\x00\x3c\x00\xf0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0b\x00\x00\x00\x3c\x00\x7e\x00\xc7\x00\xc3\x00\x03\x00\x03\x00'\
|
||||
b'\x06\x00\x0c\x00\x08\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00'\
|
||||
b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x03'\
|
||||
b'\xf0\x00\x0f\xfc\x00\x1e\x0f\x00\x38\x03\x80\x71\xe1\x80\x63\xe9'\
|
||||
b'\xc0\x67\x18\xc0\xce\x18\xc0\xcc\x18\xc0\xcc\x10\xc0\xcc\x31\x80'\
|
||||
b'\xce\x73\x80\x67\xff\x00\x63\x9e\x00\x30\x00\x00\x3c\x00\x00\x0f'\
|
||||
b'\xf8\x00\x03\xf0\x00\x00\x00\x00\x0d\x00\x00\x00\x07\x00\x07\x00'\
|
||||
b'\x07\x80\x0d\x80\x0d\x80\x08\xc0\x18\xc0\x18\xc0\x10\x60\x3f\xe0'\
|
||||
b'\x3f\xe0\x30\x30\x60\x30\x60\x38\xc0\x18\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0d\x00\x00\x00\xff\x00\xff\x80\xc1\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc1\xc0\xff\x00\xff\x80\xc0\xc0\xc0\x60\xc0\x60\xc0\x60\xc0\xe0'\
|
||||
b'\xff\xc0\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x0f\x80\x3f\xe0\x70\x60\x60\x30\xe0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xe0\x30\x60\x70\x70\x60\x3f\xe0\x0f\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\xff\x00\xff\x80\xc1\xc0'\
|
||||
b'\xc0\xc0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\
|
||||
b'\xc0\xc0\xc1\xc0\xff\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0d\x00\x00\x00\xff\xc0\xff\xc0\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xff\x80\xff\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\xc0'\
|
||||
b'\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\xff\x80'\
|
||||
b'\xff\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\x00\xff\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0f\x00\x00\x00\x0f\xc0\x3f\xf0\x38\x30\x60\x18'\
|
||||
b'\x60\x00\xc0\x00\xc0\x00\xc1\xf8\xc1\xf8\xc0\x18\xe0\x18\x60\x38'\
|
||||
b'\x78\x78\x3f\xd8\x0f\x88\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\
|
||||
b'\x00\x00\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xff\xe0'\
|
||||
b'\xff\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0b\x00'\
|
||||
b'\x00\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00'\
|
||||
b'\x03\x00\x03\x00\x03\x00\xc3\x00\xc3\x00\xe7\x00\x7e\x00\x3c\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc0\x60\xc0\xc0'\
|
||||
b'\xc1\x80\xc3\x00\xc6\x00\xcc\x00\xdc\x00\xf6\x00\xe6\x00\xc3\x00'\
|
||||
b'\xc1\x80\xc1\x80\xc0\xc0\xc0\x60\xc0\x60\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0b\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00'\
|
||||
b'\x00\xe0\x1c\x00\xe0\x1c\x00\xf0\x3c\x00\xf0\x3c\x00\xd0\x2c\x00'\
|
||||
b'\xd8\x6c\x00\xd8\x6c\x00\xc8\x4c\x00\xcc\xcc\x00\xcc\xcc\x00\xc4'\
|
||||
b'\x8c\x00\xc6\x8c\x00\xc7\x8c\x00\xc3\x0c\x00\xc3\x0c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\xe0\x60'\
|
||||
b'\xe0\x60\xf0\x60\xf0\x60\xd8\x60\xd8\x60\xcc\x60\xc4\x60\xc6\x60'\
|
||||
b'\xc2\x60\xc3\x60\xc1\xe0\xc1\xe0\xc0\xe0\xc0\xe0\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x10\x00\x00\x00\x0f\xc0\x1f\xe0\x38\x70\x60\x18'\
|
||||
b'\x60\x1c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\x60\x1c\x60\x18'\
|
||||
b'\x38\x70\x1f\xe0\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00'\
|
||||
b'\x00\x00\xff\x00\xff\x80\xc1\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc0'\
|
||||
b'\xff\x80\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x0f\xc0\x1f\xe0'\
|
||||
b'\x38\x70\x60\x18\x60\x1c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c'\
|
||||
b'\x60\x18\x60\xd8\x38\x70\x1f\xf8\x0f\x98\x00\x08\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0e\x00\x00\x00\xff\x80\xff\xc0\xc0\xe0\xc0\x60\xc0\x60'\
|
||||
b'\xc0\x60\xc0\xc0\xff\x80\xff\xc0\xc0\xe0\xc0\x60\xc0\x60\xc0\x60'\
|
||||
b'\xc0\x60\xc0\x70\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00'\
|
||||
b'\x1f\x80\x7f\xe0\xe0\x70\xc0\x30\xc0\x00\xe0\x00\x78\x00\x3f\x80'\
|
||||
b'\x03\xe0\x00\x70\xc0\x30\xc0\x30\x70\x60\x7f\xe0\x1f\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xff\xc0\xff\xc0\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\
|
||||
b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\
|
||||
b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\x60\xc0\x7f\xc0'\
|
||||
b'\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc0\x30'\
|
||||
b'\x60\x30\x60\x30\x20\x20\x30\x60\x30\x60\x10\x40\x18\xc0\x18\xc0'\
|
||||
b'\x08\x80\x0d\x80\x0d\x80\x07\x00\x07\x00\x07\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x13\x00\x00\x00\x00\xc0\xc0\xc0\x60\xe0\xc0\x60'\
|
||||
b'\xe0\xc0\x61\xe0\xc0\x61\xb1\x80\x31\xb1\x80\x31\xb1\x80\x33\x11'\
|
||||
b'\x80\x33\x19\x00\x13\x1b\x00\x1f\x1b\x00\x1e\x0b\x00\x1e\x0e\x00'\
|
||||
b'\x0e\x0e\x00\x0c\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0d\x00\x00\x00\x60\x30\x30\x70\x30\x60\x18\xc0\x0c\xc0'\
|
||||
b'\x0d\x80\x07\x00\x07\x00\x07\x00\x0d\x80\x18\xc0\x18\xe0\x30\x60'\
|
||||
b'\x70\x30\x60\x38\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\
|
||||
b'\x60\x18\x70\x38\x30\x30\x18\x60\x18\x60\x0c\xc0\x0f\xc0\x07\x80'\
|
||||
b'\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\xff\xe0\xff\xe0\x00\xc0'\
|
||||
b'\x01\x80\x03\x80\x03\x00\x06\x00\x0c\x00\x1c\x00\x38\x00\x30\x00'\
|
||||
b'\x60\x00\xc0\x00\xff\xe0\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x06\x00\x00\xe0\xe0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc0\xc0\xc0\xc0\xe0\xe0\x06\x00\x00\x80\xc0\x40\x40\x60\x20\x20'\
|
||||
b'\x30\x10\x10\x18\x08\x08\x0c\x04\x00\x00\x00\x00\x06\x00\x00\xe0'\
|
||||
b'\xe0\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60'\
|
||||
b'\xe0\xe0\x09\x00\x00\x00\x00\x00\x18\x00\x38\x00\x28\x00\x2c\x00'\
|
||||
b'\x64\x00\x46\x00\xc2\x00\x82\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf0'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\xc0\x60\x30\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3e\x00\xff\x80\xc1\x80\x01\x80'\
|
||||
b'\x01\x80\x3f\x80\xf1\x80\xc1\x80\xc3\x80\xff\xc0\x78\xc0\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc0\x00\xdf\x00\xff\x80\xe1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc0\xc0\xe1\x80\xff\x80\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x7f\x00'\
|
||||
b'\x61\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc1\x80\x63\x80\x7f\x00'\
|
||||
b'\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x01\x80'\
|
||||
b'\x01\x80\x01\x80\x01\x80\x3d\x80\x7f\x80\x63\x80\xc1\x80\xc1\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x80\x3d\x80\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x3e\x00\x7f\x00\x63\x00\xc1\x80\xff\x80\xff\x80\xc0\x00\xc0\x00'\
|
||||
b'\x63\x80\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00'\
|
||||
b'\x00\x30\x70\x60\x60\xf0\xf0\x60\x60\x60\x60\x60\x60\x60\x60\x60'\
|
||||
b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x3d\x80\x7f\x80\x63\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\
|
||||
b'\x63\x80\x7f\x80\x3d\x80\x01\x80\xc3\x80\x7f\x00\x3e\x00\x0b\x00'\
|
||||
b'\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xdf\x00\xdf\x80\xe3\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\xc0\xc0\x00\x00\xc0'\
|
||||
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x05\x00'\
|
||||
b'\x00\x30\x30\x00\x00\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30'\
|
||||
b'\x30\x30\xf0\xe0\x0a\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
|
||||
b'\xc3\x00\xc6\x00\xcc\x00\xd8\x00\xf8\x00\xec\x00\xce\x00\xc6\x00'\
|
||||
b'\xc3\x00\xc3\x00\xc1\x80\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00'\
|
||||
b'\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\xde\x78\xfe\xfc\xe3\x8c\xc3\x0c\xc3\x0c\xc3\x0c\xc3\x0c\xc3\x0c'\
|
||||
b'\xc3\x0c\xc3\x0c\xc3\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x00\xdf\x80\xe3\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x3e\x00\x7f\x00\x63\x00\xc1\x80\xc1\x80\xc1\x80'\
|
||||
b'\xc1\x80\xc1\x80\x63\x00\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\x00'\
|
||||
b'\xff\x80\xe1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xe1\x80'\
|
||||
b'\xff\x80\xde\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3d\x80\x7f\x80\x63\x80\xc1\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x80\x3d\x80\x01\x80'\
|
||||
b'\x01\x80\x01\x80\x00\x00\x07\x00\x00\x00\x00\x00\x00\xd8\xf8\xe0'\
|
||||
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0a\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x7f\x00\xc3\x00\xc0\x00'\
|
||||
b'\xf0\x00\x7e\x00\x0f\x00\x03\x00\xc3\x00\xfe\x00\x7c\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x60\x60\xf0\xf0\x60'\
|
||||
b'\x60\x60\x60\x60\x60\x60\x70\x70\x00\x00\x00\x00\x0b\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\
|
||||
b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x80\xfd\x80\x79\x80\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\xc0\xc0\x61\x80\x61\x80\x61\x00\x23\x00\x33\x00\x32\x00'\
|
||||
b'\x16\x00\x1e\x00\x1c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc3\x0c\xc3\x8c'\
|
||||
b'\x63\x8c\x67\x88\x66\x98\x24\xd8\x34\xd0\x3c\xd0\x3c\x70\x18\x70'\
|
||||
b'\x18\x60\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x61\x80\x63\x00\x33\x00\x1e\x00\x1c\x00'\
|
||||
b'\x0c\x00\x1c\x00\x16\x00\x33\x00\x63\x00\x41\x80\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\xc0\x80\x41\x80\x61\x80\x61\x00\x23\x00\x33\x00\x32\x00\x16\x00'\
|
||||
b'\x1c\x00\x1c\x00\x0c\x00\x08\x00\x18\x00\x78\x00\x70\x00\x0a\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\xff\x00\x06\x00'\
|
||||
b'\x06\x00\x0c\x00\x18\x00\x30\x00\x60\x00\xc0\x00\xff\x00\xff\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x18\x38\x30\x30\x30'\
|
||||
b'\x30\x30\x30\x70\xc0\x70\x30\x30\x30\x30\x30\x30\x38\x18\x05\x00'\
|
||||
b'\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
|
||||
b'\xc0\xc0\xc0\xc0\x07\x00\x00\xc0\xe0\x60\x60\x60\x60\x60\x60\x70'\
|
||||
b'\x18\x70\x60\x60\x60\x60\x60\x60\xe0\xc0\x0a\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\xf1\x00\x9f\x00'\
|
||||
b'\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00'
|
||||
|
||||
_index =\
|
||||
b'\x00\x00\x2a\x00\x2a\x00\x40\x00\x40\x00\x56\x00\x56\x00\x6c\x00'\
|
||||
b'\x6c\x00\x96\x00\x96\x00\xc0\x00\xc0\x00\xfe\x00\xfe\x00\x28\x01'\
|
||||
b'\x28\x01\x3e\x01\x3e\x01\x54\x01\x54\x01\x6a\x01\x6a\x01\x80\x01'\
|
||||
b'\x80\x01\xaa\x01\xaa\x01\xc0\x01\xc0\x01\xd6\x01\xd6\x01\xec\x01'\
|
||||
b'\xec\x01\x02\x02\x02\x02\x2c\x02\x2c\x02\x56\x02\x56\x02\x80\x02'\
|
||||
b'\x80\x02\xaa\x02\xaa\x02\xd4\x02\xd4\x02\xfe\x02\xfe\x02\x28\x03'\
|
||||
b'\x28\x03\x52\x03\x52\x03\x7c\x03\x7c\x03\xa6\x03\xa6\x03\xbc\x03'\
|
||||
b'\xbc\x03\xd2\x03\xd2\x03\xfc\x03\xfc\x03\x26\x04\x26\x04\x50\x04'\
|
||||
b'\x50\x04\x7a\x04\x7a\x04\xb8\x04\xb8\x04\xe2\x04\xe2\x04\x0c\x05'\
|
||||
b'\x0c\x05\x36\x05\x36\x05\x60\x05\x60\x05\x8a\x05\x8a\x05\xb4\x05'\
|
||||
b'\xb4\x05\xde\x05\xde\x05\x08\x06\x08\x06\x1e\x06\x1e\x06\x48\x06'\
|
||||
b'\x48\x06\x72\x06\x72\x06\x9c\x06\x9c\x06\xda\x06\xda\x06\x04\x07'\
|
||||
b'\x04\x07\x2e\x07\x2e\x07\x58\x07\x58\x07\x82\x07\x82\x07\xac\x07'\
|
||||
b'\xac\x07\xd6\x07\xd6\x07\x00\x08\x00\x08\x2a\x08\x2a\x08\x54\x08'\
|
||||
b'\x54\x08\x92\x08\x92\x08\xbc\x08\xbc\x08\xe6\x08\xe6\x08\x10\x09'\
|
||||
b'\x10\x09\x26\x09\x26\x09\x3c\x09\x3c\x09\x52\x09\x52\x09\x7c\x09'\
|
||||
b'\x7c\x09\xa6\x09\xa6\x09\xbc\x09\xbc\x09\xe6\x09\xe6\x09\x10\x0a'\
|
||||
b'\x10\x0a\x3a\x0a\x3a\x0a\x64\x0a\x64\x0a\x8e\x0a\x8e\x0a\xa4\x0a'\
|
||||
b'\xa4\x0a\xce\x0a\xce\x0a\xf8\x0a\xf8\x0a\x0e\x0b\x0e\x0b\x24\x0b'\
|
||||
b'\x24\x0b\x4e\x0b\x4e\x0b\x64\x0b\x64\x0b\x8e\x0b\x8e\x0b\xb8\x0b'\
|
||||
b'\xb8\x0b\xe2\x0b\xe2\x0b\x0c\x0c\x0c\x0c\x36\x0c\x36\x0c\x4c\x0c'\
|
||||
b'\x4c\x0c\x76\x0c\x76\x0c\x8c\x0c\x8c\x0c\xb6\x0c\xb6\x0c\xe0\x0c'\
|
||||
b'\xe0\x0c\x0a\x0d\x0a\x0d\x34\x0d\x34\x0d\x5e\x0d\x5e\x0d\x88\x0d'\
|
||||
b'\x88\x0d\x9e\x0d\x9e\x0d\xb4\x0d\xb4\x0d\xca\x0d\xca\x0d\xf4\x0d'\
|
||||
|
||||
_mvfont = memoryview(_font)
|
||||
|
||||
def get_ch(ch):
|
||||
ordch = ord(ch)
|
||||
ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32
|
||||
idx_offs = 4 * (ordch - 32)
|
||||
offset = int.from_bytes(_index[idx_offs : idx_offs + 2], 'little')
|
||||
next_offs = int.from_bytes(_index[idx_offs + 2 : idx_offs + 4], 'little')
|
||||
width = int.from_bytes(_font[offset:offset + 2], 'little')
|
||||
return _mvfont[offset + 2:next_offs], 20, width
|
||||
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 50 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 44 KiB |
|
@ -0,0 +1,116 @@
|
|||
# mono_test.py Demo program for nano_gui on an SSD1306 OLED display.
|
||||
|
||||
# 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.
|
||||
|
||||
|
||||
# https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display
|
||||
# https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html
|
||||
|
||||
# V0.31 9th Sep 2018
|
||||
|
||||
import utime
|
||||
import uos
|
||||
from ssd1306_setup import WIDTH, HEIGHT, setup
|
||||
from writer import Writer, CWriter
|
||||
from nanogui import Label, Meter, refresh
|
||||
|
||||
# Fonts
|
||||
import courier20 as fixed
|
||||
import font6 as small
|
||||
import arial10
|
||||
|
||||
|
||||
def fields(use_spi=False, soft=True):
|
||||
ssd = setup(use_spi, soft) # Create a display instance
|
||||
Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = Writer(ssd, fixed, verbose=False)
|
||||
wri.set_clip(False, False, False)
|
||||
textfield = Label(wri, 0, 2, wri.stringlen('longer'))
|
||||
numfield = Label(wri, 25, 2, wri.stringlen('99.99'), bdcolor=None)
|
||||
countfield = Label(wri, 0, 90, wri.stringlen('1'))
|
||||
n = 1
|
||||
for s in ('short', 'longer', '1', ''):
|
||||
textfield.value(s)
|
||||
numfield.value('{:5.2f}'.format(int.from_bytes(uos.urandom(2),'little')/1000))
|
||||
countfield.value('{:1d}'.format(n))
|
||||
n += 1
|
||||
refresh(ssd)
|
||||
utime.sleep(2)
|
||||
textfield.value('Done', True)
|
||||
refresh(ssd)
|
||||
|
||||
def multi_fields(use_spi=False, soft=True):
|
||||
ssd = setup(use_spi, soft) # Create a display instance
|
||||
Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = Writer(ssd, small, verbose=False)
|
||||
wri.set_clip(False, False, False)
|
||||
|
||||
nfields = []
|
||||
dy = small.height() + 6
|
||||
y = 2
|
||||
col = 15
|
||||
width = wri.stringlen('99.99')
|
||||
for txt in ('X:', 'Y:', 'Z:'):
|
||||
Label(wri, y, 0, txt)
|
||||
nfields.append(Label(wri, y, col, width, bdcolor=None)) # Draw border
|
||||
y += dy
|
||||
|
||||
for _ in range(10):
|
||||
for field in nfields:
|
||||
value = int.from_bytes(uos.urandom(3),'little')/167772
|
||||
field.value('{:5.2f}'.format(value))
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
Label(wri, 0, 64, ' DONE ', True)
|
||||
refresh(ssd)
|
||||
|
||||
def meter(use_spi=False, soft=True):
|
||||
ssd = setup(use_spi, soft)
|
||||
wri = Writer(ssd, arial10, verbose=False)
|
||||
ssd.fill(0)
|
||||
refresh(ssd)
|
||||
m0 = Meter(wri, 5, 2, height = 50, divisions = 4, legends=('0.0', '0.5', '1.0'))
|
||||
m1 = Meter(wri, 5, 44, height = 50, divisions = 4, legends=('-1', '0', '+1'))
|
||||
m2 = Meter(wri, 5, 86, height = 50, divisions = 4, legends=('-1', '0', '+1'))
|
||||
steps = 10
|
||||
for n in range(steps + 1):
|
||||
m0.value(int.from_bytes(uos.urandom(3),'little')/16777216)
|
||||
m1.value(n/steps)
|
||||
m2.value(1 - n/steps)
|
||||
refresh(ssd)
|
||||
utime.sleep(1)
|
||||
|
||||
|
||||
tstr = '''Test assumes a 128*64 (w*h) display. Edit WIDTH and HEIGHT in ssd1306_setup.py for others.
|
||||
Device pinouts are comments in ssd1306_setup.py.
|
||||
All tests take two boolean args:
|
||||
use_spi = False. Set True for SPI connected device
|
||||
soft=True set False to use hardware I2C/SPI. Hardware I2C option currently fails with official SSD1306 driver.
|
||||
|
||||
Available tests:
|
||||
fields() Label test with dynamic data.
|
||||
multi_fields() More Labels.
|
||||
meter() Demo of Meter object.
|
||||
'''
|
||||
|
||||
print(tstr)
|
|
@ -0,0 +1,385 @@
|
|||
# nanogui.py Displayable objects based on the Writer and CWriter classes
|
||||
# V0.3 Peter Hinch 26th Aug 2018
|
||||
|
||||
# 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.
|
||||
|
||||
# Base class for a displayable object. Subclasses must implement .show() and .value()
|
||||
# Has position, colors and border definition.
|
||||
# border: False no border None use bgcolor, int: treat as color
|
||||
|
||||
import cmath
|
||||
from writer import Writer
|
||||
import framebuf
|
||||
import gc
|
||||
|
||||
def _circle(dev, x0, y0, r, color): # Single pixel circle
|
||||
x = -r
|
||||
y = 0
|
||||
err = 2 -2*r
|
||||
while x <= 0:
|
||||
dev.pixel(x0 -x, y0 +y, color)
|
||||
dev.pixel(x0 +x, y0 +y, color)
|
||||
dev.pixel(x0 +x, y0 -y, color)
|
||||
dev.pixel(x0 -x, y0 -y, color)
|
||||
e2 = err
|
||||
if (e2 <= y):
|
||||
y += 1
|
||||
err += y*2 +1
|
||||
if (-x == y and e2 <= x):
|
||||
e2 = 0
|
||||
if (e2 > x):
|
||||
x += 1
|
||||
err += x*2 +1
|
||||
|
||||
def circle(dev, x0, y0, r, color, width =1): # Draw circle
|
||||
x0, y0, r = int(x0), int(y0), int(r)
|
||||
for r in range(r, r -width, -1):
|
||||
_circle(dev, x0, y0, r, color)
|
||||
|
||||
def fillcircle(dev, x0, y0, r, color): # Draw filled circle
|
||||
x0, y0, r = int(x0), int(y0), int(r)
|
||||
x = -r
|
||||
y = 0
|
||||
err = 2 -2*r
|
||||
while x <= 0:
|
||||
dev.line(x0 -x, y0 -y, x0 -x, y0 +y, color)
|
||||
dev.line(x0 +x, y0 -y, x0 +x, y0 +y, color)
|
||||
e2 = err
|
||||
if (e2 <= y):
|
||||
y +=1
|
||||
err += y*2 +1
|
||||
if (-x == y and e2 <= x):
|
||||
e2 = 0
|
||||
if (e2 > x):
|
||||
x += 1
|
||||
err += x*2 +1
|
||||
|
||||
# Line defined by polar coords; origin and line are complex
|
||||
def polar(dev, origin, line, color):
|
||||
xs, ys = origin.real, origin.imag
|
||||
theta = cmath.polar(line)[1]
|
||||
dev.line(round(xs), round(ys), round(xs + line.real), round(ys - line.imag), color)
|
||||
|
||||
def conj(v): # complex conjugate
|
||||
return v.real - v.imag * 1j
|
||||
|
||||
# Draw an arrow; origin and vec are complex, scalar lc defines length of chevron.
|
||||
# cw and ccw are unit vectors of +-3pi/4 radians for chevrons (precompiled)
|
||||
def arrow(dev, origin, vec, lc, color, ccw=cmath.exp(3j * cmath.pi/4), cw=cmath.exp(-3j * cmath.pi/4)):
|
||||
length, theta = cmath.polar(vec)
|
||||
uv = cmath.rect(1, theta) # Unit rotation vector
|
||||
start = -vec
|
||||
if length > 3 * lc: # If line is long
|
||||
ds = cmath.rect(lc, theta)
|
||||
start += ds # shorten to allow for length of tail chevrons
|
||||
chev = lc + 0j
|
||||
polar(dev, origin, vec, color) # Origin to tip
|
||||
polar(dev, origin, start, color) # Origin to tail
|
||||
polar(dev, origin + conj(vec), chev*ccw*uv, color) # Tip chevron
|
||||
polar(dev, origin + conj(vec), chev*cw*uv, color)
|
||||
if length > lc: # Confusing appearance of very short vectors with tail chevron
|
||||
polar(dev, origin + conj(start), chev*ccw*uv, color) # Tail chevron
|
||||
polar(dev, origin + conj(start), chev*cw*uv, color)
|
||||
|
||||
# If a (framebuf based) device is passed to refresh, the screen is cleared.
|
||||
# None causes pending widgets to be drawn and the result to be copied to hardware.
|
||||
# The pend mechanism enables a displayable object to postpone its renedering
|
||||
# until it is complete: efficient for e.g. Dial which may have multiple Pointers
|
||||
def refresh(device, clear=False):
|
||||
if not isinstance(device, framebuf.FrameBuffer):
|
||||
raise ValueError('Device must be derived from FrameBuffer.')
|
||||
if device not in DObject.devices:
|
||||
DObject.devices[device] = set()
|
||||
device.fill(0)
|
||||
else:
|
||||
if clear:
|
||||
DObject.devices[device].clear() # Clear the pending set
|
||||
device.fill(0)
|
||||
else:
|
||||
for obj in DObject.devices[device]:
|
||||
obj.show()
|
||||
DObject.devices[device].clear()
|
||||
device.show()
|
||||
|
||||
# Displayable object: effectively an ABC for all GUI objects.
|
||||
class DObject():
|
||||
devices = {} # Index device instance, value is a set of pending objects
|
||||
|
||||
@classmethod
|
||||
def _set_pend(cls, obj):
|
||||
cls.devices[obj.device].add(obj)
|
||||
|
||||
def __init__(self, writer, row, col, height, width, fgcolor, bgcolor, bdcolor):
|
||||
writer.set_clip(True, True, False) # Disable scrolling text
|
||||
self.writer = writer
|
||||
device = writer.device
|
||||
self.device = device
|
||||
if row < 0:
|
||||
row = 0
|
||||
self.warning()
|
||||
elif row + height >= device.height:
|
||||
row = device.height - height - 1
|
||||
self.warning()
|
||||
if col < 0:
|
||||
col = 0
|
||||
self.warning()
|
||||
elif col + width >= device.width:
|
||||
row = device.width - width - 1
|
||||
self.warning()
|
||||
self.row = row
|
||||
self.col = col
|
||||
self.width = width
|
||||
self.height = height
|
||||
self._value = None # Type depends on context but None means don't display.
|
||||
# Current colors
|
||||
if fgcolor is None:
|
||||
fgcolor = writer.fgcolor
|
||||
if bgcolor is None:
|
||||
bgcolor = writer.bgcolor
|
||||
if bdcolor is None:
|
||||
bdcolor = fgcolor
|
||||
self.fgcolor = fgcolor
|
||||
self.bgcolor = bgcolor
|
||||
# bdcolor is False if no border is to be drawn
|
||||
self.bdcolor = bdcolor
|
||||
# Default colors allow restoration after dynamic change
|
||||
self.def_fgcolor = fgcolor
|
||||
self.def_bgcolor = bgcolor
|
||||
self.def_bdcolor = bdcolor
|
||||
# has_border is True if a border was drawn
|
||||
self.has_border = False
|
||||
|
||||
def warning(self):
|
||||
print('Warning: attempt to create {} outside screen dimensions.'.format(self.__class__.__name__))
|
||||
|
||||
# Blank working area
|
||||
# Draw a border if .bdcolor specifies a color. If False, erase an existing border
|
||||
def show(self):
|
||||
wri = self.writer
|
||||
dev = self.device
|
||||
dev.fill_rect(self.col, self.row, self.width, self.height, self.bgcolor)
|
||||
if isinstance(self.bdcolor, bool): # No border
|
||||
if self.has_border: # Border exists: erase it
|
||||
dev.rect(self.col - 2, self.row - 2, self.width + 4, self.height + 4, self.bgcolor)
|
||||
self.has_border = False
|
||||
elif self.bdcolor: # Border is required
|
||||
dev.rect(self.col - 2, self.row - 2, self.width + 4, self.height + 4, self.bdcolor)
|
||||
self.has_border = True
|
||||
|
||||
def value(self, v=None):
|
||||
if v is not None:
|
||||
self._value = v
|
||||
return self._value
|
||||
|
||||
def text(self, text=None, invert=False, fgcolor=None, bgcolor=None, bdcolor=None):
|
||||
if hasattr(self, 'label'):
|
||||
self.label.value(text, invert, fgcolor, bgcolor, bdcolor)
|
||||
else:
|
||||
raise ValueError('Attempt to update nonexistent label.')
|
||||
|
||||
# text: str display string int save width
|
||||
class Label(DObject):
|
||||
def __init__(self, writer, row, col, text, invert=False, fgcolor=None, bgcolor=None, bdcolor=False):
|
||||
# Determine width of object
|
||||
if isinstance(text, int):
|
||||
width = text
|
||||
text = None
|
||||
else:
|
||||
width = writer.stringlen(text)
|
||||
height = writer.height
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
|
||||
if text is not None:
|
||||
self.value(text, invert)
|
||||
|
||||
def value(self, text=None, invert=False, fgcolor=None, bgcolor=None, bdcolor=None):
|
||||
txt = super().value(text)
|
||||
# Redraw even if no text supplied: colors may have changed.
|
||||
self.invert = invert
|
||||
self.fgcolor = self.def_fgcolor if fgcolor is None else fgcolor
|
||||
self.bgcolor = self.def_bgcolor if bgcolor is None else bgcolor
|
||||
if bdcolor is False:
|
||||
self.def_bdcolor = False
|
||||
self.bdcolor = self.def_bdcolor if bdcolor is None else bdcolor
|
||||
self.show()
|
||||
return txt
|
||||
|
||||
def show(self):
|
||||
txt = super().value()
|
||||
if txt is None: # No content to draw. Future use.
|
||||
return
|
||||
super().show() # Draw or erase border
|
||||
wri = self.writer
|
||||
dev = self.device
|
||||
wri.setcolor(self.fgcolor, self.bgcolor)
|
||||
Writer.set_textpos(dev, self.row, self.col)
|
||||
wri.setcolor(self.fgcolor, self.bgcolor)
|
||||
wri.printstring(txt, self.invert)
|
||||
wri.setcolor() # Restore defaults
|
||||
|
||||
class Meter(DObject):
|
||||
BAR = 1
|
||||
LINE = 0
|
||||
def __init__(self, writer, row, col, *, height=50, width=10,
|
||||
fgcolor=None, bgcolor=None, ptcolor=None, bdcolor=None,
|
||||
divisions=5, label=None, style=0, legends=None, value=None):
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
|
||||
self.divisions = divisions
|
||||
if label is not None:
|
||||
Label(writer, row + height + 3, col, label)
|
||||
self.style = style
|
||||
self.legends = legends
|
||||
self.ptcolor = ptcolor if ptcolor is not None else self.fgcolor
|
||||
self.value(value)
|
||||
|
||||
def value(self, n=None, color=None):
|
||||
if n is None:
|
||||
return super().value()
|
||||
n = super().value(min(1, max(0, n)))
|
||||
if color is not None:
|
||||
self.ptcolor = color
|
||||
self.show()
|
||||
return n
|
||||
|
||||
def show(self):
|
||||
super().show() # Draw or erase border
|
||||
val = super().value()
|
||||
wri = self.writer
|
||||
dev = self.device
|
||||
width = self.width
|
||||
height = self.height
|
||||
legends = self.legends
|
||||
x0 = self.col
|
||||
x1 = self.col + width
|
||||
y0 = self.row
|
||||
y1 = self.row + height
|
||||
if self.divisions > 0:
|
||||
dy = height / (self.divisions) # Tick marks
|
||||
for tick in range(self.divisions + 1):
|
||||
ypos = int(y0 + dy * tick)
|
||||
dev.hline(x0 + 2, ypos, x1 - x0 - 4, self.fgcolor)
|
||||
|
||||
if legends is not None: # Legends
|
||||
dy = 0 if len(legends) <= 1 else height / (len(legends) -1)
|
||||
yl = y1 - wri.height / 2 # Start at bottom
|
||||
for legend in legends:
|
||||
Label(wri, int(yl), x1 + 4, legend)
|
||||
yl -= dy
|
||||
y = int(y1 - val * height) # y position of slider
|
||||
if self.style == self.LINE:
|
||||
dev.hline(x0, y, width, self.ptcolor) # Draw pointer
|
||||
else:
|
||||
w = width / 2
|
||||
dev.fill_rect(int(x0 + w - 2), y, 4, y1 - y, self.ptcolor)
|
||||
|
||||
|
||||
class LED(DObject):
|
||||
def __init__(self, writer, row, col, *, height=12,
|
||||
fgcolor=None, bgcolor=None, bdcolor=None, label=None):
|
||||
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
|
||||
if label is not None:
|
||||
self.label = Label(writer, row + height + 3, col, label)
|
||||
self.radius = self.height // 2
|
||||
|
||||
def color(self, c=None):
|
||||
self.fgcolor = self.bgcolor if c is None else c
|
||||
self.show()
|
||||
|
||||
def show(self):
|
||||
super().show()
|
||||
wri = self.writer
|
||||
dev = self.device
|
||||
r = self.radius
|
||||
fillcircle(dev, self.col + r, self.row + r, r, self.fgcolor)
|
||||
if isinstance(self.bdcolor, int):
|
||||
circle(dev, self.col + r, self.row + r, r, self.bdcolor)
|
||||
|
||||
|
||||
class Pointer():
|
||||
def __init__(self, dial):
|
||||
self.dial = dial
|
||||
self.val = 0 + 0j
|
||||
self.color = None
|
||||
|
||||
def value(self, v=None, color=None):
|
||||
self.color = color
|
||||
if v is not None:
|
||||
if isinstance(v, complex):
|
||||
l = cmath.polar(v)[0]
|
||||
if l > 1:
|
||||
self.val = v/l
|
||||
else:
|
||||
self.val = v
|
||||
else:
|
||||
raise ValueError('Pointer value must be complex.')
|
||||
self.dial.vectors.add(self)
|
||||
self.dial._set_pend(self.dial) # avoid redrawing for each vector
|
||||
return self.val
|
||||
|
||||
class Dial(DObject):
|
||||
CLOCK = 0
|
||||
COMPASS = 1
|
||||
def __init__(self, writer, row, col, *, height=50,
|
||||
fgcolor=None, bgcolor=None, bdcolor=False, ticks=4,
|
||||
label=None, style=0, pip=None):
|
||||
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
|
||||
self.style = style
|
||||
self.pip = self.fgcolor if pip is None else pip
|
||||
if label is not None:
|
||||
self.label = Label(writer, row + height + 3, col, label)
|
||||
radius = int(height / 2)
|
||||
self.radius = radius
|
||||
self.ticks = ticks
|
||||
self.xorigin = col + radius
|
||||
self.yorigin = row + radius
|
||||
self.vectors = set()
|
||||
|
||||
def show(self):
|
||||
super().show()
|
||||
# cache bound variables
|
||||
dev = self.device
|
||||
ticks = self.ticks
|
||||
radius = self.radius
|
||||
xo = self.xorigin
|
||||
yo = self.yorigin
|
||||
# vectors (complex)
|
||||
vor = xo + 1j * yo
|
||||
vtstart = 0.9 * radius + 0j # start of tick
|
||||
vtick = 0.1 * radius + 0j # tick
|
||||
vrot = cmath.exp(2j * cmath.pi/ticks) # unit rotation
|
||||
for _ in range(ticks):
|
||||
polar(dev, vor + conj(vtstart), vtick, self.fgcolor)
|
||||
vtick *= vrot
|
||||
vtstart *= vrot
|
||||
circle(dev, xo, yo, radius, self.fgcolor)
|
||||
vshort = 1000 # Length of shortest vector
|
||||
for v in self.vectors:
|
||||
color = self.fgcolor if v.color is None else v.color
|
||||
val = v.value() * radius # val is complex
|
||||
vshort = min(vshort, cmath.polar(val)[0])
|
||||
if self.style == Dial.CLOCK:
|
||||
polar(dev, vor, val, color)
|
||||
else:
|
||||
arrow(dev, vor, val, 5, color)
|
||||
if isinstance(self.pip, int) and vshort > 5:
|
||||
fillcircle(dev, xo, yo, 2, self.pip)
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
# fplot module
|
||||
|
||||
This provides a rudimentary means of displaying two dimensional Cartesian (xy)
|
||||
and polar graphs on `framebuf` based displays. It is an optional extension to
|
||||
the MicroPython [nano-gui](https://github.com/peterhinch/micropython-nano-gui)
|
||||
library: this should be installed, configured and tested before use.
|
||||
|
||||
This was ported from the
|
||||
[lcd160cr-gui library](https://github.com/peterhinch/micropython-lcd160cr-gui).
|
||||
Like `nanogui.py` it uses synchronous code.
|
||||
|
||||
# Contents
|
||||
|
||||
1. [Python files](./FPLOT.md#1-python-files)
|
||||
2. [Concepts](./FPLOT.md#2-concepts)
|
||||
2.1 [Graph classes](./FPLOT.md#21-graph-classes)
|
||||
2.2 [Curve clsses](./FPLOT.md#22-curve-classes)
|
||||
2.3 [Coordinates](./FPLOT.md#23-coordinates)
|
||||
3. [Graph classes](./FPLOT.md#3-graph-classes)
|
||||
3.1 [Class CartesianGraph](./FPLOT.md#31-class-cartesiangraph)
|
||||
3.2 [Class PolarGraph](./FPLOT.md#32-class-polargraph)
|
||||
4. [Curve classes](./FPLOT.md#4-curve-classes)
|
||||
4.1 [class Curve](./FPLOT.md#41-class-curve)
|
||||
4.1.1 [Scaling](./FPLOT.md#411-scaling) Optional scaling of data values.
|
||||
4.2 [class PolarCurve](./FPLOT.md#42-class-polarcurve)
|
||||
4.2.1 [Scaling](./FPLOT.md#421-scaling) Required scaling of complex points.
|
||||
4.3 [class TSequence](./FPLOT.md#43-class-tsequence) Plot Y values on time axis.
|
||||
|
||||
###### [Main README](../README.md)
|
||||
|
||||
# 1. Python files
|
||||
|
||||
These are located in the `plot` directory.
|
||||
|
||||
1. `fplot.py` The plot library
|
||||
2. `fpt.py` Test program. Usage examples.
|
||||
|
||||
# 2. Concepts
|
||||
|
||||
Data for Cartesian graphs constitutes a set of x, y pairs, for polar graphs
|
||||
it is a set of complex `z` values. The module supports three common cases:
|
||||
1. The dataset is complete at the outset.
|
||||
2. Arbitrary data arrives gradually and needs to be plotted as it arrives.
|
||||
3. One or more `y` values arrive gradually. The `X` axis represents time. This
|
||||
is a simplifying case of 2.
|
||||
|
||||
## 2.1 Graph classes
|
||||
|
||||
A user program first instantiates a graph object (`PolarGraph` or
|
||||
`CartesianGraph`). This creates an empty graph image upon which one or more
|
||||
curves may be plotted.
|
||||
|
||||
## 2.2 Curve classes
|
||||
|
||||
The user program then instantiates one or more curves (`Curve` or
|
||||
`PolarCurve`) as appropriate to the graph. Curves may be assigned colors to
|
||||
distinguish them.
|
||||
|
||||
A curve is plotted by means of a user defined `populate` generator. This
|
||||
assigns points to the curve in the order in which they are to be plotted. The
|
||||
curve will be displayed on the graph as a sequence of straight line segments
|
||||
between successive points.
|
||||
|
||||
Where it is required to plot realtime data as it arrives, this is achieved
|
||||
via calls to the curve's `point` method.
|
||||
|
||||
## 2.3 Coordinates
|
||||
|
||||
Graph objects are sized and positioned in terms of TFT screen pixel
|
||||
coordinates, with (0, 0) being the top left corner of the display, with x
|
||||
increasing to the right and y increasing downwards. The coordinate system
|
||||
within a graph conforms to normal mathematical conventions.
|
||||
|
||||
Scaling is provided on Cartesian curves enabling user defined ranges for x and
|
||||
y values. Points lying outside of the defined range will produce lines which
|
||||
are clipped at the graph boundary.
|
||||
|
||||
Points on polar curves are defined as Python `complex` types and should lie
|
||||
within the unit circle. Points which are out of range may be plotted beyond the
|
||||
unit circle but will be clipped to the rectangular graph boundary.
|
||||
|
||||
###### [Contents](./FPLOT.md#contents)
|
||||
|
||||
# 3. Graph classes
|
||||
|
||||
## 3.1 Class CartesianGraph
|
||||
|
||||
Constructor.
|
||||
Mandatory positional arguments:
|
||||
1. `writer` A `CWriter` instance.
|
||||
2. `row` Position of the graph in screen coordinates.
|
||||
3. `col`
|
||||
|
||||
Keyword only arguments (all optional):
|
||||
* `height=90` Dimension of the bounding box.
|
||||
* `width=110` Dimension of the bounding box.
|
||||
* `fgcolor=None` Color of the axis lines. Defaults to Writer forgeround color.
|
||||
* `bgcolor=None` Background color of graph. Defaults to Writer background.
|
||||
* `bdcolor=None` Border color. If `False` no border is displayed. If `None` a
|
||||
border is shown in the `Writer` forgeround color. If a color is passed, it is
|
||||
used.
|
||||
* `gridcolor=None` Color of grid. Default: Writer forgeround color.
|
||||
* `xdivs=10` Number of divisions (grid lines) on x axis.
|
||||
* `ydivs=10` Number of divisions on y axis.
|
||||
* `xorigin=5` Location of origin in terms of grid divisions.
|
||||
* `yorigin=5` As `xorigin`. The default of 5, 5 with 10 grid lines on each
|
||||
axis puts the origin at the centre of the graph. Settings of 0, 0 would be
|
||||
used to plot positive values only.
|
||||
|
||||
Methods:
|
||||
1. `clear` No args. Clears all curves from the graph.
|
||||
2. `show` No args. Redraws the graph. For future/subclass use.
|
||||
|
||||
## 3.2 Class PolarGraph
|
||||
|
||||
Constructor.
|
||||
Mandatory positional arguments:
|
||||
1. `writer` A `CWriter` instance.
|
||||
2. `row` Position of the graph in screen coordinates.
|
||||
3. `col`
|
||||
|
||||
Keyword only arguments (all optional):
|
||||
* `height=90` Dimension of the square bounding box.
|
||||
* `fgcolor=None` Color of the axis lines. Defaults to Writer forgeround color.
|
||||
* `bgcolor=None` Background color of graph. Defaults to Writer background.
|
||||
* `bdcolor=None` Border color. If `False` no border is displayed. If `None` a
|
||||
border is shown in the `Writer` forgeround color. If a color is passed, it is
|
||||
used.
|
||||
* `gridcolor=None` Color of grid. Default: Writer forgeround color.
|
||||
* `adivs=3` Number of angle divisions per quadrant.
|
||||
* `rdivs=4` Number radius divisions.
|
||||
|
||||
Methods:
|
||||
1. `clear` No args. Clears all curves from the graph.
|
||||
2. `show` No args. Redraws the graph. For future/subclass use.
|
||||
|
||||
###### [Contents](./FPLOT.md#contents)
|
||||
|
||||
# 4. Curve classes
|
||||
|
||||
## 4.1 class Curve
|
||||
|
||||
The Cartesian curve constructor takes the following positional arguments:
|
||||
|
||||
Mandatory arguments:
|
||||
1. `graph` The `CartesianGraph` instance.
|
||||
2. `color`
|
||||
|
||||
Optional arguments:
|
||||
3. `populate=None` A generator to populate the curve. See below.
|
||||
4. `origin=(0,0)` 2-tuple containing x and y values for the origin. Provides
|
||||
for an optional shift of the data's origin.
|
||||
5. `excursion=(1,1)` 2-tuple containing scaling values for x and y.
|
||||
|
||||
Methods:
|
||||
* `point` Arguments x, y. Defaults `None`. Adds a point to the curve. If a
|
||||
prior point exists a line will be drawn between it and the current point. If a
|
||||
point is out of range or if either arg is `None` no line will be drawn.
|
||||
Passing no args enables discontinuous curves to be plotted. This method is
|
||||
normally used for real time plotting.
|
||||
|
||||
The `populate` generator may take zero or more positional arguments. It should
|
||||
repeatedly yield `x, y` values before returning. Where a curve is discontinuous
|
||||
`None, None` may be yielded: this causes the line to stop. It is resumed when
|
||||
the next valid `x, y` pair is yielded.
|
||||
|
||||
If `populate` is not provided the curve may be plotted by successive calls to
|
||||
the `point` method. This may be of use where data points are acquired in real
|
||||
time, and realtime plotting is required. See function `rt_rect` in `fpt.py`.
|
||||
|
||||
### 4.1.1 Scaling
|
||||
|
||||
By default, with symmetrical axes, x and y values are assumed to lie between -1
|
||||
and +1.
|
||||
|
||||
To plot x values from 1000 to 4000 we would set the `origin` x value to 1000
|
||||
and the `excursion` x value to 3000. The `excursion` values scale the plotted
|
||||
values to fit the corresponding axis.
|
||||
|
||||
## 4.2 class PolarCurve
|
||||
|
||||
The constructor takes the following positional arguments:
|
||||
|
||||
Mandatory arguments:
|
||||
1. `graph` The `PolarGraph` instance.
|
||||
2. `color`
|
||||
|
||||
Optional arguments:
|
||||
3. `populate=None` A generator to populate the curve. See below.
|
||||
|
||||
Methods:
|
||||
* `point` Argument `z=None`. Normally a `complex`. Adds a point
|
||||
to the curve. If a prior point exists a line will be drawn between it and the
|
||||
current point. If the arg is `None` no line will be drawn. Passing no args
|
||||
enables discontinuous curves to be plotted. Lines are clipped at the square
|
||||
region bounded by (-1, -1) to (+1, +1).
|
||||
|
||||
The `populate` generator may take zero or more positional arguments. It should
|
||||
yield a complex `z` value for each point before returning. Where a curve is
|
||||
discontinuous a value of `None` may be yielded: this causes plotting to stop.
|
||||
It is resumed when the next valid `z` point is yielded.
|
||||
|
||||
If `populate` is not provided the curve may be plotted by successive calls to
|
||||
the `point` method. This may be of use where data points are acquired in real
|
||||
time, and realtime plotting is required.
|
||||
|
||||
### 4.2.1 Scaling
|
||||
|
||||
Complex points should lie within the unit circle to be drawn within the grid.
|
||||
|
||||
###### [Contents](./FPLOT.md#contents)
|
||||
|
||||
## 4.3 class TSequence
|
||||
|
||||
A common task is the acquisition and plotting of real time data against time,
|
||||
such as hourly temperature and air pressure readings. This class facilitates
|
||||
this. Time is on the x-axis with the most recent data on the right. Older
|
||||
points are plotted to the left until they reach the left hand edge when they
|
||||
are discarded. This is akin to old fashioned pen plotters where the pen was at
|
||||
the rightmost edge (corresponding to time now) with old values scrolling to the
|
||||
left with the time axis in the conventional direction.
|
||||
|
||||
The user instantiates a graph with the X origin at the right hand side and then
|
||||
instantiates one or more `TSequence` objects. As each set of data arrives it is
|
||||
appended to its `TSequence` using the `add` method. See the example below.
|
||||
|
||||
The constructor takes the following args:
|
||||
|
||||
Mandatory arguments:
|
||||
1. `graph` The `PolarGraph` instance.
|
||||
2. `color`
|
||||
3. `size` Integer. The number of time samples to be plotted. See below.
|
||||
|
||||
Optional arguments:
|
||||
4. `yorigin=0` These args provide scaling of Y axis values as per the `Curve`
|
||||
class.
|
||||
5 `yexc=1`
|
||||
|
||||
Method:
|
||||
1. `add` Arg `v` the value to be plotted. This should lie between -1 and +1
|
||||
unless scaling is applied.
|
||||
|
||||
Note that there is little point in setting the `size` argument to a value
|
||||
greater than the number of X-axis pixels on the graph. It will work but RAM
|
||||
and execution time will be wasted: the constructor instantiates an array of
|
||||
floats of this size.
|
||||
|
||||
Each time a data set arrives the graph should be cleared, a data value should
|
||||
be added to each `TSequence` instance, and the display instance should be
|
||||
refreshed. The following example assumes that `ssd` is the display device and
|
||||
`wri` is a `Writer` or `CWriter` instance.
|
||||
|
||||
```python
|
||||
def foo():
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = CartesianGraph(wri, 2, 2, xorigin = 10, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
tsy = TSequence(g, YELLOW, 50)
|
||||
tsr = TSequence(g, RED, 50)
|
||||
for t in range(100):
|
||||
g.clear()
|
||||
tsy.add(0.9*math.sin(t/10))
|
||||
tsr.add(0.4*math.cos(t/10))
|
||||
refresh(ssd)
|
||||
utime.sleep_ms(100)
|
||||
```
|
||||
|
||||
###### [Contents](./FPLOT.md#contents)
|
|
@ -0,0 +1,272 @@
|
|||
# fplot.py Graph plotting extension for nanogui
|
||||
# Now clips out of range lines
|
||||
|
||||
# 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.
|
||||
|
||||
from nanogui import DObject, circle
|
||||
from cmath import rect, pi
|
||||
from micropython import const
|
||||
from array import array
|
||||
|
||||
type_gen = type((lambda: (yield))())
|
||||
|
||||
# Line clipping outcode bits
|
||||
_TOP = const(1)
|
||||
_BOTTOM = const(2)
|
||||
_LEFT = const(4)
|
||||
_RIGHT = const(8)
|
||||
# Bounding box for line clipping
|
||||
_XMAX = const(1)
|
||||
_XMIN = const(-1)
|
||||
_YMAX = const(1)
|
||||
_YMIN = const(-1)
|
||||
|
||||
|
||||
class Curve():
|
||||
@staticmethod
|
||||
def _outcode(x, y):
|
||||
oc = _TOP if y > 1 else 0
|
||||
oc |= _BOTTOM if y < -1 else 0
|
||||
oc |= _RIGHT if x > 1 else 0
|
||||
oc |= _LEFT if x < -1 else 0
|
||||
return oc
|
||||
|
||||
def __init__(self, graph, color, populate=None, origin=(0, 0), excursion=(1, 1)):
|
||||
self.graph = graph
|
||||
self.origin = origin
|
||||
self.excursion = excursion
|
||||
self.color = color
|
||||
self.lastpoint = None
|
||||
self.newpoint = None
|
||||
if populate is not None and self._valid(populate):
|
||||
for x, y in populate:
|
||||
self.point(x, y)
|
||||
|
||||
def _valid(self, populate):
|
||||
if not isinstance(populate, type_gen):
|
||||
raise ValueError('populate must be a generator.')
|
||||
return True
|
||||
|
||||
def point(self, x=None, y=None):
|
||||
if x is None or y is None:
|
||||
self.newpoint = None
|
||||
self.lastpoint = None
|
||||
return
|
||||
|
||||
self.newpoint = self._scale(x, y) # In-range points scaled to +-1 bounding box
|
||||
if self.lastpoint is None: # Nothing to plot. Save for next line.
|
||||
self.lastpoint = self.newpoint
|
||||
return
|
||||
|
||||
res = self._clip(*(self.lastpoint + self.newpoint)) # Clip to +-1 box
|
||||
if res is not None: # Ignore lines which don't intersect
|
||||
self.graph.line(res[0:2], res[2:], self.color)
|
||||
self.lastpoint = self.newpoint # Scaled but not clipped
|
||||
|
||||
# Cohen–Sutherland line clipping algorithm
|
||||
# If self.newpoint and self.lastpoint are valid clip them so that both lie
|
||||
# in +-1 range. If both are outside the box return None.
|
||||
def _clip(self, x0, y0, x1, y1):
|
||||
oc1 = self._outcode(x0, y0)
|
||||
oc2 = self._outcode(x1, y1)
|
||||
while True:
|
||||
if not oc1 | oc2: # OK to plot
|
||||
return x0, y0, x1, y1
|
||||
if oc1 & oc2: # Nothing to do
|
||||
return
|
||||
oc = oc1 if oc1 else oc2
|
||||
if oc & _TOP:
|
||||
x = x0 + (_YMAX - y0)*(x1 - x0)/(y1 - y0)
|
||||
y = _YMAX
|
||||
elif oc & _BOTTOM:
|
||||
x = x0 + (_YMIN - y0)*(x1 - x0)/(y1 - y0)
|
||||
y = _YMIN
|
||||
elif oc & _RIGHT:
|
||||
y = y0 + (_XMAX - x0)*(y1 - y0)/(x1 - x0)
|
||||
x = _XMAX
|
||||
elif oc & _LEFT:
|
||||
y = y0 + (_XMIN - x0)*(y1 - y0)/(x1 - x0)
|
||||
x = _XMIN
|
||||
if oc is oc1:
|
||||
x0, y0 = x, y
|
||||
oc1 = self._outcode(x0, y0)
|
||||
else:
|
||||
x1, y1 = x, y
|
||||
oc2 = self._outcode(x1, y1)
|
||||
|
||||
def _scale(self, x, y): # Scale to +-1.0
|
||||
x0, y0 = self.origin
|
||||
xr, yr = self.excursion
|
||||
xs = (x - x0) / xr
|
||||
ys = (y - y0) / yr
|
||||
return xs, ys
|
||||
|
||||
class PolarCurve(Curve): # Points are complex
|
||||
def __init__(self, graph, color, populate=None):
|
||||
super().__init__(graph, color)
|
||||
if populate is not None and self._valid(populate):
|
||||
for z in populate:
|
||||
self.point(z)
|
||||
|
||||
def point(self, z=None):
|
||||
if z is None:
|
||||
self.newpoint = None
|
||||
self.lastpoint = None
|
||||
return
|
||||
|
||||
self.newpoint = self._scale(z.real, z.imag) # In-range points scaled to +-1 bounding box
|
||||
if self.lastpoint is None: # Nothing to plot. Save for next line.
|
||||
self.lastpoint = self.newpoint
|
||||
return
|
||||
|
||||
res = self._clip(*(self.lastpoint + self.newpoint)) # Clip to +-1 box
|
||||
if res is not None: # At least part of line was in box
|
||||
start = res[0] + 1j*res[1]
|
||||
end = res[2] + 1j*res[3]
|
||||
self.graph.cline(start, end, self.color)
|
||||
self.lastpoint = self.newpoint # Scaled but not clipped
|
||||
|
||||
|
||||
class TSequence(Curve):
|
||||
def __init__(self, graph, color, size, yorigin=0, yexc=1):
|
||||
super().__init__(graph, color, origin=(0, yorigin), excursion=(1, yexc))
|
||||
self.data = array('f', (0 for _ in range(size)))
|
||||
self.cur = 0
|
||||
self.size = size
|
||||
self.count = 0
|
||||
|
||||
def add(self, v):
|
||||
p = self.cur
|
||||
size = self.size
|
||||
self.data[self.cur] = v
|
||||
self.cur += 1
|
||||
self.cur %= size
|
||||
if self.count < size:
|
||||
self.count += 1
|
||||
x = 0
|
||||
dx = 1/size
|
||||
for _ in range(self.count):
|
||||
self.point(x, self.data[p])
|
||||
x -= dx
|
||||
p -= 1
|
||||
p %= size
|
||||
self.point()
|
||||
|
||||
|
||||
class Graph(DObject):
|
||||
def __init__(self, writer, row, col, height, width, fgcolor, bgcolor, bdcolor, gridcolor):
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
|
||||
super().show() # Draw border
|
||||
self.x0 = col
|
||||
self.x1 = col + width
|
||||
self.y0 = row
|
||||
self.y1 = row + height
|
||||
if gridcolor is None:
|
||||
gridcolor = self.fgcolor
|
||||
self.gridcolor = gridcolor
|
||||
|
||||
def clear(self):
|
||||
self.show() # Clear working area
|
||||
|
||||
class CartesianGraph(Graph):
|
||||
def __init__(self, writer, row, col, *, height=90, width = 120, fgcolor=None, bgcolor=None, bdcolor=None,
|
||||
gridcolor=None, xdivs=10, ydivs=10, xorigin=5, yorigin=5):
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, gridcolor)
|
||||
self.xdivs = xdivs
|
||||
self.ydivs = ydivs
|
||||
self.x_axis_len = max(xorigin, xdivs - xorigin) * width / xdivs # Max distance from origin in pixels
|
||||
self.y_axis_len = max(yorigin, ydivs - yorigin) * height / ydivs
|
||||
self.xp_origin = self.x0 + xorigin * width / xdivs # Origin in pixels
|
||||
self.yp_origin = self.y0 + (ydivs - yorigin) * height / ydivs
|
||||
self.xorigin = xorigin
|
||||
self.yorigin = yorigin
|
||||
self.show()
|
||||
|
||||
def show(self):
|
||||
super().show() # Clear working area
|
||||
ssd = self.device
|
||||
x0 = self.x0
|
||||
x1 = self.x1
|
||||
y0 = self.y0
|
||||
y1 = self.y1
|
||||
if self.ydivs > 0:
|
||||
dy = self.height / (self.ydivs) # Y grid line
|
||||
for line in range(self.ydivs + 1):
|
||||
color = self.fgcolor if line == self.yorigin else self.gridcolor
|
||||
ypos = round(self.y1 - dy * line)
|
||||
ssd.hline(x0, ypos, x1 - x0, color)
|
||||
if self.xdivs > 0:
|
||||
width = x1 - x0
|
||||
dx = width / (self.xdivs) # X grid line
|
||||
for line in range(self.xdivs + 1):
|
||||
color = self.fgcolor if line == self.xorigin else self.gridcolor
|
||||
xpos = round(x0 + dx * line)
|
||||
ssd.vline(xpos, y0, y1 - y0, color)
|
||||
|
||||
# Called by Curve
|
||||
def line(self, start, end, color): # start and end relative to origin and scaled -1 .. 0 .. +1
|
||||
xs = round(self.xp_origin + start[0] * self.x_axis_len)
|
||||
ys = round(self.yp_origin - start[1] * self.y_axis_len)
|
||||
xe = round(self.xp_origin + end[0] * self.x_axis_len)
|
||||
ye = round(self.yp_origin - end[1] * self.y_axis_len)
|
||||
self.device.line(xs, ys, xe, ye, color)
|
||||
|
||||
class PolarGraph(Graph):
|
||||
def __init__(self, writer, row, col, *, height=90, fgcolor=None, bgcolor=None, bdcolor=None,
|
||||
gridcolor=None, adivs=3, rdivs=4):
|
||||
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor, gridcolor)
|
||||
self.adivs = adivs * 2 # No. of divisions of Pi radians
|
||||
self.rdivs = rdivs
|
||||
self.radius = round(height / 2) # Unit: pixels
|
||||
self.xp_origin = self.x0 + self.radius # Origin in pixels
|
||||
self.yp_origin = self.y0 + self.radius
|
||||
self.show()
|
||||
|
||||
def show(self):
|
||||
super().show() # Clear working area
|
||||
ssd = self.device
|
||||
x0 = self.x0
|
||||
y0 = self.y0
|
||||
radius = self.radius
|
||||
adivs = self.adivs
|
||||
rdivs = self.rdivs
|
||||
diam = 2 * radius
|
||||
if rdivs > 0:
|
||||
for r in range(1, rdivs + 1):
|
||||
circle(ssd, self.xp_origin, self.yp_origin, round(radius * r / rdivs), self.gridcolor)
|
||||
if adivs > 0:
|
||||
v = complex(1)
|
||||
m = rect(1, pi / adivs)
|
||||
for _ in range(adivs):
|
||||
self.cline(-v, v, self.gridcolor)
|
||||
v *= m
|
||||
ssd.vline(x0 + radius, y0, diam, self.fgcolor)
|
||||
ssd.hline(x0, y0 + radius, diam, self.fgcolor)
|
||||
|
||||
def cline(self, start, end, color): # start and end are complex, 0 <= magnitude <= 1
|
||||
height = self.radius # Unit: pixels
|
||||
xs = round(self.xp_origin + start.real * height)
|
||||
ys = round(self.yp_origin - start.imag * height)
|
||||
xe = round(self.xp_origin + end.real * height)
|
||||
ye = round(self.yp_origin - end.imag * height)
|
||||
self.device.line(xs, ys, xe, ye, color)
|
|
@ -0,0 +1,214 @@
|
|||
# fpt.py Test/demo program for framebuf plot
|
||||
# Uses Adafruit ssd1351-based OLED displays (change height to suit)
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
|
||||
# WIRING (Adafruit pin nos and names)
|
||||
# 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)
|
||||
|
||||
height = 96 # 1.27 inch 96*128 (rows*cols) display
|
||||
# height = 128 # 1.5 inch 128*128 display
|
||||
|
||||
import machine
|
||||
import gc
|
||||
from ssd1351 import SSD1351 as SSD
|
||||
|
||||
# Initialise hardware and framebuf before importing modules
|
||||
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)
|
||||
gc.collect() # Precaution befor instantiating framebuf
|
||||
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance
|
||||
|
||||
import cmath
|
||||
import math
|
||||
import utime
|
||||
import uos
|
||||
from writer import Writer, CWriter
|
||||
from fplot import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
|
||||
from nanogui import Label, refresh
|
||||
refresh(ssd)
|
||||
|
||||
# Fonts
|
||||
import arial10, freesans20
|
||||
|
||||
GREEN = SSD.rgb(0, 255, 0)
|
||||
RED = SSD.rgb(255, 0, 0)
|
||||
BLUE = SSD.rgb(0, 0, 255)
|
||||
YELLOW = SSD.rgb(255, 255, 0)
|
||||
WHITE = SSD.rgb(255, 255, 255)
|
||||
BLACK = 0
|
||||
LIGHTGREEN = SSD.rgb(0, 100, 0)
|
||||
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
|
||||
def cart():
|
||||
print('Cartesian data test.')
|
||||
def populate_1(func):
|
||||
x = -1
|
||||
while x < 1.01:
|
||||
yield x, func(x) # x, y
|
||||
x += 0.1
|
||||
|
||||
def populate_2():
|
||||
x = -1
|
||||
while x < 1.01:
|
||||
yield x, x**2 # x, y
|
||||
x += 0.1
|
||||
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = CartesianGraph(wri, 2, 2, yorigin = 2, fgcolor=WHITE, gridcolor=LIGHTGREEN) # Asymmetric y axis
|
||||
curve1 = Curve(g, YELLOW, populate_1(lambda x : x**3 + x**2 -x,)) # args demo
|
||||
curve2 = Curve(g, RED, populate_2())
|
||||
refresh(ssd)
|
||||
|
||||
def polar():
|
||||
print('Polar data test.')
|
||||
def populate():
|
||||
def f(theta):
|
||||
return cmath.rect(math.sin(3 * theta), theta) # complex
|
||||
nmax = 150
|
||||
for n in range(nmax + 1):
|
||||
yield f(2 * cmath.pi * n / nmax) # complex z
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = PolarGraph(wri, 2, 2, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curve = PolarCurve(g, YELLOW, populate())
|
||||
refresh(ssd)
|
||||
|
||||
def polar_clip():
|
||||
print('Test of polar data clipping.')
|
||||
def populate(rot):
|
||||
f = lambda theta : cmath.rect(1.15 * math.sin(5 * theta), theta) * rot # complex
|
||||
nmax = 150
|
||||
for n in range(nmax + 1):
|
||||
yield f(2 * cmath.pi * n / nmax) # complex z
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = PolarGraph(wri, 2, 2, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curve = PolarCurve(g, YELLOW, populate(1))
|
||||
curve1 = PolarCurve(g, RED, populate(cmath.rect(1, cmath.pi/5),))
|
||||
refresh(ssd)
|
||||
|
||||
def rt_polar():
|
||||
print('Simulate realtime polar data acquisition.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = PolarGraph(wri, 2, 2, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curvey = PolarCurve(g, YELLOW)
|
||||
curver = PolarCurve(g, RED)
|
||||
for x in range(100):
|
||||
curvey.point(cmath.rect(x/100, -x * cmath.pi/30))
|
||||
curver.point(cmath.rect((100 - x)/100, -x * cmath.pi/30))
|
||||
utime.sleep_ms(60)
|
||||
refresh(ssd)
|
||||
|
||||
def rt_rect():
|
||||
print('Simulate realtime data acquisition of discontinuous data.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = CartesianGraph(wri, 2, 2, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curve = Curve(g, RED)
|
||||
x = -1
|
||||
for _ in range(40):
|
||||
y = 0.1/x if abs(x) > 0.05 else None # Discontinuity
|
||||
curve.point(x, y)
|
||||
utime.sleep_ms(100)
|
||||
refresh(ssd)
|
||||
x += 0.05
|
||||
g.clear()
|
||||
curve = Curve(g, YELLOW)
|
||||
x = -1
|
||||
for _ in range(40):
|
||||
y = -0.1/x if abs(x) > 0.05 else None # Discontinuity
|
||||
curve.point(x, y)
|
||||
utime.sleep_ms(100)
|
||||
refresh(ssd)
|
||||
x += 0.05
|
||||
|
||||
|
||||
def lem():
|
||||
print('Lemniscate of Bernoulli.')
|
||||
def populate():
|
||||
t = -math.pi
|
||||
while t <= math.pi + 0.1:
|
||||
x = 0.5*math.sqrt(2)*math.cos(t)/(math.sin(t)**2 + 1)
|
||||
y = math.sqrt(2)*math.cos(t)*math.sin(t)/(math.sin(t)**2 + 1)
|
||||
yield x, y
|
||||
t += 0.1
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
Label(wri, 82, 2, 'To infinity and beyond...')
|
||||
g = CartesianGraph(wri, 2, 2, height = 75, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curve = Curve(g, YELLOW, populate())
|
||||
refresh(ssd)
|
||||
|
||||
def liss():
|
||||
print('Lissajous figure.')
|
||||
def populate():
|
||||
t = -math.pi
|
||||
while t <= math.pi:
|
||||
yield math.sin(t), math.cos(3*t) # x, y
|
||||
t += 0.1
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
g = CartesianGraph(wri, 2, 2, fgcolor=WHITE, gridcolor=LIGHTGREEN)
|
||||
curve = Curve(g, YELLOW, populate())
|
||||
refresh(ssd)
|
||||
|
||||
def seq():
|
||||
print('Time sequence test - sine and cosine.')
|
||||
refresh(ssd, True) # Clear any prior image
|
||||
# y axis at t==now, no border
|
||||
g = CartesianGraph(wri, 2, 2, xorigin = 10, fgcolor=WHITE,
|
||||
gridcolor=LIGHTGREEN, bdcolor=False)
|
||||
tsy = TSequence(g, YELLOW, 50)
|
||||
tsr = TSequence(g, RED, 50)
|
||||
for t in range(100):
|
||||
g.clear()
|
||||
tsy.add(0.9*math.sin(t/10))
|
||||
tsr.add(0.4*math.cos(t/10))
|
||||
refresh(ssd)
|
||||
utime.sleep_ms(100)
|
||||
|
||||
seq()
|
||||
utime.sleep(1.5)
|
||||
liss()
|
||||
utime.sleep(1.5)
|
||||
rt_rect()
|
||||
utime.sleep(1.5)
|
||||
rt_polar()
|
||||
utime.sleep(1.5)
|
||||
polar()
|
||||
utime.sleep(1.5)
|
||||
cart()
|
||||
utime.sleep(1.5)
|
||||
polar_clip()
|
||||
utime.sleep(1.5)
|
||||
lem()
|
Ładowanie…
Reference in New Issue