kopia lustrzana https://github.com/peterhinch/micropython-samples
tft_gui now in separate repository
rodzic
84c7a5df4b
commit
6571dc8104
|
@ -1,150 +0,0 @@
|
||||||
# micropython-gui
|
|
||||||
|
|
||||||
Provides a simple touch driven event based GUI interface for the Pyboard when used with a TFT
|
|
||||||
display. The latter should be based on SSD1963 controller with XPT2046 touch controller. Such
|
|
||||||
displays are available in electronics stores and on eBay. The software is based on drivers for the
|
|
||||||
TFT and touch controller from Robert Hammelrath together with a cooperative scheduler of my own
|
|
||||||
design.
|
|
||||||
|
|
||||||
It is targeted at hardware control and display applications.
|
|
||||||
|
|
||||||
# Pre requisites
|
|
||||||
|
|
||||||
[TFT driver](https://github.com/robert-hh/SSD1963-TFT-Library-for-PyBoard.git)
|
|
||||||
[XPT2046 driver](https://github.com/robert-hh/XPT2046-touch-pad-driver-for-PyBoard.git)
|
|
||||||
[Scheduler](https://github.com/peterhinch/Micropython-scheduler.git)
|
|
||||||
|
|
||||||
Core files:
|
|
||||||
1. TFT_io.py Low level TFT driver *
|
|
||||||
2. touch.py Touch controller driver *
|
|
||||||
3. tft.py TFT driver
|
|
||||||
4. usched.py Scheduler
|
|
||||||
5. delay.py Used with the scheduler for watchdog type delays.
|
|
||||||
6. ugui.py The micro GUI library.
|
|
||||||
|
|
||||||
Optional files:
|
|
||||||
1. font10.py Font used by the test programs.
|
|
||||||
2. font14.py Font used by the test programs.
|
|
||||||
|
|
||||||
Test/demo programs:
|
|
||||||
1. vst.py A test program for vertical linear sliders.
|
|
||||||
2. hst.py Tests horizontal slider controls, meters and LED.
|
|
||||||
3. buttontest.py Pushbuttons and checkboxes.
|
|
||||||
4. knobtest.py Rotary control test.
|
|
||||||
|
|
||||||
It should be noted that by the standards of the Pyboard this is a large library. Attempts to use it
|
|
||||||
in the normal way are likely to provoke memory errors owing to heap fragmentation. It is
|
|
||||||
recommended that the core and optional files are included with the firmware as persistent bytecode.
|
|
||||||
You may also want to include any other fonts you plan to use. The first two core files listed above
|
|
||||||
cannot be included as they use inline assembler. Instructions on how to do this may be found
|
|
||||||
[here](http://forum.micropython.org/viewtopic.php?f=6&t=1776).
|
|
||||||
|
|
||||||
It is also wise to issue ctrl-D to soft reset the Pyboard before importing a module which uses the
|
|
||||||
library. The test programs require a ctrl-D before import.
|
|
||||||
|
|
||||||
Instructions on creating font files may be found in the README for the TFT driver listed above.
|
|
||||||
|
|
||||||
# Concepts
|
|
||||||
|
|
||||||
### Coordinates
|
|
||||||
|
|
||||||
In common with most displays, the top left hand corner of the display is (0, 0) with increasing
|
|
||||||
values of x to the right, and increasing values of y downward. Display objects exist within a
|
|
||||||
rectangular bounding box; in the case of touch sensitive controls this corresponds to the sensitive
|
|
||||||
region. The location of the object is defined as the coordinates of the top left hand corner of the
|
|
||||||
bounding box. Locations are defined as a 2-tuple (x, y).
|
|
||||||
|
|
||||||
### Colours
|
|
||||||
|
|
||||||
These are defined as a 3-tuple (r, g, b) with values of red, green and blue in range 0 to 255. The
|
|
||||||
interface uses the American spelling (color) throughout for consistency with the TFT library.
|
|
||||||
|
|
||||||
### Callbacks
|
|
||||||
|
|
||||||
The interface is event driven. Optional callbacks may be provided which will be executed when a
|
|
||||||
given event occurs. A callback function receives positional arguments. The first is a reference to
|
|
||||||
the object raising the callback. Subsequent arguments are user defined, and are specified as a list
|
|
||||||
of items. Note that a list rather than a tuple should be used.
|
|
||||||
|
|
||||||
# Initialisation Code
|
|
||||||
|
|
||||||
# Displays
|
|
||||||
|
|
||||||
These classes provide ways to display data and are not touch sensitive.
|
|
||||||
|
|
||||||
## Class Label
|
|
||||||
|
|
||||||
Displays text in a fixed length field. Constructor mandatory positional arguments:
|
|
||||||
1. ``tft`` The TFT object.
|
|
||||||
2. ``location`` 2-tuple defining position.
|
|
||||||
Keyword only arguments:
|
|
||||||
1. ``font`` Mandatory. Font object to use.
|
|
||||||
2. ``width`` Mandatory. The width of the object in pixels.
|
|
||||||
3. ``border`` Border width in pixels - typically 2. If omitted, no border will be drawn.
|
|
||||||
4. ``fgcolor`` Color of border. Defaults to system color.
|
|
||||||
5. ``bgcolor`` Background color of object. Defaults to system background.
|
|
||||||
6. ``fontcolor`` Text color. Defaults to system text color.
|
|
||||||
7. ``text`` Initial text. Defaults to ''.
|
|
||||||
Method:
|
|
||||||
1. ``show`` Argument: ``text``. Displays the string in the label.
|
|
||||||
|
|
||||||
## Class Dial
|
|
||||||
|
|
||||||
Displays angles in a circular dial. Angles are in radians with zero represented by a vertical
|
|
||||||
pointer. Positive angles appear as clockwise rotation of the pointer. The object can display
|
|
||||||
multiple angles using pointers of differing lengths (e.g. clock face). Constructor mandatory
|
|
||||||
positional arguments:
|
|
||||||
1. ``tft`` The TFT object.
|
|
||||||
2. ``location`` 2-tuple defining position.
|
|
||||||
Keyword only arguments (all
|
|
||||||
1. ``height`` Dimension of the square bounding box. Default 100 pixels.
|
|
||||||
2. ``fgcolor`` Color of border. Defaults to system color.
|
|
||||||
3. ``bgcolor`` Background color of object. Defaults to system background.
|
|
||||||
4. ``border`` Border width in pixels - typically 2. If omitted, no border will be drawn.
|
|
||||||
5. ``pointers`` Tuple of floats in range 0 to 0.9. Defines the length of each pointer as a
|
|
||||||
proportion of the dial diameter. Default (0.9,) i.e. one pointer.
|
|
||||||
6. ``ticks`` Defines the number of graduations around the dial. Default 4.
|
|
||||||
Method:
|
|
||||||
1. ``show`` Displays an angle. Arguments: ``angle`` (mandatory), ``pointer`` the pointer index
|
|
||||||
(default 0).
|
|
||||||
|
|
||||||
## Class LED
|
|
||||||
|
|
||||||
Displays a boolean state. Can display other information by varying the color. Constructor mandatory
|
|
||||||
positional arguments:
|
|
||||||
1. ``tft`` The TFT object.
|
|
||||||
2. ``location`` 2-tuple defining position.
|
|
||||||
Keyword only arguments:
|
|
||||||
1. ``height`` Dimension of the square bounding box. Default 30 pixels.
|
|
||||||
2. ``fgcolor`` Color of border. Defaults to system color.
|
|
||||||
3. ``bgcolor`` Background color of object. Defaults to system background.
|
|
||||||
4. ``border`` Border width in pixels - typically 2. If omitted, no border will be drawn.
|
|
||||||
5. ``color`` The color of the LED.
|
|
||||||
Methods:
|
|
||||||
1. ``off`` No arguments. Turns the LED off.
|
|
||||||
2. ``on`` Optional arguemnt ``color``. Turns the LED on. By default it will use the ``color``
|
|
||||||
specified in the constructor.
|
|
||||||
|
|
||||||
## Class Meter
|
|
||||||
|
|
||||||
This displays a single value in range 0.0 to 1.0 on a vertical linear meter. Constructor mandatory
|
|
||||||
positional arguments:
|
|
||||||
1. ``tft`` The TFT object.
|
|
||||||
2. ``location`` 2-tuple defining position.
|
|
||||||
Keyword only arguments:
|
|
||||||
1. ``height`` Dimension of the bounding box. Default 200 pixels.
|
|
||||||
2. ``width`` Dimension of the bounding box. Default 30 pixels.
|
|
||||||
3. ``font`` Font to use in any legends. Default: ``None`` No legends will be displayed.
|
|
||||||
4. ``legends`` A tuple of strings to display on the centreline of the meter. These should be
|
|
||||||
short to physically fit. They will be displayed equidistantly along the vertical scale, with
|
|
||||||
string 0 at the bottom. Default ``None``: no legends will be shown.
|
|
||||||
5. ``divisions`` Count of graduations on the meter scale. Default 10.
|
|
||||||
6. ``fgcolor`` Color of border. Defaults to system color.
|
|
||||||
7. ``bgcolor`` Background color of object. Defaults to system background.
|
|
||||||
8. ``fontcolor`` Text color. Defaults to system text color.
|
|
||||||
9. ``pointercolor`` Color of meter pointer. Defaults to ``fgcolor``.
|
|
||||||
10. ``value`` Initial value to display. Default 0.
|
|
||||||
Methods:
|
|
||||||
1.``value`` Optional argument ``val``. If set, refreshes the meter display with a new value,
|
|
||||||
otherwise returns its current value.
|
|
||||||
|
|
|
@ -1,925 +0,0 @@
|
||||||
#
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 Robert Hammelrath
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# Low level I/O drivers for the class supporting TFT LC-displays
|
|
||||||
# with a parallel Interface
|
|
||||||
# First example: Controller SSD1963
|
|
||||||
# It uses X1..X8 for data and Y3, Y9, Y10, Y11 and Y12 for control signals.
|
|
||||||
# The minimal connection is:
|
|
||||||
# X1..X8 for data, Y9 for /Reset, Y10 for /RD, Y11 for /WR and Y12 for /RS
|
|
||||||
# Then LED must be hard tied to Vcc and /CS to GND.
|
|
||||||
#
|
|
||||||
|
|
||||||
import pyb, stm
|
|
||||||
from uctypes import addressof
|
|
||||||
|
|
||||||
# define constants
|
|
||||||
#
|
|
||||||
RESET = const(1 << 10) ## Y9
|
|
||||||
RD = const(1 << 11) ## Y10
|
|
||||||
WR = const(0x01) ## Y11
|
|
||||||
D_C = const(0x02) ## Y12
|
|
||||||
|
|
||||||
LED = const(1 << 8) ## Y3
|
|
||||||
POWER = const(1 << 9) ## Y4
|
|
||||||
|
|
||||||
## CS is not used and must be hard tied to GND
|
|
||||||
|
|
||||||
PORTRAIT = const(1)
|
|
||||||
LANDSCAPE = const(0)
|
|
||||||
|
|
||||||
#
|
|
||||||
# display font bitmap for text
|
|
||||||
#
|
|
||||||
@micropython.viper
|
|
||||||
def displaySCR_charbitmap(bits: ptr8, size: int, control: ptr8, bg_buf: ptr8):
|
|
||||||
gpioa = ptr8(stm.GPIOA)
|
|
||||||
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
|
|
||||||
#
|
|
||||||
transparency = control[6]
|
|
||||||
bm_ptr = 0
|
|
||||||
bg_ptr = 0
|
|
||||||
mask = 0x80
|
|
||||||
#
|
|
||||||
while size:
|
|
||||||
|
|
||||||
if bits[bm_ptr] & mask:
|
|
||||||
if transparency & 8: # Invert bg color as foreground
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 1] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 2] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
else: # not invert
|
|
||||||
gpioa[stm.GPIO_ODR] = control[3] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = control[4] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = control[5] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
else:
|
|
||||||
if transparency & 1: # Dim background
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr] >> 1 # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 1] >> 1 # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 2] >> 1 # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
elif transparency & 2: # keep Background
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 1] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 2] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
elif transparency & 4: # invert Background
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 1] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 2] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
else: # not transparent
|
|
||||||
gpioa[stm.GPIO_ODR] = control[0] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = control[1] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = control[2] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
mask >>= 1
|
|
||||||
if mask == 0: # mask reset & data ptr advance on byte exhaust
|
|
||||||
mask = 0x80
|
|
||||||
bm_ptr += 1
|
|
||||||
size -= 1
|
|
||||||
bg_ptr += 3
|
|
||||||
#
|
|
||||||
# display Windows BMP data, optionally with colortables
|
|
||||||
#
|
|
||||||
@micropython.viper
|
|
||||||
def displaySCR_bmp(data: ptr8, size: int, bits: int, colortable: ptr8):
|
|
||||||
gpioa = ptr8(stm.GPIOA)
|
|
||||||
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
|
|
||||||
#
|
|
||||||
bm_ptr = 0
|
|
||||||
shift = 8 - bits
|
|
||||||
mask = ((1 << bits) - 1) << shift
|
|
||||||
#
|
|
||||||
while size:
|
|
||||||
|
|
||||||
offset = ((data[bm_ptr] & mask) >> shift) * 4
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = colortable[offset + 2] # Red
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = colortable[offset + 1] # green
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
gpioa[stm.GPIO_ODR] = colortable[offset + 0] # blue
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
|
|
||||||
mask >>= bits
|
|
||||||
shift -= bits
|
|
||||||
if mask == 0: # mask rebuild & data ptr advance on byte exhaust
|
|
||||||
shift = 8 - bits
|
|
||||||
mask = ((1 << bits) - 1) << shift
|
|
||||||
bm_ptr += 1
|
|
||||||
size -= 1
|
|
||||||
#
|
|
||||||
# Set the address range for various draw commands and set the TFT for expecting data
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Assembler version of
|
|
||||||
# SetXY: takes net about 6 µs including the call
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def setXY_L(r0, r1, r2, r3):
|
|
||||||
# r0: x1, r1: y1, r2: x2, r3: y2
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r4: changing data
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2a)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, 8)
|
|
||||||
mov(r4, r0) # get x1
|
|
||||||
asr(r4, r5) # get the upper byte
|
|
||||||
mov(r5, WR)
|
|
||||||
strb(r4, [r6, 0]) # Store upper x1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r0, 8) # from here on r0 keeps 8
|
|
||||||
mov(r4, r2) # get x2
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper x2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r2, [r6, 0]) # Store lower x2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2b)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, WR)
|
|
||||||
mov(r4, r1) # get y1
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r4, r3) # get x2
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r3, [r6, 0]) # Store lower y2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2c)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# and done
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def setXY_P(r0, r1, r2, r3):
|
|
||||||
# r0: x1, r1: y1, r2: x2, r3: y2
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r4: changing data
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2b)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, 8)
|
|
||||||
mov(r4, r0) # get x1
|
|
||||||
asr(r4, r5) # get the upper byte
|
|
||||||
mov(r5, WR)
|
|
||||||
strb(r4, [r6, 0]) # Store upper x1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r0, 8) # from here on r0 keeps 8
|
|
||||||
mov(r4, r2) # get x2
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper x2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r2, [r6, 0]) # Store lower x2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2a)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, WR)
|
|
||||||
mov(r4, r1) # get y1
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y1
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r4, r3) # get x2
|
|
||||||
asr(r4, r0) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r3, [r6, 0]) # Store lower y2
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2c)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# and done
|
|
||||||
#
|
|
||||||
# Assembler version of
|
|
||||||
# drawPixel, Landscape
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def drawPixel_L(r0, r1, r2):
|
|
||||||
# r0: x, r1: y, r2: colorvector
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r4: changing data
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2a)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r3, 8)
|
|
||||||
mov(r4, r0) # get x
|
|
||||||
asr(r4, r3) # get the upper byte
|
|
||||||
mov(r5, WR)
|
|
||||||
strb(r4, [r6, 0]) # Store upper x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r4, [r6, 0]) # Store upper x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2b)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, WR)
|
|
||||||
mov(r4, r1) # get y
|
|
||||||
asr(r4, r3) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r4, [r6, 0]) # Store upper y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2c)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
mov(r5, WR)
|
|
||||||
# Send color
|
|
||||||
ldrb(r4, [r2, 0]) # red
|
|
||||||
strb(r4, [r6, 0]) # Store red
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r4, [r2, 1]) # green
|
|
||||||
strb(r4, [r6, 0]) # store greem
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r4, [r2, 2]) # blue
|
|
||||||
strb(r4, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
# and done
|
|
||||||
#
|
|
||||||
# Assembler version of
|
|
||||||
# drawPixel, Portrait
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def drawPixel_P(r0, r1, r2):
|
|
||||||
# r0: x, r1: y, r2: colorvector
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r4: changing data
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2b)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r3, 8)
|
|
||||||
mov(r4, r0) # get x
|
|
||||||
asr(r4, r3) # get the upper byte
|
|
||||||
mov(r5, WR)
|
|
||||||
strb(r4, [r6, 0]) # Store upper x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r4, [r6, 0]) # Store upper x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r0, [r6, 0]) # Store lower x
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2a)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
mov(r5, WR)
|
|
||||||
mov(r4, r1) # get y
|
|
||||||
asr(r4, r3) # get the upper byte
|
|
||||||
strb(r4, [r6, 0]) # Store upper y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r4, [r6, 0]) # Store upper y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
|
|
||||||
strb(r1, [r6, 0]) # Store lower y
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
mov (r4, 0x2c)
|
|
||||||
strb(r4, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
mov(r5, WR)
|
|
||||||
# Send color
|
|
||||||
ldrb(r4, [r2, 0]) # red
|
|
||||||
strb(r4, [r6, 0]) # Store red
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r4, [r2, 1]) # green
|
|
||||||
strb(r4, [r6, 0]) # store greem
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r4, [r2, 2]) # blue
|
|
||||||
strb(r4, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
# Assembler version of
|
|
||||||
# Fill screen by writing size pixels with the color given in data
|
|
||||||
# data must be 3 bytes of red, green, blue
|
|
||||||
# The area to be filled has to be set in advance by setXY
|
|
||||||
# The speed is about 214 ns/pixel
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def fillSCR_AS(r0, r1): # r0: ptr to data, r1: number of pixels (3 bytes/pixel)
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
mov(r5, WR)
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
ldrb(r2, [r0, 0]) # red
|
|
||||||
ldrb(r3, [r0, 1]) # green
|
|
||||||
ldrb(r4, [r0, 2]) # blue
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
strb(r2, [r6, 0]) # Store red
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
# nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
strb(r3, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
strb(r4, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
# nop()
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
#
|
|
||||||
# Assembler version of:
|
|
||||||
# Fill screen by writing size pixels with the data
|
|
||||||
# data must contains size triplets of red, green and blue data values
|
|
||||||
# The area to be filled has to be set in advance by setXY
|
|
||||||
# the speed is 266 ns for a byte triple
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def displaySCR_AS(r0, r1): # r0: ptr to data, r1: is number of pixels (3 bytes/pixel)
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
mov(r5, WR)
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
ldrb(r2, [r0, 0]) # red
|
|
||||||
strb(r2, [r6, 0]) # Store red
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r2, [r0, 1]) # pre green
|
|
||||||
strb(r2, [r6, 0]) # store greem
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r2, [r0, 2]) # blue
|
|
||||||
strb(r2, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
add (r0, 3) # advance data ptr
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
# Assembler version of:
|
|
||||||
# Fill screen by writing size pixels with the data
|
|
||||||
# data must contains size packed duplets of red, green and blue data values
|
|
||||||
# The area to be filled has to be set in advance by setXY
|
|
||||||
# the speed is 266 ns for a byte pixel
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def displaySCR565_AS(r0, r1): # r0: ptr to data, r1: is number of pixels (3 bytes/pixel)
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
mov(r5, WR)
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
|
|
||||||
ldrb(r2, [r0, 0]) # red
|
|
||||||
mov (r3, 0xf8) # mask out lower 3 bits
|
|
||||||
and_(r2, r3)
|
|
||||||
strb(r2, [r6, 0]) # Store red
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r2, [r0, 0]) # pre green
|
|
||||||
mov (r3, 5) # shift 5 bits up to
|
|
||||||
lsl(r2, r3)
|
|
||||||
ldrb(r4, [r0, 1]) # get the next 3 bits
|
|
||||||
mov (r3, 3) # shift 3 to the right
|
|
||||||
lsr(r4, r3)
|
|
||||||
orr(r2, r4) # add them to the first bits
|
|
||||||
mov(r3, 0xfc) # mask off the lower two bits
|
|
||||||
and_(r2, r3)
|
|
||||||
strb(r2, [r6, 0]) # store green
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
ldrb(r2, [r0, 1]) # blue
|
|
||||||
mov (r3, 3)
|
|
||||||
lsl(r2, r3)
|
|
||||||
strb(r2, [r6, 0]) # store blue
|
|
||||||
strb(r5, [r7, 2]) # WR low
|
|
||||||
strb(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
add (r0, 2) # advance data ptr
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
#
|
|
||||||
# Send a command and data to the TFT controller
|
|
||||||
# cmd is the command byte, data must be a bytearray object with the command payload,
|
|
||||||
# int is the size of the data
|
|
||||||
# For the startup-phase use this function.
|
|
||||||
#
|
|
||||||
@micropython.viper
|
|
||||||
def tft_cmd_data(cmd: int, data: ptr8, size: int):
|
|
||||||
gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR)
|
|
||||||
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
|
|
||||||
gpioa[0] = cmd # set data on port A
|
|
||||||
gpiob[1] = D_C | WR # set C/D and WR low
|
|
||||||
gpiob[0] = D_C | WR # set C/D and WR high
|
|
||||||
for i in range(size):
|
|
||||||
gpioa[0] = data[i] # set data on port A
|
|
||||||
gpiob[1] = WR # set WR low. C/D still high
|
|
||||||
gpiob[0] = WR # set WR high again
|
|
||||||
#
|
|
||||||
# Assembler version of send command & data to the TFT controller
|
|
||||||
# data must be a bytearray object, int is the size of the data.
|
|
||||||
# The speed is about 120 ns/byte
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def tft_cmd_data_AS(r0, r1, r2): # r0: command, r1: ptr to data, r2 is size in bytes
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
mov(r5, WR | D_C)
|
|
||||||
strb(r0, [r6, 0]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# now loop though data
|
|
||||||
mov(r5, WR)
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
ldrb(r4, [r1, 0]) # load data
|
|
||||||
strb(r4, [r6, 0]) # Store data
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
strh(r5, [r7, 0]) # WR high
|
|
||||||
add (r1, 1) # advance data ptr
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r2, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
#
|
|
||||||
# Send a command to the TFT controller
|
|
||||||
#
|
|
||||||
@micropython.viper
|
|
||||||
def tft_cmd(cmd: int):
|
|
||||||
gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR)
|
|
||||||
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
|
|
||||||
gpioa[0] = cmd # set data on port A
|
|
||||||
gpiob[1] = D_C | WR # set C/D and WR low
|
|
||||||
gpiob[0] = D_C | WR # set C/D and WR high
|
|
||||||
#
|
|
||||||
# Assembler version of send data to the TFT controller
|
|
||||||
# data must be a bytearray object, int is the size of the data.
|
|
||||||
# The speed is about 120 ns/byte
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def tft_write_data_AS(r0, r1): # r0: ptr to data, r1: is size in Bytes
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA ODR register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
add (r6, stm.GPIO_ODR)
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
mov(r5, WR)
|
|
||||||
# and go, first test size for 0
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
ldrb(r3, [r0, 0]) # load data
|
|
||||||
strb(r3, [r6, 0]) # Store data
|
|
||||||
strh(r5, [r7, 2]) # WR low
|
|
||||||
strh(r5, [r7, 0]) # WR high
|
|
||||||
|
|
||||||
add (r0, 1) # advance data ptr
|
|
||||||
label(loopend)
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
#
|
|
||||||
# Assembler version of send a command byte and read data from to the TFT controller
|
|
||||||
# data must be a bytearray object, int is the size of the data.
|
|
||||||
# The speed is about 130 ns/byte
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def tft_read_cmd_data_AS(r0, r1, r2):
|
|
||||||
# r0: command, r1: ptr to data buffer, r2 is expected size in bytes
|
|
||||||
# set up pointers to GPIO
|
|
||||||
# r5: bit mask for control lines
|
|
||||||
# r6: GPIOA base register ptr
|
|
||||||
# r7: GPIOB BSSRL register ptr
|
|
||||||
movwt(r6, stm.GPIOA) # target
|
|
||||||
movwt(r7, stm.GPIOB)
|
|
||||||
add (r7, stm.GPIO_BSRRL)
|
|
||||||
# Emit command byte
|
|
||||||
movw(r5, WR | D_C)
|
|
||||||
strb(r0, [r6, stm.GPIO_ODR]) # set command byte
|
|
||||||
strh(r5, [r7, 2]) # WR and D_C low
|
|
||||||
strh(r5, [r7, 0]) # WR and D_C high
|
|
||||||
# now switch gpioaa to input
|
|
||||||
movw(r0, 0)
|
|
||||||
strh(r0, [r6, stm.GPIO_MODER])
|
|
||||||
# now loop though data
|
|
||||||
movw(r5, RD)
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
strh(r5, [r7, 2]) # RD low
|
|
||||||
nop() # short delay
|
|
||||||
nop()
|
|
||||||
ldrb(r4, [r6, stm.GPIO_IDR]) # load data
|
|
||||||
strh(r5, [r7, 0]) # RD high
|
|
||||||
strb(r4, [r1, 0]) # Store data
|
|
||||||
add (r1, 1) # advance data ptr
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r2, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
# now switch gpioaa back to input
|
|
||||||
movw(r0, 0x5555)
|
|
||||||
strh(r0, [r6, stm.GPIO_MODER])
|
|
||||||
#
|
|
||||||
# swap byte pairs in a buffer
|
|
||||||
# sometimes needed for picture data
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def swapbytes(r0, r1): # bytearray, len(bytearray)
|
|
||||||
mov(r2, 1) # divide loop count by 2
|
|
||||||
lsr(r1, r2) # to avoid odd valued counter
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
ldrb(r2, [r0, 0])
|
|
||||||
ldrb(r3, [r0, 1])
|
|
||||||
strb(r3, [r0, 0])
|
|
||||||
strb(r2, [r0, 1])
|
|
||||||
add(r0, 2)
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
|
|
||||||
#
|
|
||||||
# swap colors red/blue in the buffer
|
|
||||||
#
|
|
||||||
@micropython.asm_thumb
|
|
||||||
def swapcolors(r0, r1): # bytearray, len(bytearray)
|
|
||||||
mov(r2, 3)
|
|
||||||
udiv(r1, r1, r2) # 3 bytes per triple
|
|
||||||
b(loopend)
|
|
||||||
|
|
||||||
label(loopstart)
|
|
||||||
ldrb(r2, [r0, 0])
|
|
||||||
ldrb(r3, [r0, 2])
|
|
||||||
strb(r3, [r0, 0])
|
|
||||||
strb(r2, [r0, 2])
|
|
||||||
add(r0, 3)
|
|
||||||
|
|
||||||
label(loopend)
|
|
||||||
sub (r1, 1) # End of loop?
|
|
||||||
bpl(loopstart)
|
|
||||||
|
|
|
@ -1,139 +0,0 @@
|
||||||
# buttontest.py Test/demo of pushbutton classes for Pybboard TFT GUI
|
|
||||||
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 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 font14 import font14
|
|
||||||
from tft import TFT, LANDSCAPE
|
|
||||||
from usched import Sched
|
|
||||||
from touch import TOUCH
|
|
||||||
from ugui import Button, Buttonset, RadioButtons, Checkbox, Label
|
|
||||||
from ugui import CIRCLE, RECTANGLE, CLIPPED_RECT, WHITE, BLACK, RED, GREEN, BLUE, YELLOW, GREY
|
|
||||||
|
|
||||||
def callback(button, arg, label):
|
|
||||||
label.show(arg)
|
|
||||||
if arg == 'Q':
|
|
||||||
button.objsched.stop()
|
|
||||||
|
|
||||||
def cbcb(checkbox, label):
|
|
||||||
if checkbox.value():
|
|
||||||
label.show('True')
|
|
||||||
else:
|
|
||||||
label.show('False')
|
|
||||||
|
|
||||||
# These tables contain args that differ between members of a set of related buttons
|
|
||||||
table = [
|
|
||||||
{'fgcolor' : GREEN, 'text' : 'Yes', 'args' : ['A'], 'fontcolor' : (0, 0, 0)},
|
|
||||||
{'fgcolor' : RED, 'text' : 'No', 'args' : ['B']},
|
|
||||||
{'fgcolor' : BLUE, 'text' : '???', 'args' : ['C'], 'fill': False},
|
|
||||||
{'fgcolor' : GREY, 'text' : 'Quit', 'args' : ['Q'], 'shape' : CLIPPED_RECT},
|
|
||||||
]
|
|
||||||
|
|
||||||
# similar buttons: only tabulate data that varies
|
|
||||||
table2 = [
|
|
||||||
{'text' : 'P', 'args' : ['p']},
|
|
||||||
{'text' : 'Q', 'args' : ['q']},
|
|
||||||
{'text' : 'R', 'args' : ['r']},
|
|
||||||
{'text' : 'S', 'args' : ['s']},
|
|
||||||
]
|
|
||||||
|
|
||||||
# A Buttonset with two entries
|
|
||||||
# If buttons to be used in a buttonset, Use list rather than tuple for args because buttonset appends.
|
|
||||||
|
|
||||||
table3 = [
|
|
||||||
{'fgcolor' : GREEN, 'shape' : CLIPPED_RECT, 'text' : 'Start', 'args' : ['Live']},
|
|
||||||
{'fgcolor' : RED, 'shape' : CLIPPED_RECT, 'text' : 'Stop', 'args' : ['Die']},
|
|
||||||
]
|
|
||||||
|
|
||||||
table4 = [
|
|
||||||
{'text' : '1', 'args' : ['1']},
|
|
||||||
{'text' : '2', 'args' : ['2']},
|
|
||||||
{'text' : '3', 'args' : ['3']},
|
|
||||||
{'text' : '4', 'args' : ['4']},
|
|
||||||
]
|
|
||||||
|
|
||||||
labels = { 'width' : 70,
|
|
||||||
'fontcolor' : WHITE,
|
|
||||||
'border' : 2,
|
|
||||||
'fgcolor' : RED,
|
|
||||||
'bgcolor' : (0, 40, 0),
|
|
||||||
'font' : font14,
|
|
||||||
}
|
|
||||||
|
|
||||||
# USER TEST FUNCTION
|
|
||||||
|
|
||||||
def cbtest(checkbox):
|
|
||||||
while True:
|
|
||||||
yield 3
|
|
||||||
checkbox.value(not checkbox.value())
|
|
||||||
|
|
||||||
def test():
|
|
||||||
print('Testing TFT...')
|
|
||||||
objsched = Sched() # Instantiate the scheduler
|
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
|
||||||
mytouch = TOUCH("XPT2046", objsched, confidence = 50, margin = 50)
|
|
||||||
mytft.backlight(100) # light on
|
|
||||||
lstlbl = []
|
|
||||||
for n in range(3):
|
|
||||||
lstlbl.append(Label(mytft, (350, 50 * n), **labels))
|
|
||||||
|
|
||||||
# Button assortment
|
|
||||||
x = 0
|
|
||||||
for t in table:
|
|
||||||
t['args'].append(lstlbl[2])
|
|
||||||
Button(objsched, mytft, mytouch, (x, 0), font = font14, callback = callback, **t)
|
|
||||||
x += 70
|
|
||||||
|
|
||||||
# Highlighting buttons
|
|
||||||
x = 0
|
|
||||||
for t in table2:
|
|
||||||
t['args'].append(lstlbl[2])
|
|
||||||
Button(objsched, mytft, mytouch, (x, 60), fgcolor = GREY,
|
|
||||||
fontcolor = BLACK, litcolor = WHITE, font = font14, callback = callback, **t)
|
|
||||||
x += 70
|
|
||||||
|
|
||||||
# On/Off toggle
|
|
||||||
x = 0
|
|
||||||
bs = Buttonset(callback)
|
|
||||||
for t in table3: # Buttons overlay each other at same location
|
|
||||||
t['args'].append(lstlbl[2])
|
|
||||||
bs.add_button(objsched, mytft, mytouch, (x, 120), font = font14, fontcolor = BLACK, **t)
|
|
||||||
bs.run()
|
|
||||||
|
|
||||||
# Radio buttons
|
|
||||||
x = 0
|
|
||||||
rb = RadioButtons(callback, BLUE) # color of selected button
|
|
||||||
for t in table4:
|
|
||||||
t['args'].append(lstlbl[2])
|
|
||||||
rb.add_button(objsched, mytft, mytouch, (x, 180), font = font14, fontcolor = WHITE,
|
|
||||||
fgcolor = (0, 0, 90), height = 40, **t)
|
|
||||||
x += 60
|
|
||||||
rb.run()
|
|
||||||
|
|
||||||
# Checkbox
|
|
||||||
Checkbox(objsched, mytft, mytouch, (300, 0), callback = cbcb, args = [lstlbl[0]])
|
|
||||||
cb2 = Checkbox(objsched, mytft, mytouch, (300, 50), fillcolor = RED, callback = cbcb, args = [lstlbl[1]])
|
|
||||||
|
|
||||||
objsched.add_thread(cbtest(cb2)) # Toggle every 2 seconds
|
|
||||||
objsched.run() # Run it!
|
|
||||||
|
|
||||||
test()
|
|
|
@ -1,42 +0,0 @@
|
||||||
# A time delay class for the micropython board. Based on the scheduler class. 24th Aug 2014
|
|
||||||
# Author: Peter Hinch
|
|
||||||
# V1.0 25th Aug 2014
|
|
||||||
# Used by Pushbutton library.
|
|
||||||
# This class implements the software equivalent of a retriggerable monostable. When first instantiated
|
|
||||||
# a delay object does nothing until it trigger method is called. It then enters a running state until
|
|
||||||
# the specified time elapses when it calls the optional callback function and stops running.
|
|
||||||
# A running delay may be retriggered by calling its trigger function: its time to run is now specified
|
|
||||||
# by the passed value.
|
|
||||||
|
|
||||||
# The usual caveats re microsheduler time periods applies: if you need millisecond accuracy
|
|
||||||
# (or better) use a hardware timer. Times can easily be -0 +20mS or more, depending on other threads
|
|
||||||
|
|
||||||
from usched import Sched, microsWhen, seconds, after, microsUntil, Timeout
|
|
||||||
|
|
||||||
class Delay(object):
|
|
||||||
def __init__(self, objSched, callback = None, callback_args = ()):
|
|
||||||
self.objSched = objSched
|
|
||||||
self.callback = callback
|
|
||||||
self.callback_args = callback_args
|
|
||||||
self._running = False
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._running = False
|
|
||||||
|
|
||||||
def trigger(self, duration):
|
|
||||||
self.tstop = microsWhen(seconds(duration)) # Update end time
|
|
||||||
if not self._running: # Start a thread which stops the
|
|
||||||
self.objSched.add_thread(self.killer()) # delay after its period has elapsed
|
|
||||||
self._running = True
|
|
||||||
|
|
||||||
def running(self):
|
|
||||||
return self._running
|
|
||||||
|
|
||||||
def killer(self):
|
|
||||||
to = Timeout(1) # Initial value is arbitrary
|
|
||||||
while not after(self.tstop): # Might have been retriggered
|
|
||||||
yield to._ussetdelay(microsUntil(self.tstop))
|
|
||||||
if self._running and self.callback:
|
|
||||||
self.callback(*self.callback_args)
|
|
||||||
self._running = False
|
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
# Code generated by cfonts_to_trans_py.py
|
|
||||||
import TFTfont
|
|
||||||
_font10 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x04\x92\x49\x24\x82\x00\x00'\
|
|
||||||
b'\x02\x52\x94\x80\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x04\x82\x42\x41\x27\xfc\x48\x48\x24\x7f\xc9\x09\x04\x80\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x08\x1e\x2b\x49\x48\x48\x38\x0e\x09\x49\x49\x2a\x1c\x08\x08\x00\x00'\
|
|
||||||
b'\x00\x00\xe0\x84\x44\x11\x10\x44\x81\x12\x04\x53\x8e\x51\x02\x44\x11\x10\x44\x42\x11\x08\x38\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x03\x81\x10\x44\x11\x02\x81\xc0\x90\x42\x50\xa4\x10\x86\x1e\x40\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x15\x40\x00\x00\x00'\
|
|
||||||
b'\x01\x22\x24\x44\x44\x44\x22\x21\x00'\
|
|
||||||
b'\x01\x04\x21\x04\x21\x08\x42\x11\x08\x44\x00'\
|
|
||||||
b'\x00\x47\xc4\x28\xa0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x08\x08\x08\x7f\x08\x08\x08\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x02\x48\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x02\x00\x00'\
|
|
||||||
b'\x00\x44\x21\x10\x84\x42\x11\x08\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x22\x41\x41\x41\x41\x41\x41\x41\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\xc5\x24\x10\x41\x04\x10\x41\x04\x00\x00\x00'\
|
|
||||||
b'\x00\x1e\x22\x41\x01\x01\x02\x02\x04\x08\x10\x20\x7f\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x22\x42\x02\x04\x1c\x02\x01\x01\x41\x62\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x04\x0c\x14\x14\x24\x24\x44\x84\xff\x04\x04\x04\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x3f\x20\x20\x40\x7c\x42\x01\x01\x01\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x23\x41\x40\x5c\x62\x41\x41\x41\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x7f\x02\x02\x04\x04\x08\x08\x08\x08\x10\x10\x10\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x22\x41\x41\x22\x1c\x22\x41\x41\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x22\x41\x41\x41\x41\x23\x1d\x01\x41\x22\x3c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x02\x00\x00\x02\x00\x00'\
|
|
||||||
b'\x00\x02\x00\x00\x02\x48\x00'\
|
|
||||||
b'\x00\x00\x00\x20\x60\xc1\x81\x00\x60\x0c\x01\x80\x20\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x03\xfc\x00\x00\x00\x3f\xc0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x10\x06\x00\xc0\x18\x02\x06\x0c\x18\x10\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x22\x41\x41\x01\x02\x04\x08\x08\x08\x00\x08\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x01\xfc\x03\x01\x82\x00\x22\x1c\x91\x11\xc4\x90\x42\x90\x21\x48\x10\xa4\x10\x52\x08\x48\x8c\x42\x3b\xc1\x00\x04\x40\x04\x18\x0c\x03\xf8\x00'\
|
|
||||||
b'\x00\x00\x80\x28\x05\x01\x10\x22\x04\x41\x04\x3f\x88\x09\x01\x40\x18\x02\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1f\xc4\x09\x01\x40\x50\x27\xf9\x02\x40\x50\x14\x05\x02\x7f\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xf0\x41\x10\x14\x00\x80\x10\x02\x00\x40\x08\x00\x80\x88\x20\xf8\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x0f\xe1\x02\x20\x24\x02\x80\x50\x0a\x01\x40\x28\x05\x01\x20\x47\xf0\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1f\xf4\x01\x00\x40\x10\x07\xf9\x00\x40\x10\x04\x01\x00\x7f\xc0\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x3f\xd0\x08\x04\x02\x01\xfc\x80\x40\x20\x10\x08\x04\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xe0\x42\x10\x24\x00\x80\x10\x02\x1f\x40\x28\x04\x81\x08\x40\xf0\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x14\x05\x01\x40\x50\x17\xfd\x01\x40\x50\x14\x05\x01\x40\x40\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x04\x92\x49\x24\x92\x00\x00'\
|
|
||||||
b'\x00\x01\x01\x01\x01\x01\x01\x01\x01\x41\x41\x22\x3c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x14\x09\x04\x42\x11\x04\xc1\x48\x61\x10\x44\x09\x02\x40\x40\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x7f\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x04\x01\x60\x36\x03\x50\x55\x05\x48\x94\x89\x49\x14\x51\x45\x14\x21\x42\x10\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x16\x05\x41\x50\x52\x14\x45\x11\x42\x50\x54\x15\x03\x40\x40\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xe0\x42\x10\x24\x02\x80\x50\x0a\x01\x40\x28\x04\x81\x08\x40\xf0\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1f\xc4\x09\x01\x40\x50\x14\x09\xfc\x40\x10\x04\x01\x00\x40\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xe0\x42\x10\x24\x02\x80\x50\x0a\x01\x40\x28\x04\x8d\x88\x60\xf6\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1f\xc4\x09\x01\x40\x50\x14\x09\xfc\x42\x10\x44\x11\x02\x40\x40\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x07\xc2\x09\x01\x40\x08\x01\xc0\x0e\x00\x50\x14\x04\x82\x1f\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x7f\xc2\x01\x00\x80\x40\x20\x10\x08\x04\x02\x01\x00\x80\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x14\x05\x01\x40\x50\x14\x05\x01\x40\x50\x14\x04\x82\x1f\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x14\x04\x82\x20\x88\x21\x10\x44\x11\x02\x80\xa0\x10\x04\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x40\x40\x50\x50\x48\x28\x24\x14\x11\x11\x10\x88\x88\x44\x44\x14\x14\x0a\x0a\x05\x05\x01\x01\x00\x80\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x08\x08\x82\x08\x81\x10\x14\x01\x00\x50\x11\x02\x20\x82\x20\x28\x02\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x12\x08\x82\x11\x02\x80\xa0\x10\x04\x01\x00\x40\x10\x04\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x3f\xc0\x40\x40\x20\x20\x20\x10\x10\x10\x08\x08\x0f\xf8\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x07\x44\x44\x44\x44\x44\x44\x47\x00'\
|
|
||||||
b'\x04\x10\x84\x10\x84\x10\x84\x10\x80\x00\x00'\
|
|
||||||
b'\x07\x11\x11\x11\x11\x11\x11\x17\x00'\
|
|
||||||
b'\x00\x20\xa1\x44\x48\x91\x41\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\x01\xff\x00\x00'\
|
|
||||||
b'\x08\x80\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1e\x21\x41\x07\x39\x41\x41\x43\x3d\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x40\x40\x40\x5c\x62\x41\x41\x41\x41\x41\x62\x5c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1c\x22\x41\x40\x40\x40\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\x01\x01\x1d\x23\x41\x41\x41\x41\x41\x23\x1d\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1c\x22\x41\x41\x7f\x40\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\xc8\x47\x90\x84\x21\x08\x42\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1d\x23\x41\x41\x41\x41\x41\x23\x1d\x01\x42\x3c\x00'\
|
|
||||||
b'\x00\x40\x40\x40\x5e\x61\x41\x41\x41\x41\x41\x41\x41\x00\x00\x00\x00'\
|
|
||||||
b'\x10\x55\x55\x40\x00'\
|
|
||||||
b'\x10\x55\x55\x56\x00'\
|
|
||||||
b'\x00\x40\x40\x40\x41\x42\x44\x48\x58\x64\x42\x42\x41\x00\x00\x00\x00'\
|
|
||||||
b'\x15\x55\x55\x40\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x5c\xe6\x31\x42\x14\x21\x42\x14\x21\x42\x14\x21\x42\x10\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x5e\x61\x41\x41\x41\x41\x41\x41\x41\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1c\x22\x41\x41\x41\x41\x41\x22\x1c\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x5c\x62\x41\x41\x41\x41\x41\x62\x5c\x40\x40\x40\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x1d\x23\x41\x41\x41\x41\x41\x23\x1d\x01\x01\x01\x00'\
|
|
||||||
b'\x00\x00\x00\x5d\x84\x10\x41\x04\x10\x40\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x03\xc8\x50\x20\x3c\x04\x0a\x13\xc0\x00\x00\x00'\
|
|
||||||
b'\x04\x44\xf4\x44\x44\x44\x70\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x41\x41\x41\x41\x41\x41\x41\x43\x3d\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x08\x30\x51\x22\x44\x50\xa0\x81\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x08\x43\x08\x52\x92\x52\x51\x4a\x29\x45\x10\x42\x08\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x08\x28\x8a\x14\x10\x50\xa2\x28\x20\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x41\x41\x41\x22\x22\x26\x14\x14\x08\x08\x10\x60\x00'\
|
|
||||||
b'\x00\x00\x00\x0f\xe0\x82\x04\x10\x40\x82\x0f\xe0\x00\x00\x00'\
|
|
||||||
b'\x00\x31\x04\x10\x41\x04\x60\x41\x04\x10\x41\x03\x00'\
|
|
||||||
b'\x04\x92\x49\x24\x92\x49\x20'\
|
|
||||||
b'\x06\x08\x42\x10\x84\x19\x08\x42\x10\x98\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\xe3\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x7b\x6d\xb6\xf0\x00\x00'\
|
|
||||||
|
|
||||||
_font10_index = b'\x00\x00\x14\x00\x1b\x00\x26\x00\x3a\x00\x4b\x00\x69\x00\x7f\x00'\
|
|
||||||
b'\x84\x00\x8d\x00\x98\x00\xa5\x00\xb6\x00\xbd\x00\xc8\x00\xcf\x00'\
|
|
||||||
b'\xda\x00\xeb\x00\xf8\x00\x09\x01\x1a\x01\x2b\x01\x3c\x01\x4d\x01'\
|
|
||||||
b'\x5e\x01\x6f\x01\x80\x01\x87\x01\x8e\x01\xa2\x01\xb6\x01\xca\x01'\
|
|
||||||
b'\xdb\x01\x00\x02\x18\x02\x2e\x02\x46\x02\x5e\x02\x74\x02\x88\x02'\
|
|
||||||
b'\xa0\x02\xb6\x02\xbd\x02\xce\x02\xe4\x02\xf5\x02\x0f\x03\x25\x03'\
|
|
||||||
b'\x3d\x03\x53\x03\x6b\x03\x81\x03\x97\x03\xab\x03\xc1\x03\xd7\x03'\
|
|
||||||
b'\xfc\x03\x14\x04\x2a\x04\x3e\x04\x47\x04\x52\x04\x5b\x04\x6a\x04'\
|
|
||||||
b'\x7e\x04\x85\x04\x96\x04\xa7\x04\xb8\x04\xc9\x04\xda\x04\xe5\x04'\
|
|
||||||
b'\xf6\x04\x07\x05\x0c\x05\x11\x05\x22\x05\x27\x05\x41\x05\x52\x05'\
|
|
||||||
b'\x63\x05\x74\x05\x85\x05\x92\x05\xa1\x05\xaa\x05\xbb\x05\xca\x05'\
|
|
||||||
b'\xe2\x05\xf1\x05\x02\x06\x11\x06\x1e\x06\x25\x06\x30\x06\x44\x06'\
|
|
||||||
b'\x4b\x06'
|
|
||||||
|
|
||||||
font10 = TFTfont.TFTFont(_font10, _font10_index, 17, 17, 96)
|
|
||||||
|
|
||||||
fonts = {"font10":font10,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
# Code generated by cfonts_to_trans_py.py
|
|
||||||
import TFTfont
|
|
||||||
_font14 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\xc6\x31\x8c\x63\x18\xc6\x31\x8c\x00\x18\xc0\x00\x00\x00'\
|
|
||||||
b'\x00\xcd\x9b\x36\x6c\xd9\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x61\x83\x0c\x18\x61\x86\x7f\xff\xff\xe3\x0c\x18\x61\xc6\x0c\x31\xff\xff\xff\x98\x60\xc3\x0c\x30\x61\x83\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x02\x00\xf8\x3f\xe7\x27\x62\x36\x20\x62\x03\xa0\x3f\x00\xfc\x02\xe0\x27\x02\x36\x23\x72\x33\x26\x3f\xe0\xf8\x02\x00\x20\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x03\x80\x60\xd8\x18\x31\x83\x06\x30\xc0\xc6\x18\x18\xc6\x03\x19\x80\x36\x30\x03\x8c\x70\x01\x9b\x00\x66\x30\x0c\xc6\x03\x18\xc0\xe3\x18\x18\x63\x06\x06\xc0\xc0\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x1c\x00\xf8\x06\x30\x18\xc0\x63\x01\xcc\x03\x60\x07\x00\x7c\x03\xb8\x1c\x76\x60\xd1\x81\xc6\x03\x0c\x3e\x3f\xdc\x3e\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x0d\xb6\xd8\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x10\x86\x10\xc3\x08\x61\x86\x18\x61\x86\x18\x30\xc3\x04\x18\x20\x40'\
|
|
||||||
b'\x00\x40\x40\xc0\x81\x83\x02\x06\x0c\x18\x30\x60\xc1\x83\x0c\x18\x30\x41\x82\x08\x00'\
|
|
||||||
b'\x00\x06\x03\x0f\xf1\xe0\xf0\xcc\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x30\x01\x80\x0c\x00\x60\x03\x03\xff\xdf\xfe\x06\x00\x30\x01\x80\x0c\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x33\x11\x20\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x33\x00\x00\x00'\
|
|
||||||
b'\x00\x0c\x18\x60\xc1\x86\x0c\x18\x70\xc1\x83\x0c\x18\x30\xc1\x80\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf8\x1f\xc3\x8e\x30\x66\x03\x60\x36\x03\x60\x36\x03\x60\x36\x03\x60\x36\x03\x30\x63\x8e\x3f\xc0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xc0\x60\x70\xf8\xec\x66\x03\x01\x80\xc0\x60\x30\x18\x0c\x06\x03\x01\x80\xc0\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf8\x3f\xe3\x06\x60\x36\x03\x00\x30\x03\x00\x60\x0c\x01\x80\x30\x06\x00\xc0\x18\x03\x00\x7f\xf7\xff\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf0\x3f\xc3\x0e\x60\x60\x06\x00\x60\x0c\x07\x80\x7c\x00\x60\x03\x00\x36\x03\x70\x33\x06\x1f\xc0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x0c\x01\xc0\x3c\x03\xc0\x6c\x0e\xc0\xcc\x18\xc1\x8c\x30\xc7\x0c\x7f\xf7\xff\x00\xc0\x0c\x00\xc0\x0c\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xfe\x1f\xe3\x00\x30\x03\x00\x37\x83\xfc\x70\x66\x03\x00\x30\x03\x00\x36\x03\x60\x23\x06\x1f\xc0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x7c\x1f\xe3\x87\x30\x33\x00\x60\x06\x78\x6f\xe7\x86\x70\x36\x03\x60\x36\x03\x30\x33\x86\x1f\xc0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x07\xff\x7f\xf0\x02\x00\x60\x0c\x00\x80\x18\x03\x00\x30\x03\x00\x60\x06\x00\x60\x0c\x00\xc0\x0c\x00\xc0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf8\x1f\xc3\x8e\x30\x63\x06\x30\x61\x8c\x0f\x81\xfc\x30\x66\x03\x60\x36\x03\x60\x33\x06\x3f\xe0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf8\x1f\xc3\x06\x70\x76\x03\x60\x36\x03\x60\x33\x0f\x3f\xb0\xf3\x00\x30\x06\x60\x67\x0e\x3f\xc1\xf0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x03\x30\x00\x00\x00\x00\x33\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x03\x30\x00\x00\x00\x00\x33\x11\x20\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x02\x01\xc0\xf0\x78\x38\x0c\x00\xe0\x07\x80\x3c\x01\xc0\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xfb\xff\x00\x00\x00\x00\x3f\xf7\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x04\x00\xe0\x0f\x00\x78\x01\xc0\x0c\x07\x07\x83\xc0\xe0\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xf8\x1f\xe3\x86\x70\x36\x03\x00\x30\x06\x00\xe0\x1c\x03\x00\x60\x06\x00\x60\x00\x00\x00\x06\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x03\xf8\x00\x3f\xf8\x01\xe0\xf0\x0e\x00\xe0\x73\xcd\xc3\x9f\xb3\x0c\xe3\xc6\x36\x06\x19\x98\x18\x66\xc0\x61\x9b\x01\x86\x6c\x06\x39\xb0\x30\xc6\xc1\xc7\x19\x8f\x38\x37\xff\xc0\xcf\x1c\x01\x80\x00\xc7\x00\x0e\x0f\x80\xf0\x0f\xff\x00\x0f\xf0\x00'\
|
|
||||||
b'\x00\x00\x07\x00\x0e\x00\x36\x00\x6c\x01\xdc\x03\x18\x06\x30\x18\x30\x30\x60\x60\xc1\xff\xc3\xff\x8e\x03\x98\x03\x30\x06\xc0\x07\x80\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xff\x83\xff\x0c\x0e\x30\x18\xc0\x63\x01\x8c\x0c\x3f\xf0\xff\xc3\x03\x8c\x03\x30\x0c\xc0\x33\x00\xcc\x06\x3f\xf8\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x03\xf0\x0f\xfc\x1c\x0e\x38\x06\x30\x03\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00\x60\x03\x30\x03\x38\x06\x1c\x0e\x0f\xfc\x03\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x3f\xf0\x3f\xf8\x30\x1c\x30\x0e\x30\x06\x30\x03\x30\x03\x30\x03\x30\x03\x30\x03\x30\x03\x30\x03\x30\x06\x30\x06\x30\x1c\x3f\xf8\x3f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xff\xf3\xff\xcc\x00\x30\x00\xc0\x03\x00\x0c\x00\x3f\xf8\xff\xe3\x00\x0c\x00\x30\x00\xc0\x03\x00\x0c\x00\x3f\xfc\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xff\xcf\xfe\x60\x03\x00\x18\x00\xc0\x06\x00\x3f\xf1\xff\x8c\x00\x60\x03\x00\x18\x00\xc0\x06\x00\x30\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x01\xfc\x03\xff\x83\xc1\xe3\x80\x31\x80\x0d\xc0\x00\xc0\x00\x60\x00\x30\x1f\xd8\x0f\xec\x00\x37\x00\x19\x80\x0c\xe0\x06\x3c\x0f\x0f\xfe\x00\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x60\x0c\xc0\x19\x80\x33\x00\x66\x00\xcc\x01\x98\x03\x3f\xfe\x7f\xfc\xc0\x19\x80\x33\x00\x66\x00\xcc\x01\x98\x03\x30\x06\x60\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x03\x33\x33\x33\x33\x33\x33\x33\x33\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x30\x0c\x03\x00\xc0\x30\x0c\x03\x00\xc0\x30\x0c\x03\x00\xd8\x36\x0d\xc7\x3f\x87\xc0\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x30\x0e\x30\x1c\x30\x38\x30\x70\x30\xe0\x31\xc0\x33\x80\x37\x00\x3f\x80\x3d\xc0\x38\xe0\x30\x70\x30\x30\x30\x38\x30\x1c\x30\x0e\x30\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x03\x00\x30\x03\x00\x30\x03\x00\x30\x03\x00\x30\x03\x00\x30\x03\x00\x30\x03\x00\x30\x03\x00\x3f\xf3\xff\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x1c\x01\xcf\x01\xe7\x80\xf3\xc0\x79\xb0\x6c\xd8\x36\x6c\x1b\x33\x19\x99\x8c\xcc\xc6\x66\x63\x33\x1b\x19\x8d\x8c\xc6\xc6\x61\xc3\x30\xe1\x98\x70\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x60\x0c\xe0\x19\xe0\x33\xc0\x66\xc0\xcc\xc1\x99\x83\x31\x86\x63\x8c\xc3\x19\x83\x33\x06\x66\x06\xcc\x07\x98\x0f\x30\x0e\x60\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x01\xf8\x03\xff\x03\x81\xc3\x80\x71\x80\x19\x80\x0e\xc0\x03\x60\x01\xb0\x00\xd8\x00\x6c\x00\x36\x00\x39\x80\x18\xe0\x1c\x38\x1c\x0f\xfc\x01\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xff\x83\xff\x8c\x06\x30\x0c\xc0\x33\x00\xcc\x03\x30\x18\xff\xe3\xfe\x0c\x00\x30\x00\xc0\x03\x00\x0c\x00\x30\x00\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x01\xf8\x03\xff\x03\x81\xc3\x80\x71\x80\x19\x80\x0e\xc0\x03\x60\x01\xb0\x00\xd8\x00\x6c\x00\x37\x00\x19\x83\x18\xe1\xdc\x38\x3c\x0f\xfe\x01\xfd\xc0\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x3f\xf8\x3f\xfc\x30\x0e\x30\x06\x30\x06\x30\x06\x30\x0e\x3f\xfc\x3f\xf0\x30\xe0\x30\x70\x30\x38\x30\x38\x30\x1c\x30\x0c\x30\x0e\x30\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x1f\x81\xff\x06\x0e\x30\x0c\xc0\x33\x00\x07\x00\x1f\xc0\x1f\xc0\x07\x80\x03\x60\x0d\x80\x33\x00\xce\x0e\x1f\xf0\x1f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x03\xff\xdf\xfe\x06\x00\x30\x01\x80\x0c\x00\x60\x03\x00\x18\x00\xc0\x06\x00\x30\x01\x80\x0c\x00\x60\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x60\x0c\xc0\x19\x80\x33\x00\x66\x00\xcc\x01\x98\x03\x30\x06\x60\x0c\xc0\x19\x80\x33\x00\x66\x00\xc6\x03\x0e\x0e\x0f\xf8\x0f\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\x80\x0f\x80\x3b\x00\x66\x00\xce\x03\x8c\x06\x18\x0c\x38\x38\x30\x60\x60\xc0\x63\x00\xc6\x01\xdc\x01\xb0\x03\x60\x07\xc0\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x01\x80\x70\x0f\x00\xe0\x1b\x03\x60\x66\x06\xc0\xcc\x0d\x81\x98\x31\x83\x18\x63\x0c\x30\xc6\x18\x63\x06\x30\xc6\x0c\x61\xcc\x19\xc1\xb8\x3b\x03\x60\x36\x06\xc0\x6c\x0d\x80\xd8\x0e\x00\xe0\x1c\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\xe0\x38\xe0\xe0\xc1\x81\xc7\x01\xdc\x01\xb0\x03\xe0\x03\x80\x07\x00\x1b\x00\x77\x00\xc6\x03\x8e\x0e\x0e\x18\x0c\x70\x1d\xc0\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x03\x80\x76\x01\x8c\x0c\x38\x70\x61\x81\xce\x03\x30\x07\x80\x1e\x00\x30\x00\xc0\x03\x00\x0c\x00\x30\x00\xc0\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x03\xff\xdf\xfe\x00\x60\x06\x00\x70\x03\x00\x30\x03\x00\x38\x01\x80\x18\x01\x80\x1c\x00\xc0\x0c\x00\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x03\xde\xc6\x31\x8c\x63\x18\xc6\x31\x8c\x63\x18\xc6\x3d\xe0'\
|
|
||||||
b'\x01\x83\x03\x06\x0c\x0c\x18\x30\x70\x60\xc1\x81\x83\x06\x06\x0c\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x03\xde\x31\x8c\x63\x18\xc6\x31\x8c\x63\x18\xc6\x31\xbd\xe0'\
|
|
||||||
b'\x00\x00\xc0\x3c\x07\x81\x98\x33\x06\x61\x86\x30\xcc\x0c\x00\x00\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\xe0'\
|
|
||||||
b'\x03\x8c\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3f\xc7\x0e\x60\x60\x1e\x1f\xe3\xe6\x70\x66\x06\x60\xe7\x1e\x3f\xe1\xe3\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x0c\x01\x80\x30\x06\x00\xde\x1f\xf3\x86\x60\x6c\x0d\x81\xb0\x36\x06\xc0\xd8\x1b\x86\x7f\x8d\xe0\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x1f\x07\xf1\xc7\x70\x6c\x01\x80\x30\x06\x00\xc0\x1c\x19\x87\x1f\xc1\xf0\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x0c\x01\x80\x30\x06\x1e\xc7\xf9\x87\x60\x6c\x0d\x81\xb0\x36\x06\xc0\xd8\x19\x87\x1f\xe1\xec\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x1f\xc3\x86\x60\x36\x03\x7f\xf7\xff\x60\x06\x00\x70\x33\x86\x1f\xe0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x3c\xf9\x83\x1f\xbf\x18\x30\x60\xc1\x83\x06\x0c\x18\x30\x60\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x1e\xc7\xf9\x87\x70\x6c\x0d\x81\xb0\x36\x06\xc0\xdc\x19\xc7\x1f\xe1\xec\x01\xb0\x37\x0c\x7f\x87\xc0'\
|
|
||||||
b'\x00\x0c\x01\x80\x30\x06\x00\xcf\x1b\xf3\xc7\x70\x6c\x0d\x81\xb0\x36\x06\xc0\xd8\x1b\x03\x60\x6c\x0c\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x0d\x80\xdb\x6d\xb6\xdb\x6c\x00\x00'\
|
|
||||||
b'\x0d\x80\xdb\x6d\xb6\xdb\x6d\xb7\xf0'\
|
|
||||||
b'\x00\x0c\x01\x80\x30\x06\x00\xc1\xd8\x73\x1c\x67\x0d\xc1\xf0\x3f\x07\x70\xc6\x18\x63\x0e\x60\xcc\x1c\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x0d\xb6\xdb\x6d\xb6\xdb\x6c\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x38\x71\xbe\x7c\xf3\xe7\x70\xe1\xb0\x60\xd8\x30\x6c\x18\x36\x0c\x1b\x06\x0d\x83\x06\xc1\x83\x60\xc1\xb0\x60\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\xcf\x1b\xf3\xc7\x70\x6c\x0d\x81\xb0\x36\x06\xc0\xd8\x1b\x03\x60\x6c\x0c\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x1f\xc3\x8e\x70\x76\x03\x60\x36\x03\x60\x36\x03\x70\x73\x8e\x1f\xc0\xf8\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\xde\x1f\xf3\x86\x60\xec\x0d\x81\xb0\x36\x06\xc0\xd8\x3b\x8e\x7f\x8d\xe1\x80\x30\x06\x00\xc0\x18\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x1e\xcf\xf9\xc7\x70\x6c\x0d\x81\xb0\x36\x06\xc0\xdc\x19\xc7\x1f\xe1\xec\x01\x80\x30\x06\x00\xc0\x18'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x6f\x7f\x70\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x3f\x0f\xf3\x83\x60\x0e\x00\xfc\x0f\xe0\x3e\x00\xd8\x1b\x87\x3f\xc3\xf0\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x43\x0c\x33\xff\xcc\x30\xc3\x0c\x30\xc3\x0c\x3c\x70\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\xc0\xd8\x1b\x03\x60\x6c\x0d\x81\xb0\x36\x06\xc0\xd8\x3b\x8f\x3f\x63\xcc\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x80\xf0\x1b\x06\x60\xcc\x18\xc6\x18\xc1\xb0\x36\x06\xc0\x70\x0e\x00\x80\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x20\xf0\xe1\xe1\xc3\x63\x8c\xc5\x19\x9b\x31\x36\xc3\x6d\x86\x8b\x05\x14\x0e\x38\x1c\x70\x10\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\xc0\xd8\x31\x8c\x39\x83\x60\x38\x07\x00\xe0\x36\x0e\xe1\x8c\x60\xdc\x1c\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x80\xf0\x1b\x06\x60\xc6\x18\xc6\x0c\xc1\x98\x36\x03\xc0\x78\x06\x00\xc0\x18\x03\x00\xc0\x78\x0e\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x1f\xf7\xfc\x07\x03\x81\xc0\xe0\x70\x18\x0e\x07\x03\x80\xff\xff\xf0\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x1c\x78\xc1\x83\x06\x0c\x18\x30\xc3\x07\x06\x06\x0c\x18\x30\x60\xc1\x83\xc3\x80'\
|
|
||||||
b'\x03\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33\x30'\
|
|
||||||
b'\x00\xe1\xe0\xc1\x83\x06\x0c\x18\x30\x30\x30\xe1\x86\x0c\x18\x30\x60\xc1\x8f\x1c\x00'\
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x07\xf1\x47\xf0\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
|
||||||
b'\x00\x01\xf8\xc6\x31\x8c\x63\x18\xc6\x31\xf8\x00\x00\x00\x00'\
|
|
||||||
|
|
||||||
_font14_index = b'\x00\x00\x23\x00\x32\x00\x47\x00\x6d\x00\x90\x00\xc7\x00\xf0\x00'\
|
|
||||||
b'\xf9\x00\x0b\x01\x20\x01\x3a\x01\x60\x01\x6c\x01\x81\x01\x8d\x01'\
|
|
||||||
b'\xa2\x01\xc5\x01\xdf\x01\x02\x02\x25\x02\x48\x02\x6b\x02\x8e\x02'\
|
|
||||||
b'\xb1\x02\xd4\x02\xf7\x02\x03\x03\x0f\x03\x2f\x03\x4f\x03\x6f\x03'\
|
|
||||||
b'\x92\x03\xd2\x03\xfe\x03\x27\x04\x55\x04\x83\x04\xac\x04\xd2\x04'\
|
|
||||||
b'\x03\x05\x2f\x05\x3b\x05\x58\x05\x86\x05\xa9\x05\xda\x05\x06\x06'\
|
|
||||||
b'\x37\x06\x60\x06\x91\x06\xbf\x06\xe8\x06\x0e\x07\x3a\x07\x66\x07'\
|
|
||||||
b'\xa9\x07\xd5\x07\xfe\x07\x24\x08\x33\x08\x48\x08\x57\x08\x77\x08'\
|
|
||||||
b'\x9d\x08\xac\x08\xcf\x08\xef\x08\x0f\x09\x2f\x09\x52\x09\x67\x09'\
|
|
||||||
b'\x87\x09\xa7\x09\xb0\x09\xb9\x09\xd9\x09\xe2\x09\x13\x0a\x33\x0a'\
|
|
||||||
b'\x56\x0a\x76\x0a\x96\x0a\xad\x0a\xcd\x0a\xdf\x0a\xff\x0a\x1f\x0b'\
|
|
||||||
b'\x4b\x0b\x6b\x0b\x8b\x0b\xa8\x0b\xbd\x0b\xc9\x0b\xde\x0b\x01\x0c'\
|
|
||||||
b'\x10\x0c'
|
|
||||||
|
|
||||||
font14 = TFTfont.TFTFont(_font14, _font14_index, 23, 23, 96)
|
|
||||||
|
|
||||||
fonts = {"font14":font14,
|
|
||||||
}
|
|
||||||
|
|
117
tft_gui/hst.py
117
tft_gui/hst.py
|
@ -1,117 +0,0 @@
|
||||||
# hst.py Demo/test for Horizontal Slider class for Pyboard TFT GUI
|
|
||||||
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 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 font10 import font10
|
|
||||||
from tft import TFT, LANDSCAPE
|
|
||||||
from usched import Sched
|
|
||||||
from touch import TOUCH
|
|
||||||
from ugui import HorizSlider, Button, Dial, Label, LED, Meter, CLIPPED_RECT, GREEN, RED, YELLOW, WHITE, BLUE
|
|
||||||
import pyb
|
|
||||||
|
|
||||||
# CALLBACKS
|
|
||||||
# cb_end occurs when user stops touching the control
|
|
||||||
def callback(slider, control_name):
|
|
||||||
print('{} returned {}'.format(control_name, slider.value()))
|
|
||||||
|
|
||||||
def to_string(val):
|
|
||||||
return '{:3.1f} ohms'.format(val * 10)
|
|
||||||
|
|
||||||
def master_moved(slider, slave1, slave2, label, led):
|
|
||||||
val = slider.value()
|
|
||||||
slave1.value(val)
|
|
||||||
slave2.value(val)
|
|
||||||
label.show(to_string(val))
|
|
||||||
if val > 0.8:
|
|
||||||
led.on()
|
|
||||||
else:
|
|
||||||
led.off()
|
|
||||||
|
|
||||||
# Either slave has had its slider moved (by user or by having value altered)
|
|
||||||
def slave_moved(slider, label):
|
|
||||||
val = slider.value()
|
|
||||||
if val > 0.8:
|
|
||||||
slider.fgcolor = RED
|
|
||||||
else:
|
|
||||||
slider.fgcolor = GREEN
|
|
||||||
label.show(to_string(val))
|
|
||||||
|
|
||||||
def doquit(button):
|
|
||||||
button.objsched.stop()
|
|
||||||
|
|
||||||
# USER TEST FUNCTION
|
|
||||||
# Common args for the labels
|
|
||||||
labels = { 'width' : 70,
|
|
||||||
'fontcolor' : WHITE,
|
|
||||||
'border' : 2,
|
|
||||||
'fgcolor' : RED,
|
|
||||||
'bgcolor' : (0, 40, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
# '0', '1','2','3','4','5','6','7','8','9','10'
|
|
||||||
# Common arguments for all three sliders
|
|
||||||
table = {'fontcolor' : WHITE,
|
|
||||||
'legends' : ('0', '5', '10'),
|
|
||||||
'cb_end' : callback,
|
|
||||||
}
|
|
||||||
# 'border' : 2,
|
|
||||||
|
|
||||||
def testmeter(meter):
|
|
||||||
oldvalue = 0
|
|
||||||
yield
|
|
||||||
while True:
|
|
||||||
val = pyb.rng()/2**30
|
|
||||||
steps = 20
|
|
||||||
delta = (val - oldvalue) / steps
|
|
||||||
for _ in range(steps):
|
|
||||||
oldvalue += delta
|
|
||||||
meter.value(oldvalue)
|
|
||||||
yield 0.05
|
|
||||||
|
|
||||||
def test():
|
|
||||||
print('Test TFT panel...')
|
|
||||||
objsched = Sched() # Instantiate the scheduler
|
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
|
||||||
mytouch = TOUCH("XPT2046", objsched, confidence = 50, margin = 50) #, calibration = (-3886,-0.1287,-3812,-0.132,-3797,-0.07685,-3798,-0.07681))
|
|
||||||
mytft.backlight(100) # light on
|
|
||||||
led = LED(mytft, (420, 0), border = 2)
|
|
||||||
meter1 = Meter(mytft, (320, 0), font=font10, legends=('0','5','10'), pointercolor = YELLOW, fgcolor = GREEN)
|
|
||||||
meter2 = Meter(mytft, (360, 0), font=font10, legends=('0','5','10'), pointercolor = YELLOW)
|
|
||||||
Button(objsched, mytft, mytouch, (420, 240), font = font10, callback = doquit, fgcolor = RED,
|
|
||||||
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
|
||||||
x = 230
|
|
||||||
lstlbl = []
|
|
||||||
for n in range(3):
|
|
||||||
lstlbl.append(Label(mytft, (x, 40 + 60 * n), font = font10, **labels))
|
|
||||||
x = 0
|
|
||||||
slave1 = HorizSlider(objsched, mytft, mytouch, (x, 100), font10,
|
|
||||||
fgcolor = GREEN, cbe_args = ['Slave1'], cb_move = slave_moved, cbm_args = [lstlbl[1]], **table)
|
|
||||||
slave2 = HorizSlider(objsched, mytft, mytouch, (x, 160), font10,
|
|
||||||
fgcolor = GREEN, cbe_args = ['Slave2'], cb_move = slave_moved, cbm_args = [lstlbl[2]], **table)
|
|
||||||
master = HorizSlider(objsched, mytft, mytouch, (x, 40), font10,
|
|
||||||
fgcolor = YELLOW, cbe_args = ['Master'], cb_move = master_moved, slidecolor=RED, cbm_args = [slave1, slave2, lstlbl[0], led], value=0.5, **table)
|
|
||||||
objsched.add_thread(testmeter(meter1))
|
|
||||||
objsched.add_thread(testmeter(meter2))
|
|
||||||
objsched.run() # Run it!
|
|
||||||
|
|
||||||
test()
|
|
|
@ -1,35 +0,0 @@
|
||||||
from font10 import font10
|
|
||||||
from tft import TFT, LANDSCAPE
|
|
||||||
from usched import Sched
|
|
||||||
from touch import TOUCH
|
|
||||||
from ugui import Knob, Dial, Label, Button, WHITE, YELLOW, GREEN, RED, CLIPPED_RECT
|
|
||||||
from math import pi
|
|
||||||
|
|
||||||
# CALLBACKS
|
|
||||||
# cb_end occurs when user stops touching the control
|
|
||||||
def callback(knob, control_name):
|
|
||||||
print('{} returned {}'.format(control_name, knob.value()))
|
|
||||||
|
|
||||||
def knob_moved(knob, dial):
|
|
||||||
val = knob.value() # range 0..1
|
|
||||||
dial.show(2 * (val - 0.5) * pi)
|
|
||||||
|
|
||||||
def doquit(button):
|
|
||||||
button.objsched.stop()
|
|
||||||
|
|
||||||
def test():
|
|
||||||
print('Test TFT panel...')
|
|
||||||
objsched = Sched() # Instantiate the scheduler
|
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
|
||||||
mytouch = TOUCH("XPT2046", objsched, confidence = 50, margin = 50)
|
|
||||||
mytft.backlight(100) # light on
|
|
||||||
Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = RED,
|
|
||||||
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
|
||||||
dial1 = Dial(mytft, (120, 0), fgcolor = YELLOW, border = 2, pointers = (0.9, 0.7))
|
|
||||||
Knob(objsched, mytft, mytouch, (0, 0), fgcolor = GREEN, bgcolor=(0, 0, 80), color = (168,63,63), border = 2,
|
|
||||||
cb_end = callback, cbe_args = ['Knob1'], cb_move = knob_moved, cbm_args = [dial1]) #, arc = pi * 1.5)
|
|
||||||
Knob(objsched, mytft, mytouch, (0, 120), fgcolor = WHITE, border = 2,
|
|
||||||
cb_end = callback, cbe_args = ['Knob2'], arc = pi * 1.5)
|
|
||||||
objsched.run() # Run it!
|
|
||||||
|
|
||||||
test()
|
|
772
tft_gui/tft.py
772
tft_gui/tft.py
|
@ -1,772 +0,0 @@
|
||||||
#
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 Robert Hammelrath
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# Some parts of the software are a port of code provided by Rinky-Dink Electronics, Henning Karlsen,
|
|
||||||
# with the following copyright notice:
|
|
||||||
#
|
|
||||||
## Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved
|
|
||||||
## This library is free software; you can redistribute it and/or
|
|
||||||
## modify it under the terms of the CC BY-NC-SA 3.0 license.
|
|
||||||
## Please see the included documents for further information.
|
|
||||||
#
|
|
||||||
# Class supporting TFT LC-displays with a parallel Interface
|
|
||||||
# First example: Controller SSD1963 with a 4.3" or 7" display
|
|
||||||
#
|
|
||||||
# The minimal connection is:
|
|
||||||
# X1..X8 for data, Y9 for /Reset, Y10 for /RD, Y11 for /WR and Y12 for /RS
|
|
||||||
# Then LED must be hard tied to Vcc and /CS to GND.
|
|
||||||
#
|
|
||||||
|
|
||||||
import pyb, stm
|
|
||||||
from uctypes import addressof
|
|
||||||
import TFT_io
|
|
||||||
|
|
||||||
# define constants
|
|
||||||
#
|
|
||||||
RESET = const(1 << 10) ## Y9
|
|
||||||
RD = const(1 << 11) ## Y10
|
|
||||||
WR = const(0x01) ## Y11
|
|
||||||
D_C = const(0x02) ## Y12
|
|
||||||
|
|
||||||
LED = const(1 << 8) ## Y3
|
|
||||||
POWER = const(1 << 9) ## Y4
|
|
||||||
|
|
||||||
## CS is not used and must be hard tied to GND
|
|
||||||
|
|
||||||
PORTRAIT = const(1)
|
|
||||||
LANDSCAPE = const(0)
|
|
||||||
|
|
||||||
class TFT:
|
|
||||||
|
|
||||||
def __init__(self, controller = "SSD1963", lcd_type = "LB04301", orientation = LANDSCAPE, v_flip = False, h_flip = False):
|
|
||||||
self.tft_init(controller, lcd_type, orientation, v_flip, h_flip)
|
|
||||||
|
|
||||||
def tft_init(self, controller = "SSD1963", lcd_type = "LB04301", orientation = LANDSCAPE, v_flip = False, h_flip = False):
|
|
||||||
#
|
|
||||||
# For convenience, define X1..X1 and Y9..Y12 as output port using thy python functions.
|
|
||||||
# X1..X8 will be redefind on the fly as Input by accessing the MODER control registers
|
|
||||||
# when needed. Y9 is treate seperately, since it is used for Reset, which is done at python level
|
|
||||||
# since it need long delays anyhow, 5 and 15 ms vs. 10 µs.
|
|
||||||
#
|
|
||||||
# Set TFT general defaults
|
|
||||||
self.controller = controller
|
|
||||||
self.lcd_type = lcd_type
|
|
||||||
self.orientation = orientation
|
|
||||||
self.v_flip = v_flip # flip vertical
|
|
||||||
self.h_flip = h_flip # flip horizontal
|
|
||||||
self.c_flip = 0 # flip blue/red
|
|
||||||
self.rc_flip = 0 # flip row/column
|
|
||||||
|
|
||||||
self.setColor((255, 255, 255)) # set FG color to white as can be.
|
|
||||||
self.setBGColor((0, 0, 0)) # set BG to black
|
|
||||||
# special treat for BG LED
|
|
||||||
self.pin_led = pyb.Pin("Y3", pyb.Pin.OUT_PP)
|
|
||||||
self.led_tim = pyb.Timer(4, freq=500)
|
|
||||||
self.led_ch = self.led_tim.channel(3, pyb.Timer.PWM, pin=self.pin_led)
|
|
||||||
self.led_ch.pulse_width_percent(0) # led off
|
|
||||||
self.pin_led.value(0) ## switch BG LED off
|
|
||||||
# special treat for Power Pin
|
|
||||||
self.pin_power = pyb.Pin("Y4", pyb.Pin.OUT_PP)
|
|
||||||
self.pin_power.value(1) ## switch Power on
|
|
||||||
pyb.delay(10)
|
|
||||||
# this may have to be moved to the controller specific section
|
|
||||||
if orientation == PORTRAIT:
|
|
||||||
self.setXY = TFT_io.setXY_P
|
|
||||||
self.drawPixel = TFT_io.drawPixel_P
|
|
||||||
else:
|
|
||||||
self.setXY = TFT_io.setXY_L
|
|
||||||
self.drawPixel = TFT_io.drawPixel_L
|
|
||||||
self.swapbytes = TFT_io.swapbytes
|
|
||||||
self.swapcolors = TFT_io.swapcolors
|
|
||||||
# ----------
|
|
||||||
for pin_name in ["X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8",
|
|
||||||
"Y10", "Y11", "Y12"]:
|
|
||||||
pin = pyb.Pin(pin_name, pyb.Pin.OUT_PP) # set as output
|
|
||||||
pin.value(1) ## set high as default
|
|
||||||
# special treat for Reset
|
|
||||||
self.pin_reset = pyb.Pin("Y9", pyb.Pin.OUT_PP)
|
|
||||||
# Reset the device
|
|
||||||
self.pin_reset.value(1) ## do a hard reset
|
|
||||||
pyb.delay(10)
|
|
||||||
self.pin_reset.value(0) ## Low
|
|
||||||
pyb.delay(20)
|
|
||||||
self.pin_reset.value(1) ## set high again
|
|
||||||
pyb.delay(20)
|
|
||||||
#
|
|
||||||
# Now initialiize the LCD
|
|
||||||
# This is for the SSD1963 controller and two specific LCDs. More may follow.
|
|
||||||
# Data taken from the SSD1963 data sheet, SSD1963 Application Note and the LCD Data sheets
|
|
||||||
#
|
|
||||||
if controller == "SSD1963": # 1st approach for 480 x 272
|
|
||||||
TFT_io.tft_cmd_data(0xe2, bytearray(b'\x1d\x02\x54'), 3) # PLL multiplier, set PLL clock to 100M
|
|
||||||
# N=0x2D for 6.5MHz, 0x1D for 10MHz crystal
|
|
||||||
# PLLClock = Crystal * (Mult + 1) / (Div + 1)
|
|
||||||
# The intermediate value Crystal * (Mult + 1) must be between 250MHz and 750 MHz
|
|
||||||
TFT_io.tft_cmd_data(0xe0, bytearray(b'\x01'), 1) # PLL Enable
|
|
||||||
pyb.delay(10)
|
|
||||||
TFT_io.tft_cmd_data(0xe0, bytearray(b'\x03'), 1)
|
|
||||||
pyb.delay(10)
|
|
||||||
TFT_io.tft_cmd(0x01) # software reset
|
|
||||||
pyb.delay(10)
|
|
||||||
#
|
|
||||||
# Settings for the LCD
|
|
||||||
#
|
|
||||||
# The LCDC_FPR depends on PLL clock and the reccomended LCD Dot clock DCLK
|
|
||||||
#
|
|
||||||
# LCDC_FPR = (DCLK * 1048576 / PLLClock) - 1
|
|
||||||
#
|
|
||||||
# The other settings are less obvious, since the definitions of the SSD1963 data sheet and the
|
|
||||||
# LCD data sheets differ. So what' common, even if the names may differ:
|
|
||||||
# HDP Horizontal Panel width (also called HDISP, Thd). The value store in the register is HDP - 1
|
|
||||||
# VDP Vertical Panel Width (also called VDISP, Tvd). The value stored in the register is VDP - 1
|
|
||||||
# HT Total Horizontal Period, also called HP, th... The exact value does not matter
|
|
||||||
# VT Total Vertical Period, alco called VT, tv, .. The exact value does not matter
|
|
||||||
# HPW Width of the Horizontal sync pulse, also called HS, thpw.
|
|
||||||
# VPW Width of the Vertical sync pulse, also called VS, tvpw
|
|
||||||
# Front Porch (HFP and VFP) Time between the end of display data and the sync pulse
|
|
||||||
# Back Porch (HBP and VBP Time between the start of the sync pulse and the start of display data.
|
|
||||||
# HT = FP + HDP + BP and VT = VFP + VDP + VBP (sometimes plus sync pulse width)
|
|
||||||
# Unfortunately, the controller does not use these front/back porch times, instead it uses an starting time
|
|
||||||
# in the front porch area and defines (see also figures in chapter 13.3 of the SSD1963 data sheet)
|
|
||||||
# HPS Time from that horiz. starting point to the start of the horzontal display area
|
|
||||||
# LPS Time from that horiz. starting point to the horizontal sync pulse
|
|
||||||
# VPS Time from the vert. starting point to the first line
|
|
||||||
# FPS Time from the vert. starting point to the vertical sync pulse
|
|
||||||
#
|
|
||||||
# So the following relations must be held:
|
|
||||||
#
|
|
||||||
# HT > HDP + HPS
|
|
||||||
# HPS >= HPW + LPS
|
|
||||||
# HPS = Back Porch - LPS, or HPS = Horizontal back Porch
|
|
||||||
# VT > VDP + VPS
|
|
||||||
# VPS >= VPW + FPS
|
|
||||||
# VPS = Back Porch - FPS, or VPS = Vertical back Porch
|
|
||||||
#
|
|
||||||
# LPS or FPS may have a value of zero, since the length of the front porch is detemined by the
|
|
||||||
# other figures
|
|
||||||
#
|
|
||||||
# The best is to start with the recomendations of the lCD data sheet for Back porch, grab a
|
|
||||||
# sync pulse with and the determine the other, such that they meet the relations. Typically, these
|
|
||||||
# values allow for some ambuigity.
|
|
||||||
#
|
|
||||||
if lcd_type == "LB04301": # Size 480x272, 4.3", 24 Bit, 4.3"
|
|
||||||
#
|
|
||||||
# Value Min Typical Max
|
|
||||||
# DotClock 5 MHZ 9 MHz 12 MHz
|
|
||||||
# HT (Hor. Total 490 531 612
|
|
||||||
# HDP (Hor. Disp) 480
|
|
||||||
# HBP (back porch) 8 43
|
|
||||||
# HFP (Fr. porch) 2 8
|
|
||||||
# HPW (Hor. sync) 1
|
|
||||||
# VT (Vert. Total) 275 288 335
|
|
||||||
# VDP (Vert. Disp) 272
|
|
||||||
# VBP (back porch) 2 12
|
|
||||||
# VFP (fr. porch) 1 4
|
|
||||||
# VPW (vert. sync) 1 10
|
|
||||||
#
|
|
||||||
# This table in combination with the relation above leads to the settings:
|
|
||||||
# HPS = 43, HPW = 8, LPS = 0, HT = 531
|
|
||||||
# VPS = 14, VPW = 10, FPS = 0, VT = 288
|
|
||||||
#
|
|
||||||
self.disp_x_size = 479
|
|
||||||
self.disp_y_size = 271
|
|
||||||
TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x01\x70\xa3'), 3) # PLL setting for PCLK
|
|
||||||
# (9MHz * 1048576 / 100MHz) - 1 = 94371 = 0x170a3
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION
|
|
||||||
[0x20, # 24 Color bits, HSync/VSync low, No Dithering
|
|
||||||
0x00, # TFT mode
|
|
||||||
self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT
|
|
||||||
self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT
|
|
||||||
0x00]), 7) # Last byte only required for a serial TFT
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x02\x13\x00\x2b\x08\x00\x00\x00'), 8)
|
|
||||||
# HSYNC, Set HT 531 HPS 43 HPW=Sync pulse 8 LPS 0
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x01\x20\x00\x0e\x0a\x00\x00'), 7)
|
|
||||||
# VSYNC, Set VT 288 VPS 14 VPW 10 FPS 0
|
|
||||||
TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1)
|
|
||||||
# rotation/ flip, etc., t.b.d.
|
|
||||||
elif lcd_type == "AT070TN92": # Size 800x480, 7", 18 Bit, lower color bits ignored
|
|
||||||
#
|
|
||||||
# Value Min Typical Max
|
|
||||||
# DotClock 26.4 MHz 33.3 MHz 46.8 MHz
|
|
||||||
# HT (Hor. Total 862 1056 1200
|
|
||||||
# HDP (Hor. Disp) 800
|
|
||||||
# HBP (back porch) 46 46 46
|
|
||||||
# HFP (Fr. porch) 16 210 254
|
|
||||||
# HPW (Hor. sync) 1 40
|
|
||||||
# VT (Vert. Total) 510 525 650
|
|
||||||
# VDP (Vert. Disp) 480
|
|
||||||
# VBP (back porch) 23 23 23
|
|
||||||
# VFP (fr. porch) 7 22 147
|
|
||||||
# VPW (vert. sync) 1 20
|
|
||||||
#
|
|
||||||
# This table in combination with the relation above leads to the settings:
|
|
||||||
# HPS = 46, HPW = 8, LPS = 0, HT = 1056
|
|
||||||
# VPS = 23, VPW = 10, VPS = 0, VT = 525
|
|
||||||
#
|
|
||||||
self.disp_x_size = 799
|
|
||||||
self.disp_y_size = 479
|
|
||||||
TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x05\x53\xf6'), 3) # PLL setting for PCLK
|
|
||||||
# (33.3MHz * 1048576 / 100MHz) - 1 = 349174 = 0x553f6
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION
|
|
||||||
[0x00, # 18 Color bits, HSync/VSync low, No Dithering/FRC
|
|
||||||
0x00, # TFT mode
|
|
||||||
self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT
|
|
||||||
self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT
|
|
||||||
0x00]), 7) # Last byte only required for a serial TFT
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x04\x1f\x00\x2e\x08\x00\x00\x00'), 8)
|
|
||||||
# HSYNC, Set HT 1056 HPS 46 HPW 8 LPS 0
|
|
||||||
TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x02\x0c\x00\x17\x08\x00\x00'), 7)
|
|
||||||
# VSYNC, Set VT 525 VPS 23 VPW 08 FPS 0
|
|
||||||
TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1)
|
|
||||||
# rotation/ flip, etc., t.b.d.
|
|
||||||
else:
|
|
||||||
print("Wrong Parameter lcd_type: ", lcd_type)
|
|
||||||
return
|
|
||||||
TFT_io.tft_cmd_data_AS(0xBA, bytearray(b'\x0f'), 1) # GPIO[3:0] out 1
|
|
||||||
TFT_io.tft_cmd_data_AS(0xB8, bytearray(b'\x07\x01'), 1) # GPIO3=input, GPIO[2:0]=output
|
|
||||||
|
|
||||||
TFT_io.tft_cmd_data_AS(0xf0, bytearray(b'\x00'), 1) # Pixel data Interface 8 Bit
|
|
||||||
|
|
||||||
TFT_io.tft_cmd(0x29) # Display on
|
|
||||||
TFT_io.tft_cmd_data_AS(0xbe, bytearray(b'\x06\xf0\x01\xf0\x00\x00'), 6)
|
|
||||||
# Set PWM for B/L
|
|
||||||
TFT_io.tft_cmd_data_AS(0xd0, bytearray(b'\x0d'), 1) # Set DBC: enable, agressive
|
|
||||||
else:
|
|
||||||
print("Wrong Parameter controller: ", controller)
|
|
||||||
return
|
|
||||||
#
|
|
||||||
# Set character printing defaults
|
|
||||||
#
|
|
||||||
self.text_font = None
|
|
||||||
self.setTextStyle(self.color, self.BGcolor, 0, None, 0)
|
|
||||||
#
|
|
||||||
# Init done. clear Screen and switch BG LED on
|
|
||||||
#
|
|
||||||
self.text_x = self.text_y = self.text_yabs = 0
|
|
||||||
self.clrSCR() # clear the display
|
|
||||||
# self.backlight(100) ## switch BG LED on
|
|
||||||
#
|
|
||||||
# Return screen dimensions
|
|
||||||
#
|
|
||||||
def getScreensize(self):
|
|
||||||
if self.orientation == LANDSCAPE:
|
|
||||||
return (self.disp_x_size + 1, self.disp_y_size + 1)
|
|
||||||
else:
|
|
||||||
return (self.disp_y_size + 1, self.disp_x_size + 1)
|
|
||||||
#
|
|
||||||
# set backlight brightness
|
|
||||||
#
|
|
||||||
def backlight(self, percent):
|
|
||||||
percent = max(0, min(percent, 100))
|
|
||||||
self.led_ch.pulse_width_percent(percent) # set LED
|
|
||||||
#
|
|
||||||
# switch power on/off
|
|
||||||
#
|
|
||||||
def power(self, onoff):
|
|
||||||
if onoff:
|
|
||||||
self.pin_power.value(True) ## switch power on or off
|
|
||||||
else:
|
|
||||||
self.pin_power.value(False)
|
|
||||||
|
|
||||||
#
|
|
||||||
# set the tft flip modes
|
|
||||||
#
|
|
||||||
def set_tft_mode(self, v_flip = False, h_flip = False, c_flip = False, orientation = LANDSCAPE):
|
|
||||||
self.v_flip = v_flip # flip vertical
|
|
||||||
self.h_flip = h_flip # flip horizontal
|
|
||||||
self.c_flip = c_flip # flip blue/red
|
|
||||||
self.orientation = orientation # LANDSCAPE/PORTRAIT
|
|
||||||
TFT_io.tft_cmd_data_AS(0x36,
|
|
||||||
bytearray([(self.orientation << 5) |(self.c_flip << 3) | (self.h_flip & 1) << 1 | (self.v_flip) & 1]), 1)
|
|
||||||
# rotation/ flip, etc., t.b.d.
|
|
||||||
#
|
|
||||||
# get the tft flip modes
|
|
||||||
#
|
|
||||||
def get_tft_mode(self):
|
|
||||||
return (self.v_flip, self.h_flip, self.c_flip, self.orientation) #
|
|
||||||
#
|
|
||||||
# set the color used for the draw commands
|
|
||||||
#
|
|
||||||
def setColor(self, fgcolor):
|
|
||||||
self.color = fgcolor
|
|
||||||
self.colorvect = bytearray(self.color) # prepare byte array
|
|
||||||
#
|
|
||||||
# Set BG color used for the draw commands
|
|
||||||
#
|
|
||||||
def setBGColor(self, bgcolor):
|
|
||||||
self.BGcolor = bgcolor
|
|
||||||
self.BGcolorvect = bytearray(self.BGcolor) # prepare byte array
|
|
||||||
self.BMPcolortable = bytearray([self.BGcolorvect[2], # create colortable
|
|
||||||
self.BGcolorvect[1], self.BGcolorvect[0],0,
|
|
||||||
self.colorvect[2], self.colorvect[1], self.colorvect[0],0])
|
|
||||||
#
|
|
||||||
# get the color used for the draw commands
|
|
||||||
#
|
|
||||||
def getColor(self):
|
|
||||||
return self.color
|
|
||||||
#
|
|
||||||
# get BG color used for
|
|
||||||
#
|
|
||||||
def getBGColor(self):
|
|
||||||
return self.BGcolor
|
|
||||||
#
|
|
||||||
# Draw a single pixel at location x, y with color
|
|
||||||
# Rather slow at 40µs/Pixel
|
|
||||||
#
|
|
||||||
def drawPixel_py(self, x, y, color):
|
|
||||||
self.setXY(x, y, x, y)
|
|
||||||
TFT_io.displaySCR_AS(color, 1) #
|
|
||||||
#
|
|
||||||
# clear screen, set it to BG color.
|
|
||||||
#
|
|
||||||
def clrSCR(self, color = None):
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.BGcolorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
self.clrXY()
|
|
||||||
TFT_io.fillSCR_AS(colorvect, (self.disp_x_size + 1) * (self.disp_y_size + 1))
|
|
||||||
self.setScrollArea(0, self.disp_y_size + 1, 0)
|
|
||||||
self.setScrollStart(0)
|
|
||||||
self.setTextPos(0,0)
|
|
||||||
#
|
|
||||||
# reset the address range to fullscreen
|
|
||||||
#
|
|
||||||
def clrXY(self):
|
|
||||||
if self.orientation == LANDSCAPE:
|
|
||||||
self.setXY(0, 0, self.disp_x_size, self.disp_y_size)
|
|
||||||
else:
|
|
||||||
self.setXY(0, 0, self.disp_y_size, self.disp_x_size)
|
|
||||||
#
|
|
||||||
# Draw a line from x1, y1 to x2, y2 with the color set by setColor()
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawLine(self, x1, y1, x2, y2, color = None):
|
|
||||||
if y1 == y2:
|
|
||||||
self.drawHLine(x1, y1, x2 - x1 + 1, color)
|
|
||||||
elif x1 == x2:
|
|
||||||
self.drawVLine(x1, y1, y2 - y1 + 1, color)
|
|
||||||
else:
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.colorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
dx, xstep = (x2 - x1, 1) if x2 > x1 else (x1 - x2, -1)
|
|
||||||
dy, ystep = (y2 - y1, 1) if y2 > y1 else (y1 - y2, -1)
|
|
||||||
col, row = x1, y1
|
|
||||||
if dx < dy:
|
|
||||||
t = - (dy >> 1)
|
|
||||||
while True:
|
|
||||||
self.drawPixel(col, row, colorvect)
|
|
||||||
if row == y2:
|
|
||||||
return
|
|
||||||
row += ystep
|
|
||||||
t += dx
|
|
||||||
if t >= 0:
|
|
||||||
col += xstep
|
|
||||||
t -= dy
|
|
||||||
else:
|
|
||||||
t = - (dx >> 1)
|
|
||||||
while True:
|
|
||||||
self.drawPixel(col, row, colorvect)
|
|
||||||
if col == x2:
|
|
||||||
return
|
|
||||||
col += xstep
|
|
||||||
t += dy
|
|
||||||
if t >= 0:
|
|
||||||
row += ystep
|
|
||||||
t -= dx
|
|
||||||
#
|
|
||||||
# Draw a horizontal line with 1 Pixel width, from x,y to x + l - 1, y
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawHLine(self, x, y, l, color = None): # draw horiontal Line
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.colorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
if l < 0: # negative length, swap parameters
|
|
||||||
l = -l
|
|
||||||
x -= l
|
|
||||||
self.setXY(x, y, x + l - 1, y) # set display window
|
|
||||||
TFT_io.fillSCR_AS(colorvect, l)
|
|
||||||
#
|
|
||||||
# Draw a vertical line with 1 Pixel width, from x,y to x, y + l - 1
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawVLine(self, x, y, l, color = None): # draw horiontal Line
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.colorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
if l < 0: # negative length, swap parameters
|
|
||||||
l = -l
|
|
||||||
y -= l
|
|
||||||
self.setXY(x, y, x, y + l - 1) # set display window
|
|
||||||
TFT_io.fillSCR_AS(colorvect, l)
|
|
||||||
#
|
|
||||||
# Draw rectangle from x1, y1, to x2, y2
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawRectangle(self, x1, y1, x2, y2, color = None):
|
|
||||||
if x1 > x2:
|
|
||||||
x1, x2 = x2, x1
|
|
||||||
if y1 > y2:
|
|
||||||
y1, y2 = y2, y1
|
|
||||||
self.drawHLine(x1, y1, x2 - x1 + 1, color)
|
|
||||||
self.drawHLine(x1, y2, x2 - x1 + 1, color)
|
|
||||||
self.drawVLine(x1, y1, y2 - y1 + 1, color)
|
|
||||||
self.drawVLine(x2, y1, y2 - y1 + 1, color)
|
|
||||||
#
|
|
||||||
# Fill rectangle
|
|
||||||
# Almost straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def fillRectangle(self, x1, y1, x2, y2, color=None):
|
|
||||||
if x1 > x2:
|
|
||||||
x1, x2 = x2, x1
|
|
||||||
if y1 > y2:
|
|
||||||
y1, y2 = y2, y1
|
|
||||||
self.setXY(x1, y1, x2, y2) # set display window
|
|
||||||
if color:
|
|
||||||
TFT_io.fillSCR_AS(bytearray(color), (x2 - x1 + 1) * (y2 - y1 + 1))
|
|
||||||
else:
|
|
||||||
TFT_io.fillSCR_AS(self.colorvect, (x2 - x1 + 1) * (y2 - y1 + 1))
|
|
||||||
|
|
||||||
#
|
|
||||||
# Draw smooth rectangle from x1, y1, to x2, y2
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawClippedRectangle(self, x1, y1, x2, y2, color = None):
|
|
||||||
if x1 > x2:
|
|
||||||
x1, x2 = x2, x1
|
|
||||||
if y1 > y2:
|
|
||||||
y1, y2 = y2, y1
|
|
||||||
if (x2-x1) > 4 and (y2-y1) > 4:
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.colorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
self.drawPixel(x1 + 2,y1 + 1, colorvect)
|
|
||||||
self.drawPixel(x1 + 1,y1 + 2, colorvect)
|
|
||||||
self.drawPixel(x2 - 2,y1 + 1, colorvect)
|
|
||||||
self.drawPixel(x2 - 1,y1 + 2, colorvect)
|
|
||||||
self.drawPixel(x1 + 2,y2 - 1, colorvect)
|
|
||||||
self.drawPixel(x1 + 1,y2 - 2, colorvect)
|
|
||||||
self.drawPixel(x2 - 2,y2 - 1, colorvect)
|
|
||||||
self.drawPixel(x2 - 1,y2 - 2, colorvect)
|
|
||||||
self.drawHLine(x1 + 3, y1, x2 - x1 - 5, colorvect)
|
|
||||||
self.drawHLine(x1 + 3, y2, x2 - x1 - 5, colorvect)
|
|
||||||
self.drawVLine(x1, y1 + 3, y2 - y1 - 5, colorvect)
|
|
||||||
self.drawVLine(x2, y1 + 3, y2 - y1 - 5, colorvect)
|
|
||||||
#
|
|
||||||
# Fill smooth rectangle from x1, y1, to x2, y2
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def fillClippedRectangle(self, x1, y1, x2, y2, color = None):
|
|
||||||
if x1 > x2:
|
|
||||||
t = x1; x1 = x2; x2 = t
|
|
||||||
if y1 > y2:
|
|
||||||
t = y1; y1 = y2; y2 = t
|
|
||||||
if (x2-x1) > 4 and (y2-y1) > 4:
|
|
||||||
for i in range(((y2 - y1) // 2) + 1):
|
|
||||||
if i == 0:
|
|
||||||
self.drawHLine(x1 + 3, y1 + i, x2 - x1 - 5, color)
|
|
||||||
self.drawHLine(x1 + 3, y2 - i, x2 - x1 - 5, color)
|
|
||||||
elif i == 1:
|
|
||||||
self.drawHLine(x1 + 2, y1 + i, x2 - x1 - 3, color)
|
|
||||||
self.drawHLine(x1 + 2, y2 - i, x2 - x1 - 3, color)
|
|
||||||
elif i == 2:
|
|
||||||
self.drawHLine(x1 + 1, y1 + i, x2 - x1 - 1, color)
|
|
||||||
self.drawHLine(x1 + 1, y2 - i, x2 - x1 - 1, color)
|
|
||||||
else:
|
|
||||||
self.drawHLine(x1, y1 + i, x2 - x1 + 1, color)
|
|
||||||
self.drawHLine(x1, y2 - i, x2 - x1 + 1, color)
|
|
||||||
#
|
|
||||||
# draw a circle at x, y with radius
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
#
|
|
||||||
def drawCircle(self, x, y, radius, color = None):
|
|
||||||
|
|
||||||
if color is None:
|
|
||||||
colorvect = self.colorvect
|
|
||||||
else:
|
|
||||||
colorvect = bytearray(color)
|
|
||||||
|
|
||||||
f = 1 - radius
|
|
||||||
ddF_x = 1
|
|
||||||
ddF_y = -2 * radius
|
|
||||||
x1 = 0
|
|
||||||
y1 = radius
|
|
||||||
|
|
||||||
self.drawPixel(x, y + radius, colorvect)
|
|
||||||
self.drawPixel(x, y - radius, colorvect)
|
|
||||||
self.drawPixel(x + radius, y, colorvect)
|
|
||||||
self.drawPixel(x - radius, y, colorvect)
|
|
||||||
|
|
||||||
while x1 < y1:
|
|
||||||
if f >= 0:
|
|
||||||
y1 -= 1
|
|
||||||
ddF_y += 2
|
|
||||||
f += ddF_y
|
|
||||||
x1 += 1
|
|
||||||
ddF_x += 2
|
|
||||||
f += ddF_x
|
|
||||||
self.drawPixel(x + x1, y + y1, colorvect)
|
|
||||||
self.drawPixel(x - x1, y + y1, colorvect)
|
|
||||||
self.drawPixel(x + x1, y - y1, colorvect)
|
|
||||||
self.drawPixel(x - x1, y - y1, colorvect)
|
|
||||||
self.drawPixel(x + y1, y + x1, colorvect)
|
|
||||||
self.drawPixel(x - y1, y + x1, colorvect)
|
|
||||||
self.drawPixel(x + y1, y - x1, colorvect)
|
|
||||||
self.drawPixel(x - y1, y - x1, colorvect)
|
|
||||||
#
|
|
||||||
# fill a circle at x, y with radius
|
|
||||||
# Straight port from the UTFT Library at Rinky-Dink Electronics
|
|
||||||
# Instead of calculating x = sqrt(r*r - y*y), it searches the x
|
|
||||||
# for r*r = x*x + x*x
|
|
||||||
#
|
|
||||||
def fillCircle(self, x, y, radius, color = None):
|
|
||||||
r_square = radius * radius * 4
|
|
||||||
for y1 in range (-(radius * 2), 1):
|
|
||||||
y_square = y1 * y1
|
|
||||||
for x1 in range (-(radius * 2), 1):
|
|
||||||
if x1*x1+y_square <= r_square:
|
|
||||||
x1i = x1 // 2
|
|
||||||
y1i = y1 // 2
|
|
||||||
self.drawHLine(x + x1i, y + y1i, 2 * (-x1i), color)
|
|
||||||
self.drawHLine(x + x1i, y - y1i, 2 * (-x1i), color)
|
|
||||||
break;
|
|
||||||
#
|
|
||||||
# Draw a bitmap at x,y with size sx, sy
|
|
||||||
# mode determines the type of expected data
|
|
||||||
# mode = 1: The data contains 1 bit per pixel, mapped to fg/bg color
|
|
||||||
# unless a colortable is provided
|
|
||||||
# mode = 2: The data contains 2 bit per pixel; a colortable with 4 entries must be provided
|
|
||||||
# mode = 4: The data contains 4 bit per pixel;
|
|
||||||
# a colortable with 16 entries must be provided
|
|
||||||
# mode = 8: The data contains 8 bit per pixel;
|
|
||||||
# a colortable with 256 entries must be provided
|
|
||||||
# mode = 16: The data must contain 2 packed bytes/pixel red/green/blue in 565 format
|
|
||||||
# mode = 24: The data must contain 3 bytes/pixel red/green/blue
|
|
||||||
#
|
|
||||||
def drawBitmap(self, x, y, sx, sy, data, mode = 24, colortable = None):
|
|
||||||
self.setXY(x, y, x + sx - 1, y + sy - 1)
|
|
||||||
if mode == 24:
|
|
||||||
TFT_io.displaySCR_AS(data, sx * sy)
|
|
||||||
elif mode == 16:
|
|
||||||
TFT_io.displaySCR565_AS(data, sx * sy)
|
|
||||||
elif mode == 1:
|
|
||||||
if colortable is None:
|
|
||||||
colortable = self.BMPcolortable # create colortable
|
|
||||||
TFT_io.displaySCR_bmp(data, sx*sy, 1, colortable)
|
|
||||||
elif mode == 2:
|
|
||||||
if colortable is None:
|
|
||||||
return
|
|
||||||
TFT_io.displaySCR_bmp(data, sx*sy, 2, colortable)
|
|
||||||
elif mode == 4:
|
|
||||||
if colortable is None:
|
|
||||||
return
|
|
||||||
TFT_io.displaySCR_bmp(data, sx*sy, 4, colortable)
|
|
||||||
elif mode == 8:
|
|
||||||
if colortable is None:
|
|
||||||
return
|
|
||||||
TFT_io.displaySCR_bmp(data, sx*sy, 8, colortable)
|
|
||||||
|
|
||||||
#
|
|
||||||
# set scroll area to the region between the first and last line
|
|
||||||
#
|
|
||||||
def setScrollArea(self, tfa, vsa, bfa):
|
|
||||||
TFT_io.tft_cmd_data_AS(0x33, bytearray( #set scrolling range
|
|
||||||
[(tfa >> 8) & 0xff, tfa & 0xff,
|
|
||||||
(vsa >> 8) & 0xff, vsa & 0xff,
|
|
||||||
(bfa >> 8) & 0xff, bfa & 0xff]), 6)
|
|
||||||
self.scroll_tfa = tfa
|
|
||||||
self.scroll_vsa = vsa
|
|
||||||
self.scroll_bfa = bfa
|
|
||||||
self.setScrollStart(self.scroll_tfa)
|
|
||||||
x, y = self.getTextPos()
|
|
||||||
self.setTextPos(x, y) # realign pointers
|
|
||||||
#
|
|
||||||
# get scroll area of the region between the first and last line
|
|
||||||
#
|
|
||||||
def getScrollArea(self):
|
|
||||||
return self.scroll_tfa, self.scroll_vsa, self.scroll_bfa
|
|
||||||
#
|
|
||||||
# set the line which is displayed first
|
|
||||||
#
|
|
||||||
def setScrollStart(self, lline):
|
|
||||||
self.scroll_start = lline # store the logical first line
|
|
||||||
TFT_io.tft_cmd_data_AS(0x37, bytearray([(lline >> 8) & 0xff, lline & 0xff]), 2)
|
|
||||||
#
|
|
||||||
# get the line which is displayed first
|
|
||||||
#
|
|
||||||
def getScrollStart(self):
|
|
||||||
return self.scroll_start # get the logical first line
|
|
||||||
|
|
||||||
#
|
|
||||||
# Scroll vsa up/down by a number of pixels
|
|
||||||
#
|
|
||||||
def scroll(self, pixels):
|
|
||||||
line = ((self.scroll_start - self.scroll_tfa + pixels) % self.scroll_vsa
|
|
||||||
+ self.scroll_tfa)
|
|
||||||
self.setScrollStart(line) # set the new line
|
|
||||||
#
|
|
||||||
# Set text position
|
|
||||||
#
|
|
||||||
def setTextPos(self, x, y, clip = False, scroll = True):
|
|
||||||
self.text_width, self.text_height = self.getScreensize() ## height possibly wrong
|
|
||||||
self.text_x = x
|
|
||||||
if self.scroll_tfa <= y < (self.scroll_tfa + self.scroll_vsa): # in scroll area ? check later for < or <=
|
|
||||||
# correct position relative to scroll start
|
|
||||||
self.text_y = (y + self.scroll_start - self.scroll_tfa)
|
|
||||||
if self.text_y >= (self.scroll_tfa + self.scroll_vsa):
|
|
||||||
self.text_y -= self.scroll_vsa
|
|
||||||
else: # absolute
|
|
||||||
self.text_y = y
|
|
||||||
self.text_yabs = y
|
|
||||||
# Hint: self.text_yabs = self.text_y - self.scroll_start) % self.scroll_vsa + self.scroll_tfa)
|
|
||||||
if clip and (self.text_x + clip) < self.text_width:
|
|
||||||
self.text_width = self.text_x + clip
|
|
||||||
self.text_scroll = scroll
|
|
||||||
#
|
|
||||||
# Get text position
|
|
||||||
#
|
|
||||||
def getTextPos(self):
|
|
||||||
return (self.text_x, self.text_yabs)
|
|
||||||
#
|
|
||||||
# Set Text Style
|
|
||||||
#
|
|
||||||
def setTextStyle(self, fgcolor=None, bgcolor=None, transparency=None, font=None, gap=None):
|
|
||||||
if font is not None:
|
|
||||||
self.text_font = font
|
|
||||||
self.text_rows, self.text_cols, nchar, first = font.get_properties() #
|
|
||||||
if transparency is not None:
|
|
||||||
self.transparency = transparency
|
|
||||||
if gap is not None:
|
|
||||||
self.text_gap = gap
|
|
||||||
self.text_color = bytearray(0)
|
|
||||||
if bgcolor is not None:
|
|
||||||
self.text_bgcolor = bgcolor
|
|
||||||
if fgcolor is not None:
|
|
||||||
self.text_fgcolor = fgcolor
|
|
||||||
if transparency is not None:
|
|
||||||
self.transparency = transparency
|
|
||||||
self.text_color = (bytearray(self.text_bgcolor)
|
|
||||||
+ bytearray(self.text_fgcolor)
|
|
||||||
+ bytearray([self.transparency]))
|
|
||||||
if gap is not None:
|
|
||||||
self.text_gap = gap
|
|
||||||
#
|
|
||||||
# Get Text Style: return (color, bgcolor, font, transpareny, gap)
|
|
||||||
#
|
|
||||||
def getTextStyle(self):
|
|
||||||
return (self.text_color[3:6], self.text_color[0:3],
|
|
||||||
self.transparency, self.text_font, self.text_gap)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check, if a new line is to be opened
|
|
||||||
# if yes, advance, including scrolling, and clear line, if flags is set
|
|
||||||
# Obsolete?
|
|
||||||
#
|
|
||||||
def printNewline(self, clear = False):
|
|
||||||
if (self.text_yabs + self.text_rows) >= (self.scroll_tfa + self.scroll_vsa): # does the line fit?
|
|
||||||
self.scroll(self.text_rows) # no. scroll
|
|
||||||
else: # Yes, just advance pointers
|
|
||||||
self.text_yabs += self.text_rows
|
|
||||||
self.setTextPos(self.text_x, self.text_yabs)
|
|
||||||
if clear:
|
|
||||||
self.printClrLine(2) # clear actual line
|
|
||||||
#
|
|
||||||
# Carriage Return
|
|
||||||
#
|
|
||||||
def printCR(self): # clear to end of line
|
|
||||||
self.text_x = 0
|
|
||||||
#
|
|
||||||
# clear line modes
|
|
||||||
#
|
|
||||||
def printClrLine(self, mode = 0): # clear to end of line/bol/line
|
|
||||||
if mode == 0:
|
|
||||||
self.setXY(self.text_x, self.text_y,
|
|
||||||
self.text_width - 1, self.text_y + self.text_rows - 1) # set display window
|
|
||||||
TFT_io.fillSCR_AS(self.text_color, (self.text_width - self.text_x + 1) * self.text_rows)
|
|
||||||
elif mode == 1 and self.text_x > 0:
|
|
||||||
self.setXY(0, self.text_y,
|
|
||||||
self.text_x - 1, self.text_y + self.text_rows - 1) # set display window
|
|
||||||
TFT_io.fillSCR_AS(self.text_color, (self.text_x - 1) * self.text_rows)
|
|
||||||
elif mode == 2:
|
|
||||||
self.setXY(0, self.text_y,
|
|
||||||
self.text_width - 1, self.text_y + self.text_rows - 1) # set display window
|
|
||||||
TFT_io.fillSCR_AS(self.text_color, self.text_width * self.text_rows)
|
|
||||||
#
|
|
||||||
# clear sreen modes
|
|
||||||
#
|
|
||||||
def printClrSCR(self): # clear Area set by setScrollArea
|
|
||||||
self.setXY(0, self.scroll_tfa,
|
|
||||||
self.text_width - 1, self.scroll_tfa + self.scroll_vsa) # set display window
|
|
||||||
TFT_io.fillSCR_AS(self.text_color, self.text_width * self.scroll_vsa)
|
|
||||||
self.setScrollStart(self.scroll_tfa)
|
|
||||||
self.setTextPos(0, self.scroll_tfa)
|
|
||||||
#
|
|
||||||
# Print string s, returning the length of the printed string in pixels
|
|
||||||
#
|
|
||||||
def printString(self, s, bg_buf=None):
|
|
||||||
len = 0
|
|
||||||
for c in s:
|
|
||||||
cols = self.printChar(c, bg_buf)
|
|
||||||
if cols == 0: # could not print (any more)
|
|
||||||
break
|
|
||||||
len += cols
|
|
||||||
return len
|
|
||||||
#
|
|
||||||
# Print string c using the given char bitmap at location x, y, returning the width of the printed char in pixels
|
|
||||||
#
|
|
||||||
def printChar(self, c, bg_buf=None):
|
|
||||||
# get the charactes pixel bitmap and dimensions
|
|
||||||
if self.text_font:
|
|
||||||
fontptr, rows, cols = self.text_font.get_ch(ord(c))
|
|
||||||
else:
|
|
||||||
raise AttributeError('No font selected')
|
|
||||||
pix_count = cols * rows # number of bits in the char
|
|
||||||
# test char fit
|
|
||||||
if self.text_x + cols > self.text_width: # does the char fit on the screen?
|
|
||||||
if self.text_scroll:
|
|
||||||
self.printCR() # No, then CR
|
|
||||||
self.printNewline(True) # NL: advance to the next line
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
# Retrieve Background data if transparency is required
|
|
||||||
if self.transparency: # in case of transpareny, the frame buffer content is needed
|
|
||||||
if not bg_buf: # buffer allocation needed?
|
|
||||||
bg_buf = bytearray(pix_count * 3) # sigh...
|
|
||||||
self.setXY(self.text_x, self.text_y, self.text_x + cols - 1, self.text_y + rows - 1) # set area
|
|
||||||
TFT_io.tft_read_cmd_data_AS(0x2e, bg_buf, pix_count * 3) # read background data
|
|
||||||
else:
|
|
||||||
bg_buf = 0 # dummy assignment, since None is not accepted
|
|
||||||
# Set XY range & print char
|
|
||||||
self.setXY(self.text_x, self.text_y, self.text_x + cols - 1, self.text_y + rows - 1) # set area
|
|
||||||
TFT_io.displaySCR_charbitmap(fontptr, pix_count, self.text_color, bg_buf) # display char!
|
|
||||||
#advance pointer
|
|
||||||
self.text_x += (cols + self.text_gap)
|
|
||||||
return cols + self.text_gap
|
|
||||||
|
|
||||||
|
|
293
tft_gui/touch.py
293
tft_gui/touch.py
|
@ -1,293 +0,0 @@
|
||||||
#
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 Robert Hammelrath
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# Class supporting the resisitve touchpad of TFT LC-displays
|
|
||||||
# First example: Controller XPT2046
|
|
||||||
# It uses Y5..Y8 of PyBoard
|
|
||||||
#
|
|
||||||
import pyb, stm
|
|
||||||
# define constants
|
|
||||||
#
|
|
||||||
PCB_VERSION = 2
|
|
||||||
|
|
||||||
#if PCB_VERSION == 1:
|
|
||||||
# CONTROL_PORT = stm.GPIOB
|
|
||||||
# T_CLOCK = const(1 << 15) ## Y8 = B15
|
|
||||||
# T_DOUT = const(1 << 14) ## Y7 = B14
|
|
||||||
# T_DIN = const(1 << 13) ## Y6 = B13
|
|
||||||
# T_IRQ = const(1 << 12) ## Y5 = B12
|
|
||||||
|
|
||||||
if PCB_VERSION == 2:
|
|
||||||
CONTROL_PORT = stm.GPIOC
|
|
||||||
T_CLOCK = const(1 << 5) ## X12 = C5
|
|
||||||
T_DOUT = const(1 << 4) ## X11 = C4
|
|
||||||
T_DIN = const(1 << 7) ## Y2 = C7
|
|
||||||
T_IRQ = const(1 << 6) ## Y1 = C6
|
|
||||||
|
|
||||||
# T_CS is not used and must be hard tied to GND
|
|
||||||
|
|
||||||
T_GETX = const(0xd0) ## 12 bit resolution
|
|
||||||
T_GETY = const(0x90) ## 12 bit resolution
|
|
||||||
T_GETZ1 = const(0xb8) ## 8 bit resolution
|
|
||||||
T_GETZ2 = const(0xc8) ## 8 bit resolution
|
|
||||||
#
|
|
||||||
X_LOW = const(10) ## lowest reasonable X value from the touchpad
|
|
||||||
Y_HIGH = const(4090) ## highest reasonable Y value
|
|
||||||
|
|
||||||
class TOUCH:
|
|
||||||
#
|
|
||||||
# Init just sets the PIN's to In / out as required
|
|
||||||
# objsched: scheduler if asynchronous operation intended
|
|
||||||
# confidence: confidence level - number of consecutive touches with a margin smaller than the given level
|
|
||||||
# which the function will sample until it accepts it as a valid touch
|
|
||||||
# margin: Difference from mean centre at which touches are considered at the same position
|
|
||||||
# delay: Delay between samples in ms. (n/a if asynchronous)
|
|
||||||
#
|
|
||||||
DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814)
|
|
||||||
def __init__(self, controller = "XPT2046", objsched = None, *, confidence = 5, margin = 50, delay = 10, calibration = None):
|
|
||||||
if PCB_VERSION == 1:
|
|
||||||
self.pin_clock = pyb.Pin("Y8", pyb.Pin.OUT_PP)
|
|
||||||
self.pin_clock.value(0)
|
|
||||||
self.pin_d_out = pyb.Pin("Y7", pyb.Pin.OUT_PP)
|
|
||||||
self.pin_d_in = pyb.Pin("Y6", pyb.Pin.IN)
|
|
||||||
self.pin_irq = pyb.Pin("Y5", pyb.Pin.IN)
|
|
||||||
else:
|
|
||||||
self.pin_clock = pyb.Pin("X11", pyb.Pin.OUT_PP)
|
|
||||||
self.pin_clock.value(0)
|
|
||||||
self.pin_d_out = pyb.Pin("X12", pyb.Pin.OUT_PP)
|
|
||||||
self.pin_d_in = pyb.Pin("Y1", pyb.Pin.IN)
|
|
||||||
self.pin_irq = pyb.Pin("Y2", pyb.Pin.IN)
|
|
||||||
# set default values
|
|
||||||
self.ready = False
|
|
||||||
self.touched = False
|
|
||||||
self.x = 0
|
|
||||||
self.y = 0
|
|
||||||
self.buf_length = 0
|
|
||||||
cal = TOUCH.DEFAULT_CAL if calibration is None else calibration
|
|
||||||
self.asynchronous = False
|
|
||||||
self.touch_parameter(confidence, margin, delay, cal)
|
|
||||||
if objsched is not None:
|
|
||||||
self.asynchronous = True
|
|
||||||
objsched.add_thread(self._main_thread())
|
|
||||||
|
|
||||||
# set parameters for get_touch()
|
|
||||||
# res: Resolution in bits of the returned values, default = 10
|
|
||||||
# confidence: confidence level - number of consecutive touches with a margin smaller than the given level
|
|
||||||
# which the function will sample until it accepts it as a valid touch
|
|
||||||
# margin: Difference from mean centre at which touches are considered at the same position
|
|
||||||
# delay: Delay between samples in ms.
|
|
||||||
#
|
|
||||||
def touch_parameter(self, confidence = 5, margin = 50, delay = 10, calibration = None):
|
|
||||||
if not self.asynchronous: # Ignore attempts to change on the fly.
|
|
||||||
confidence = max(min(confidence, 25), 5)
|
|
||||||
if confidence != self.buf_length:
|
|
||||||
self.buff = [[0,0] for x in range(confidence)]
|
|
||||||
self.buf_length = confidence
|
|
||||||
self.delay = max(min(delay, 100), 5)
|
|
||||||
margin = max(min(margin, 100), 1)
|
|
||||||
self.margin = margin * margin # store the square value
|
|
||||||
if calibration:
|
|
||||||
self.calibration = calibration
|
|
||||||
|
|
||||||
# get_touch(): Synchronous use. get a touch value; Parameters:
|
|
||||||
#
|
|
||||||
# initital: Wait for a non-touch state before getting a sample.
|
|
||||||
# True = Initial wait for a non-touch state
|
|
||||||
# False = Do not wait for a release
|
|
||||||
# wait: Wait for a touch or not?
|
|
||||||
# False: Do not wait for a touch and return immediately
|
|
||||||
# True: Wait until a touch is pressed.
|
|
||||||
# raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned
|
|
||||||
# setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping
|
|
||||||
# timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release
|
|
||||||
#
|
|
||||||
# Return (x,y) or None
|
|
||||||
#
|
|
||||||
def get_touch(self, initial = True, wait = True, raw = False, timeout = None):
|
|
||||||
if self.asynchronous:
|
|
||||||
return None # Should only be called in synhronous mode
|
|
||||||
if timeout == None:
|
|
||||||
timeout = 3600000 # set timeout to 1 hour
|
|
||||||
#
|
|
||||||
if initial: ## wait for a non-touch state
|
|
||||||
sample = True
|
|
||||||
while sample and timeout > 0:
|
|
||||||
sample = self.raw_touch()
|
|
||||||
pyb.delay(self.delay)
|
|
||||||
timeout -= self.delay
|
|
||||||
if timeout <= 0: # after timeout, return None
|
|
||||||
return None
|
|
||||||
#
|
|
||||||
buff = self.buff
|
|
||||||
buf_length = self.buf_length
|
|
||||||
buffptr = 0
|
|
||||||
nsamples = 0
|
|
||||||
while timeout > 0:
|
|
||||||
if nsamples == buf_length:
|
|
||||||
meanx = sum([c[0] for c in buff]) // buf_length
|
|
||||||
meany = sum([c[1] for c in buff]) // buf_length
|
|
||||||
dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length
|
|
||||||
if dev <= self.margin: # got one; compare against the square value
|
|
||||||
if raw:
|
|
||||||
return (meanx, meany)
|
|
||||||
else:
|
|
||||||
return self.do_normalize((meanx, meany))
|
|
||||||
# get a new value
|
|
||||||
sample = self.raw_touch() # get a touch
|
|
||||||
if sample == None:
|
|
||||||
if not wait:
|
|
||||||
return None
|
|
||||||
nsamples = 0 # Invalidate buff
|
|
||||||
else:
|
|
||||||
buff[buffptr] = sample # put in buff
|
|
||||||
buffptr = (buffptr + 1) % buf_length
|
|
||||||
nsamples = min(nsamples +1, buf_length)
|
|
||||||
pyb.delay(self.delay)
|
|
||||||
timeout -= self.delay
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Asynchronous use: this thread maintains self.x and self.y
|
|
||||||
def _main_thread(self):
|
|
||||||
buff = self.buff
|
|
||||||
buf_length = self.buf_length
|
|
||||||
buffptr = 0
|
|
||||||
nsamples = 0
|
|
||||||
yield # Initialisation complete, wait for scheduler to start
|
|
||||||
while True:
|
|
||||||
if nsamples == buf_length:
|
|
||||||
meanx = sum([c[0] for c in buff]) // buf_length
|
|
||||||
meany = sum([c[1] for c in buff]) // buf_length
|
|
||||||
dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length
|
|
||||||
if dev <= self.margin: # got one; compare against the square value
|
|
||||||
self.ready = True
|
|
||||||
self.x, self.y = self.do_normalize((meanx, meany))
|
|
||||||
sample = self.raw_touch() # get a touch
|
|
||||||
if sample == None:
|
|
||||||
self.touched = False
|
|
||||||
self.ready = False
|
|
||||||
nsamples = 0 # Invalidate buff
|
|
||||||
else:
|
|
||||||
self.touched = True
|
|
||||||
buff[buffptr] = sample # put in buff
|
|
||||||
buffptr = (buffptr + 1) % buf_length
|
|
||||||
nsamples = min(nsamples + 1, buf_length)
|
|
||||||
yield
|
|
||||||
|
|
||||||
# Asynchronous get_touch
|
|
||||||
def get_touch_async(self):
|
|
||||||
if self.ready:
|
|
||||||
self.ready = False
|
|
||||||
return self.x, self.y
|
|
||||||
return None
|
|
||||||
#
|
|
||||||
# do_normalize(touch)
|
|
||||||
# calculate the screen coordinates from the touch values, using the calibration values
|
|
||||||
# touch must be the tuple return by get_touch
|
|
||||||
#
|
|
||||||
def do_normalize(self, touch):
|
|
||||||
xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096)
|
|
||||||
xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096)
|
|
||||||
ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096)
|
|
||||||
yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096)
|
|
||||||
x = int((touch[0] + xadd) * xmul)
|
|
||||||
y = int((touch[1] + yadd) * ymul)
|
|
||||||
return (x, y)
|
|
||||||
#
|
|
||||||
# raw_touch(tuple)
|
|
||||||
# raw read touch. Returns (x,y) or None
|
|
||||||
#
|
|
||||||
def raw_touch(self):
|
|
||||||
global CONTROL_PORT
|
|
||||||
x = self.touch_talk(T_GETX, 12, CONTROL_PORT)
|
|
||||||
y = self.touch_talk(T_GETY, 12, CONTROL_PORT)
|
|
||||||
if x > X_LOW and y < Y_HIGH: # touch pressed?
|
|
||||||
return (x, y)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
#
|
|
||||||
# Send a command to the touch controller and wait for the response
|
|
||||||
# cmd is the command byte
|
|
||||||
# int is the expected size of return data bits
|
|
||||||
# port is the gpio base port
|
|
||||||
#
|
|
||||||
# Straight down coding of the data sheet's timing diagram
|
|
||||||
# Clock low & high cycles must last at least 200ns, therefore
|
|
||||||
# additional delays are required. At the moment it is set to
|
|
||||||
# about 500ns each, 1µs total at 168 MHz clock rate.
|
|
||||||
# Total net time for a 12 bit sample: ~ 25 µs, 8 bit sample ~20 µs
|
|
||||||
#
|
|
||||||
@staticmethod
|
|
||||||
@micropython.viper
|
|
||||||
def touch_talk(cmd: int, bits: int, port: int) -> int:
|
|
||||||
gpio_bsr = ptr16(port + stm.GPIO_BSRRL)
|
|
||||||
gpio_idr = ptr16(port + stm.GPIO_IDR)
|
|
||||||
#
|
|
||||||
# now shift the command out, which is 8 bits
|
|
||||||
# data is sampled at the low-> high transient
|
|
||||||
#
|
|
||||||
gpio_bsr[1] = T_CLOCK # Empty clock cycle before start, maybe obsolete
|
|
||||||
for i in range(2): pass #delay
|
|
||||||
# gpio_bsr[0] = T_CLOCK # clock High
|
|
||||||
# for i in range(2): pass #delay
|
|
||||||
# gpio_bsr[1] = T_CLOCK # set clock low in the beginning
|
|
||||||
mask = 0x80 # high bit first
|
|
||||||
for i in range(8):
|
|
||||||
gpio_bsr[1] = T_CLOCK # set clock low in the beginning
|
|
||||||
if cmd & mask:
|
|
||||||
gpio_bsr[0] = T_DOUT # set data bit high
|
|
||||||
else:
|
|
||||||
gpio_bsr[1] = T_DOUT # set data bit low
|
|
||||||
for i in range(1): pass #delay
|
|
||||||
gpio_bsr[0] = T_CLOCK # set clock high
|
|
||||||
mask >>= 1
|
|
||||||
for i in range(0): pass #delay
|
|
||||||
gpio_bsr[1] = T_CLOCK | T_DOUT# Another clock & data, low
|
|
||||||
for i in range(2): pass #delay
|
|
||||||
gpio_bsr[0] = T_CLOCK # clock High
|
|
||||||
for i in range(0): pass #delay
|
|
||||||
#
|
|
||||||
# now shift the data in, which is 8 or 12 bits
|
|
||||||
# data is sampled after the high->low transient
|
|
||||||
#
|
|
||||||
result = 0
|
|
||||||
for i in range(bits):
|
|
||||||
gpio_bsr[1] = T_CLOCK # Clock low
|
|
||||||
for i in range(1): pass # short delay
|
|
||||||
if gpio_idr[0] & T_DIN: # get data
|
|
||||||
bit = 1
|
|
||||||
else:
|
|
||||||
bit = 0
|
|
||||||
result = (result << 1) | bit # shift data in
|
|
||||||
gpio_bsr[0] = T_CLOCK # Clock high
|
|
||||||
for i in range(1): pass # delay
|
|
||||||
#
|
|
||||||
# another clock cycle, maybe obsolete
|
|
||||||
#
|
|
||||||
gpio_bsr[1] = T_CLOCK # Another clock toggle, low
|
|
||||||
for i in range(2): pass # delay
|
|
||||||
gpio_bsr[0] = T_CLOCK # clock High
|
|
||||||
for i in range(2): pass #delay
|
|
||||||
gpio_bsr[1] = T_CLOCK # Clock low
|
|
||||||
# now we're ready to leave
|
|
||||||
return result
|
|
||||||
|
|
734
tft_gui/ugui.py
734
tft_gui/ugui.py
|
@ -1,734 +0,0 @@
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 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 TFT_io
|
|
||||||
import math
|
|
||||||
from delay import Delay
|
|
||||||
|
|
||||||
TWOPI = 2 * math.pi
|
|
||||||
|
|
||||||
CIRCLE = 1
|
|
||||||
RECTANGLE = 2
|
|
||||||
CLIPPED_RECT = 3
|
|
||||||
WHITE = (255, 255, 255)
|
|
||||||
BLACK = (0, 0, 0)
|
|
||||||
RED = (255, 0, 0)
|
|
||||||
GREEN = (0, 255, 0)
|
|
||||||
BLUE = (0, 0, 255)
|
|
||||||
YELLOW = (255, 255, 0)
|
|
||||||
GREY = (100, 100, 100)
|
|
||||||
|
|
||||||
def get_stringsize(s, font):
|
|
||||||
hor = 0
|
|
||||||
for c in s:
|
|
||||||
_, vert, cols = font.get_ch(ord(c))
|
|
||||||
hor += cols
|
|
||||||
return hor, vert
|
|
||||||
|
|
||||||
def print_centered(tft, x, y, s, color, font):
|
|
||||||
length, height = get_stringsize(s, font)
|
|
||||||
tft.setTextStyle(color, None, 2, font)
|
|
||||||
tft.setTextPos(x - length // 2, y - height // 2)
|
|
||||||
tft.printString(s)
|
|
||||||
|
|
||||||
# Base class for all displayable objects
|
|
||||||
class NoTouch(object):
|
|
||||||
old_color = None
|
|
||||||
def __init__(self, tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border):
|
|
||||||
self.tft = tft
|
|
||||||
self.location = location
|
|
||||||
self.font = font
|
|
||||||
self.height = height
|
|
||||||
self.width = width
|
|
||||||
self.fill = bgcolor is not None
|
|
||||||
self.fgcolor = fgcolor if fgcolor is not None else tft.getColor()
|
|
||||||
self.bgcolor = bgcolor if bgcolor is not None else tft.getBGColor()
|
|
||||||
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
|
|
||||||
self.hasborder = border is not None
|
|
||||||
self.border = 0 if border is None else border
|
|
||||||
if NoTouch.old_color is None:
|
|
||||||
NoTouch.old_color = tft.getColor()
|
|
||||||
if height is not None and width is not None: # beware special cases where height and width not yet known
|
|
||||||
self.draw_border()
|
|
||||||
|
|
||||||
def draw_border(self): # Draw background and bounding box if required
|
|
||||||
tft = self.tft
|
|
||||||
fgcolor = tft.getColor()
|
|
||||||
x = self.location[0]
|
|
||||||
y = self.location[1]
|
|
||||||
if self.fill:
|
|
||||||
tft.setColor(self.bgcolor)
|
|
||||||
tft.fillRectangle(x, y, x + self.width, y + self.height)
|
|
||||||
bw = 0 # border width
|
|
||||||
if self.hasborder: # Draw a bounding box
|
|
||||||
bw = self.border
|
|
||||||
tft.setColor(self.fgcolor)
|
|
||||||
tft.drawRectangle(x, y, x + self.width, y + self.height)
|
|
||||||
tft.setColor(fgcolor)
|
|
||||||
return bw # Actual width (may be 0)
|
|
||||||
|
|
||||||
def restore_color(self): # Restore to system default
|
|
||||||
self.tft.setColor(NoTouch.old_color)
|
|
||||||
|
|
||||||
# Base class for touch-enabled classes.
|
|
||||||
class Touchable(NoTouch):
|
|
||||||
touchlist = []
|
|
||||||
objtouch = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def touchtest(cls): # Singleton thread tests all touchable instances
|
|
||||||
mytouch = cls.objtouch
|
|
||||||
while True:
|
|
||||||
yield
|
|
||||||
if mytouch.ready:
|
|
||||||
x, y = mytouch.get_touch_async()
|
|
||||||
for obj in cls.touchlist:
|
|
||||||
if obj.enabled:
|
|
||||||
obj.trytouch(x, y)
|
|
||||||
elif not mytouch.touched:
|
|
||||||
for obj in cls.touchlist:
|
|
||||||
if obj.was_touched:
|
|
||||||
obj.was_touched = False # Call untouched once only
|
|
||||||
obj.busy = False
|
|
||||||
obj.untouched()
|
|
||||||
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border, can_drag):
|
|
||||||
super().__init__(tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border)
|
|
||||||
Touchable.touchlist.append(self)
|
|
||||||
self.can_drag = can_drag
|
|
||||||
self.busy = False
|
|
||||||
self.enabled = True # Available to user/subclass
|
|
||||||
self.was_touched = False
|
|
||||||
self.objsched = objsched
|
|
||||||
if Touchable.objtouch is None: # Initialising class and thread
|
|
||||||
Touchable.objtouch = objtouch
|
|
||||||
objsched.add_thread(self.touchtest()) # One thread only
|
|
||||||
|
|
||||||
def trytouch(self, x, y): # If touched in bounding box, process it otherwise do nothing
|
|
||||||
x0 = self.location[0]
|
|
||||||
x1 = self.location[0] + self.width
|
|
||||||
y0 = self.location[1]
|
|
||||||
y1 = self.location[1] + self.height
|
|
||||||
if x0 <= x <= x1 and y0 <= y <= y1:
|
|
||||||
self.was_touched = True
|
|
||||||
if not self.busy or self.can_drag:
|
|
||||||
self.touched(x, y) # Called repeatedly for draggable objects
|
|
||||||
self.busy = True # otherwise once only
|
|
||||||
|
|
||||||
def untouched(self): # Default if not defined in subclass
|
|
||||||
pass
|
|
||||||
|
|
||||||
# *********** DISPLAYS: NON-TOUCH CLASSES FOR DATA DISPLAY ***********
|
|
||||||
|
|
||||||
class Label(NoTouch):
|
|
||||||
def __init__(self, tft, location, *, font, border=None, width, fgcolor=None, bgcolor=None, fontcolor=None, text=''):
|
|
||||||
super().__init__(tft, location, font, None, width, fgcolor, bgcolor, fontcolor, border)
|
|
||||||
self.height = self.font.bits_vert
|
|
||||||
self.height += 2 * self.border # Height determined by font and border
|
|
||||||
self.draw_border() # Must explicitly draw because ctor did not have height
|
|
||||||
self.show(text)
|
|
||||||
|
|
||||||
def show(self, text):
|
|
||||||
tft = self.tft
|
|
||||||
bw = self.border
|
|
||||||
if text:
|
|
||||||
x = self.location[0]
|
|
||||||
y = self.location[1]
|
|
||||||
tft.fillRectangle(x + bw, y + bw, x + self.width - bw, y + self.height - bw, self.bgcolor)
|
|
||||||
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
|
||||||
tft.setTextPos(x + bw, y + bw, clip = self.width - 2 * bw, scroll = False)
|
|
||||||
tft.printString(text)
|
|
||||||
self.restore_color() # restore fg color
|
|
||||||
|
|
||||||
# class displays angles. Angle 0 is vertical, +ve increments are clockwise.
|
|
||||||
class Dial(NoTouch):
|
|
||||||
def __init__(self, tft, location, *, height=100, fgcolor=None, bgcolor=None, border=None, pointers=(0.9,), ticks=4):
|
|
||||||
NoTouch.__init__(self, tft, location, None, height, height, fgcolor, bgcolor, None, border) # __super__ provoked Python bug
|
|
||||||
border = self.border # border width
|
|
||||||
radius = height / 2 - border
|
|
||||||
self.radius = radius
|
|
||||||
self.xorigin = location[0] + border + radius
|
|
||||||
self.yorigin = location[1] + border + radius
|
|
||||||
self.pointers = tuple(z * self.radius for z in pointers) # Pointer lengths
|
|
||||||
self.angles = [None for _ in pointers]
|
|
||||||
ticklen = 0.1 * radius
|
|
||||||
for tick in range(ticks):
|
|
||||||
theta = 2 * tick * math.pi / ticks
|
|
||||||
x_start = int(self.xorigin + radius * math.sin(theta))
|
|
||||||
y_start = int(self.yorigin - radius * math.cos(theta))
|
|
||||||
x_end = int(self.xorigin + (radius - ticklen) * math.sin(theta))
|
|
||||||
y_end = int(self.yorigin - (radius - ticklen) * math.cos(theta))
|
|
||||||
self.tft.drawLine(x_start, y_start, x_end, y_end, self.fgcolor)
|
|
||||||
tft.drawCircle(self.xorigin, self.yorigin, radius, self.fgcolor)
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def show(self, angle, pointer=0):
|
|
||||||
tft = self.tft
|
|
||||||
if self.angles[pointer] is not None:
|
|
||||||
self.drawpointer(self.angles[pointer], pointer, self.bgcolor) # erase old
|
|
||||||
self.drawpointer(angle, pointer, self.fgcolor) # draw new
|
|
||||||
self.angles[pointer] = angle # update old
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def drawpointer(self, radians, pointer, color):
|
|
||||||
length = self.pointers[pointer]
|
|
||||||
x_end = int(self.xorigin + length * math.sin(radians))
|
|
||||||
y_end = int(self.yorigin - length * math.cos(radians))
|
|
||||||
self.tft.drawLine(int(self.xorigin), int(self.yorigin), x_end, y_end, color)
|
|
||||||
|
|
||||||
class LED(NoTouch):
|
|
||||||
def __init__(self, tft, location, *, border=None, height=30, fgcolor=None, bgcolor=None, color=RED):
|
|
||||||
super().__init__(tft, location, None, height, height, fgcolor, bgcolor, None, border)
|
|
||||||
self.radius = (self.height - 2 * self.border) / 2
|
|
||||||
self.x = location[0] + self.radius + self.border
|
|
||||||
self.y = location[1] + self.radius + self.border
|
|
||||||
self.color = color
|
|
||||||
self.off()
|
|
||||||
|
|
||||||
def _show(self, color): # Light the LED
|
|
||||||
self.tft.fillCircle(int(self.x), int(self.y), int(self.radius), color)
|
|
||||||
self.tft.drawCircle(int(self.x), int(self.y), int(self.radius), self.fgcolor)
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def on(self, color=None): # Light in current color
|
|
||||||
if color is not None:
|
|
||||||
self.color = color
|
|
||||||
self._show(self.color)
|
|
||||||
|
|
||||||
def off(self):
|
|
||||||
self._show(BLACK)
|
|
||||||
|
|
||||||
class Meter(NoTouch):
|
|
||||||
def __init__(self, tft, location, *, font=None, height=200, width=30,
|
|
||||||
fgcolor=None, bgcolor=None, pointercolor=None, fontcolor=None,
|
|
||||||
divisions=10, legends=None, value=0):
|
|
||||||
border = 5 if font is None else 1 + font.bits_vert / 2
|
|
||||||
NoTouch.__init__(self, tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border) # __super__ provoked Python bug
|
|
||||||
border = self.border # border width
|
|
||||||
self.ptrbytes = 3 * (self.width + 1) # 3 bytes per pixel
|
|
||||||
self.ptrbuf = bytearray(self.ptrbytes) #???
|
|
||||||
self.x0 = self.location[0]
|
|
||||||
self.x1 = self.location[0] + self.width
|
|
||||||
self.y0 = self.location[1] + border + 2
|
|
||||||
self.y1 = self.location[1] + self.height - border
|
|
||||||
self.divisions = divisions
|
|
||||||
self.legends = legends
|
|
||||||
self.pointercolor = pointercolor if pointercolor is not None else fgcolor
|
|
||||||
self._value = value
|
|
||||||
self._old_value = -1 # invalidate
|
|
||||||
self.ptr_y = -1 # Invalidate old position
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
bw = self.draw_border() # and background if required. Result is width of border
|
|
||||||
width = self.width
|
|
||||||
dx = 5
|
|
||||||
x0 = self.x0
|
|
||||||
x1 = self.x1
|
|
||||||
y0 = self.y0
|
|
||||||
y1 = self.y1
|
|
||||||
height = y1 - y0
|
|
||||||
if self.divisions > 0:
|
|
||||||
dy = height / (self.divisions) # Tick marks
|
|
||||||
for tick in range(self.divisions + 1):
|
|
||||||
ypos = int(y0 + dy * tick)
|
|
||||||
tft.drawHLine(x0, ypos, dx, self.fgcolor)
|
|
||||||
tft.drawHLine(x1 - dx, ypos, dx, self.fgcolor)
|
|
||||||
|
|
||||||
if self.legends is not None and self.font is not None: # Legends
|
|
||||||
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
|
||||||
if len(self.legends) <= 1:
|
|
||||||
dy = 0
|
|
||||||
else:
|
|
||||||
dy = height / (len(self.legends) -1)
|
|
||||||
yl = self.y1 # Start at bottom
|
|
||||||
for legend in self.legends:
|
|
||||||
print_centered(tft, int(self.x0 + self.width /2), int(yl), legend, self.fontcolor, self.font)
|
|
||||||
yl -= dy
|
|
||||||
|
|
||||||
y0 = self.ptr_y
|
|
||||||
y1 = y0
|
|
||||||
if self.ptr_y >= 0: # Restore background
|
|
||||||
tft.setXY(x0, y0, x1, y1)
|
|
||||||
TFT_io.tft_write_data_AS(self.ptrbuf, self.ptrbytes)
|
|
||||||
ptrpos = int(self.y1 - self._value * height)
|
|
||||||
y0 = ptrpos
|
|
||||||
y1 = ptrpos
|
|
||||||
tft.setXY(x0, y0, x1, y1) # Read background
|
|
||||||
TFT_io.tft_read_cmd_data_AS(0x2e, self.ptrbuf, self.ptrbytes)
|
|
||||||
self.ptr_y = y0
|
|
||||||
tft.drawHLine(x0, y0, width, self.pointercolor) # Draw pointer
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def value(self, val=None):
|
|
||||||
if val is None:
|
|
||||||
return self._value
|
|
||||||
self._value = min(max(val, 0.0), 1.0)
|
|
||||||
if self._value != self._old_value:
|
|
||||||
self._old_value = self._value
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
# *********** PUSHBUTTON AND CHECKBOX CLASSES ***********
|
|
||||||
|
|
||||||
# Button coordinates relate to bounding box (BB). x, y are of BB top left corner.
|
|
||||||
# likewise width and height refer to BB, regardless of button shape
|
|
||||||
# If font is None button will be rendered without text
|
|
||||||
|
|
||||||
class Button(Touchable):
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, *, font, shape=CIRCLE, height=50, width=50, fill=True,
|
|
||||||
fgcolor=None, bgcolor=None, fontcolor=None, litcolor=None, text='', show=True, callback=lambda *_ : None,
|
|
||||||
args=[]):
|
|
||||||
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, None, False)
|
|
||||||
self.shape = shape
|
|
||||||
self.radius = height // 2
|
|
||||||
self.fill = fill
|
|
||||||
self.litcolor = litcolor
|
|
||||||
self.text = text
|
|
||||||
self.callback = callback
|
|
||||||
self.callback_args = args
|
|
||||||
self.orig_fgcolor = fgcolor
|
|
||||||
if self.litcolor is not None:
|
|
||||||
self.delay = Delay(objsched, self.shownormal)
|
|
||||||
self.visible = True # ditto
|
|
||||||
self.litcolor = litcolor if self.fgcolor is not None else None
|
|
||||||
if show:
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
x = self.location[0]
|
|
||||||
y = self.location[1]
|
|
||||||
if not self.visible: # erase the button
|
|
||||||
tft.fillRectangle(x, y, x + self.width, y + self.height, self.bgcolor)
|
|
||||||
self.restore_color()
|
|
||||||
return
|
|
||||||
if self.shape == CIRCLE: # Button coords are of top left corner of bounding box
|
|
||||||
x += self.radius
|
|
||||||
y += self.radius
|
|
||||||
if self.fill:
|
|
||||||
tft.fillCircle(x, y, self.radius, self.fgcolor)
|
|
||||||
else:
|
|
||||||
tft.drawCircle(x, y, self.radius, self.fgcolor)
|
|
||||||
if self.font is not None and len(self.text):
|
|
||||||
print_centered(tft, x, y, self.text, self.fontcolor, self.font)
|
|
||||||
else:
|
|
||||||
x1 = x + self.width
|
|
||||||
y1 = y + self.height
|
|
||||||
if self.shape == RECTANGLE: # rectangle
|
|
||||||
if self.fill:
|
|
||||||
tft.fillRectangle(x, y, x1, y1, self.fgcolor)
|
|
||||||
else:
|
|
||||||
tft.drawRectangle(x, y, x1, y1, self.fgcolor)
|
|
||||||
if self.font is not None and len(self.text):
|
|
||||||
print_centered(tft, (x + x1) // 2, (y + y1) // 2, self.text, self.fontcolor, self.font)
|
|
||||||
elif self.shape == CLIPPED_RECT: # clipped rectangle
|
|
||||||
if self.fill:
|
|
||||||
tft.fillClippedRectangle(x, y, x1, y1, self.fgcolor)
|
|
||||||
else:
|
|
||||||
tft.drawClippedRectangle(x, y, x1, y1, self.fgcolor)
|
|
||||||
if self.font is not None and len(self.text):
|
|
||||||
print_centered(tft, (x + x1) // 2, (y + y1) // 2, self.text, self.fontcolor, self.font)
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def shownormal(self):
|
|
||||||
self.fgcolor = self.orig_fgcolor
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def touched(self, x, y): # Process touch
|
|
||||||
if self.litcolor is not None:
|
|
||||||
self.fgcolor = self.litcolor
|
|
||||||
self.show()
|
|
||||||
self.delay.trigger(1)
|
|
||||||
self.callback(self, *self.callback_args) # Callback not a bound method so pass self
|
|
||||||
|
|
||||||
class Buttons(object):
|
|
||||||
def __init__(self, user_callback):
|
|
||||||
self.user_callback = user_callback
|
|
||||||
self.lstbuttons = []
|
|
||||||
|
|
||||||
def add_button(self, *args, **kwargs):
|
|
||||||
kwargs['show'] = False
|
|
||||||
self.lstbuttons.append(Button(*args, **kwargs))
|
|
||||||
|
|
||||||
# Group of buttons, typically at same location, where pressing one shows
|
|
||||||
# the next e.g. start/stop toggle or sequential select from short list
|
|
||||||
class Buttonset(Buttons):
|
|
||||||
def __init__(self, user_callback):
|
|
||||||
super().__init__(user_callback)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
for idx, button in enumerate(self.lstbuttons):
|
|
||||||
if idx:
|
|
||||||
button.visible = False # Only button zero visible and sensitive
|
|
||||||
button.enabled = False
|
|
||||||
button.callback_args.append(idx)
|
|
||||||
button.callback = self.callback
|
|
||||||
self.lstbuttons[0].show()
|
|
||||||
|
|
||||||
def callback(self, button, *args):
|
|
||||||
button_no = args[-1]
|
|
||||||
old = self.lstbuttons[button_no]
|
|
||||||
new = self.lstbuttons[(button_no + 1) % len(self.lstbuttons)]
|
|
||||||
old.enabled = False
|
|
||||||
old.visible = False
|
|
||||||
old.show()
|
|
||||||
new.enabled = True
|
|
||||||
new.visible = True
|
|
||||||
new.busy = True # Don't respond to continued press
|
|
||||||
new.show()
|
|
||||||
self.user_callback(new, *args[:-1]) # user gets button with args they specified
|
|
||||||
|
|
||||||
# Group of buttons at different locations, where pressing one shows
|
|
||||||
# only current button highlighted and oes callback from current one
|
|
||||||
class RadioButtons(Buttons):
|
|
||||||
def __init__(self, user_callback, highlight, selected=0):
|
|
||||||
super().__init__(user_callback)
|
|
||||||
self.highlight = highlight
|
|
||||||
self.selected = selected
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
for idx, button in enumerate(self.lstbuttons):
|
|
||||||
if idx == self.selected: # Initial selection
|
|
||||||
button.fgcolor = self.highlight
|
|
||||||
else:
|
|
||||||
button.fgcolor = button.orig_fgcolor
|
|
||||||
button.show()
|
|
||||||
button.callback = self.callback
|
|
||||||
|
|
||||||
def callback(self, button, *args):
|
|
||||||
for but in self.lstbuttons:
|
|
||||||
if but is button:
|
|
||||||
but.fgcolor = self.highlight
|
|
||||||
else:
|
|
||||||
but.fgcolor = but.orig_fgcolor
|
|
||||||
but.show()
|
|
||||||
self.user_callback(button, *args) # user gets button with args they specified
|
|
||||||
|
|
||||||
|
|
||||||
class Checkbox(Touchable):
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, *, height=30, fillcolor=None,
|
|
||||||
fgcolor=None, bgcolor=None, callback=lambda x, y : None, args=[], value=False, border=None):
|
|
||||||
super().__init__(objsched, tft, objtouch, location, None, height, height, fgcolor, bgcolor, None, border, False)
|
|
||||||
self.callback = callback
|
|
||||||
self.callback_args = args
|
|
||||||
self.fillcolor = fillcolor
|
|
||||||
if value is None:
|
|
||||||
self._value = False # special case: don't execute callback on initialisation
|
|
||||||
self.show()
|
|
||||||
else:
|
|
||||||
self._value = not value
|
|
||||||
self.value(value)
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
bw = self.draw_border() # and background if required. Result is width of border
|
|
||||||
x = self.location[0] + bw
|
|
||||||
y = self.location[1] + bw
|
|
||||||
height = self.height - 2 * bw
|
|
||||||
x1 = x + height
|
|
||||||
y1 = y + height
|
|
||||||
if self._value:
|
|
||||||
if self.fillcolor is not None:
|
|
||||||
tft.fillRectangle(x, y, x1, y1, self.fillcolor)
|
|
||||||
else:
|
|
||||||
tft.fillRectangle(x, y, x1, y1, self.bgcolor)
|
|
||||||
tft.drawRectangle(x, y, x1, y1, self.fgcolor)
|
|
||||||
if self.fillcolor is None and self._value:
|
|
||||||
tft.drawLine(x, y, x1, y1, self.fgcolor)
|
|
||||||
tft.drawLine(x, y1, x1, y, self.fgcolor)
|
|
||||||
|
|
||||||
def value(self, val=None):
|
|
||||||
if val is None:
|
|
||||||
return self._value
|
|
||||||
val = bool(val)
|
|
||||||
if val != self._value:
|
|
||||||
self._value = val
|
|
||||||
self.callback(self, *self.callback_args) # Callback not a bound method so pass self
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def touched(self, x, y): # Was touched
|
|
||||||
self.value(not self._value) # Upddate and refresh
|
|
||||||
|
|
||||||
# *********** SLIDER CLASSES ***********
|
|
||||||
# A slider's text items lie outside its bounding box (area sensitive to touch)
|
|
||||||
|
|
||||||
class Slider(Touchable):
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, font, *, height=200, width=30, divisions=10, legends=None,
|
|
||||||
fgcolor=None, bgcolor=None, fontcolor=None, slidecolor=None, border=None,
|
|
||||||
cb_end=lambda *_ : None, cbe_args=[], cb_move=lambda *_ : None, cbm_args=[], value=0.0):
|
|
||||||
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border, True)
|
|
||||||
self.divisions = divisions
|
|
||||||
self.legends = legends
|
|
||||||
self.slidecolor = slidecolor
|
|
||||||
self.cb_end = cb_end
|
|
||||||
self.cbe_args = cbe_args
|
|
||||||
self.cb_move = cb_move
|
|
||||||
self.cbm_args = cbm_args
|
|
||||||
slidewidth = int(width / 1.3) & 0xfe # Ensure divisible by 2
|
|
||||||
self.slideheight = 6 # must be divisible by 2
|
|
||||||
# We draw an odd number of pixels:
|
|
||||||
self.slidebytes = (self.slideheight + 1) * (slidewidth + 1) * 3
|
|
||||||
self.slidebuf = bytearray(self.slidebytes)
|
|
||||||
self._old_value = -1 # Invalidate
|
|
||||||
b = self.border
|
|
||||||
self.pot_dimension = self.height - 2 * (b + self.slideheight // 2)
|
|
||||||
width = self.width - 2 * b
|
|
||||||
xcentre = self.location[0] + b + width // 2
|
|
||||||
self.slide_x0 = xcentre - slidewidth // 2
|
|
||||||
self.slide_x1 = xcentre + slidewidth // 2 # slide X coordinates
|
|
||||||
self.slide_y = -1 # Invalidate old position
|
|
||||||
self.value(value)
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
bw = self.draw_border() # and background if required. Result is width of border
|
|
||||||
x = self.location[0] + bw
|
|
||||||
y = self.location[1] + bw + self.slideheight // 2 # Allow space above and below slot
|
|
||||||
width = self.width - 2 * bw
|
|
||||||
height = self.pot_dimension # Height of slot
|
|
||||||
dx = width / 3
|
|
||||||
tft.drawRectangle(x + dx, y, x + 2 * dx, y + height, self.fgcolor)
|
|
||||||
|
|
||||||
if self.divisions > 0:
|
|
||||||
dy = height / (self.divisions) # Tick marks
|
|
||||||
for tick in range(self.divisions + 1):
|
|
||||||
ypos = int(y + dy * tick)
|
|
||||||
tft.drawHLine(x + 1, ypos, dx, self.fgcolor)
|
|
||||||
tft.drawHLine(x + 1 + 2 * dx, ypos, dx, self.fgcolor)
|
|
||||||
|
|
||||||
if self.legends is not None: # Legends
|
|
||||||
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
|
||||||
if len(self.legends) <= 1:
|
|
||||||
dy = 0
|
|
||||||
else:
|
|
||||||
dy = height / (len(self.legends) -1)
|
|
||||||
yl = y + height # Start at bottom
|
|
||||||
fhdelta = self.font.bits_vert / 2
|
|
||||||
for legend in self.legends:
|
|
||||||
tft.setTextPos(x + self.width, int(yl - fhdelta))
|
|
||||||
tft.printString(legend)
|
|
||||||
yl -= dy
|
|
||||||
|
|
||||||
sh = self.slideheight # Handle slider
|
|
||||||
x0 = self.slide_x0
|
|
||||||
y0 = self.slide_y
|
|
||||||
x1 = self.slide_x1
|
|
||||||
y1 = y0 + sh
|
|
||||||
if self.slide_y >= 0: # Restore background
|
|
||||||
tft.setXY(x0, y0, x1, y1)
|
|
||||||
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
|
|
||||||
sliderpos = int(y + height - self._value * height)
|
|
||||||
y0 = sliderpos - sh // 2
|
|
||||||
y1 = sliderpos + sh // 2
|
|
||||||
tft.setXY(x0, y0, x1, y1) # Read background
|
|
||||||
TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
|
|
||||||
self.slide_y = y0
|
|
||||||
color = self.slidecolor if self.slidecolor is not None else self.fgcolor
|
|
||||||
tft.fillRectangle(x0, y0, x1, y1, color) # Draw slider
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def value(self, val=None):
|
|
||||||
if val is None:
|
|
||||||
return self._value
|
|
||||||
self._value = min(max(val, 0.0), 1.0)
|
|
||||||
if self._value != self._old_value:
|
|
||||||
self._old_value = self._value
|
|
||||||
self.cb_move(self, *self.cbm_args) # Callback not a bound method so pass self
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def touched(self, x, y): # Touched in bounding box. A drag will call repeatedly.
|
|
||||||
self.value((self.location[1] + self.height - y) / self.pot_dimension)
|
|
||||||
|
|
||||||
def untouched(self): # User has released touchpad or touched elsewhere
|
|
||||||
self.cb_end(self, *self.cbe_args) # Callback not a bound method so pass self
|
|
||||||
|
|
||||||
class HorizSlider(Touchable):
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, font, *, height=30, width=200, divisions=10, legends=None,
|
|
||||||
fgcolor=None, bgcolor=None, fontcolor=None, slidecolor=None, border=None,
|
|
||||||
cb_end=lambda *_ : None, cbe_args=[], cb_move=lambda *_ : None, cbm_args=[], value=0.0):
|
|
||||||
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border, True)
|
|
||||||
self.divisions = divisions
|
|
||||||
self.legends = legends
|
|
||||||
self.slidecolor = slidecolor
|
|
||||||
self.cb_end = cb_end
|
|
||||||
self.cbe_args = cbe_args
|
|
||||||
self.cb_move = cb_move
|
|
||||||
self.cbm_args = cbm_args
|
|
||||||
slideheight = int(height / 1.3) & 0xfe # Ensure divisible by 2
|
|
||||||
self.slidewidth = 6 # must be divisible by 2
|
|
||||||
# We draw an odd number of pixels:
|
|
||||||
self.slidebytes = (slideheight + 1) * (self.slidewidth + 1) * 3
|
|
||||||
self.slidebuf = bytearray(self.slidebytes)
|
|
||||||
self._old_value = -1 # Invalidate
|
|
||||||
b = self.border
|
|
||||||
self.pot_dimension = self.width - 2 * (b + self.slidewidth // 2)
|
|
||||||
height = self.height - 2 * b
|
|
||||||
ycentre = self.location[1] + b + height // 2
|
|
||||||
self.slide_y0 = ycentre - slideheight // 2
|
|
||||||
self.slide_y1 = ycentre + slideheight // 2 # slide Y coordinates
|
|
||||||
self.slide_x = -1 # Invalidate old position
|
|
||||||
self.value(value)
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
bw = self.draw_border() # and background if required. Result is width of border
|
|
||||||
x = self.location[0] + bw + self.slidewidth // 2 # Allow space left and right slot for slider at extremes
|
|
||||||
y = self.location[1] + bw
|
|
||||||
height = self.height - 2 * bw
|
|
||||||
width = self.pot_dimension # Length of slot
|
|
||||||
dy = height / 3
|
|
||||||
ycentre = y + height // 2
|
|
||||||
tft.drawRectangle(x, y + dy, x + width, y + 2 * dy, self.fgcolor)
|
|
||||||
|
|
||||||
if self.divisions > 0:
|
|
||||||
dx = width / (self.divisions) # Tick marks
|
|
||||||
for tick in range(self.divisions + 1):
|
|
||||||
xpos = int(x + dx * tick)
|
|
||||||
tft.drawVLine(xpos, y + 1, dy, self.fgcolor) # TODO Why is +1 fiddle required here?
|
|
||||||
tft.drawVLine(xpos, y + 1 + 2 * dy, dy, self.fgcolor) # and here
|
|
||||||
|
|
||||||
if self.legends is not None: # Legends
|
|
||||||
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
|
||||||
if len(self.legends) <= 1:
|
|
||||||
dx = 0
|
|
||||||
else:
|
|
||||||
dx = width / (len(self.legends) -1)
|
|
||||||
xl = x
|
|
||||||
for legend in self.legends:
|
|
||||||
offset = get_stringsize(legend, self.font)[0] / 2
|
|
||||||
tft.setTextPos(int(xl - offset), y - self.font.bits_vert) # Arbitrary left shift should be char width /2
|
|
||||||
tft.printString(legend)
|
|
||||||
xl += dx
|
|
||||||
|
|
||||||
sw = self.slidewidth # Handle slider
|
|
||||||
x0 = self.slide_x
|
|
||||||
y0 = self.slide_y0
|
|
||||||
x1 = x0 + sw
|
|
||||||
y1 = self.slide_y1
|
|
||||||
if self.slide_x >= 0: # Restore background
|
|
||||||
tft.setXY(x0, y0, x1, y1)
|
|
||||||
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
|
|
||||||
sliderpos = int(x + self._value * width)
|
|
||||||
x0 = sliderpos - sw // 2
|
|
||||||
x1 = sliderpos + sw // 2
|
|
||||||
tft.setXY(x0, y0, x1, y1) # Read background
|
|
||||||
TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
|
|
||||||
self.slide_x = x0
|
|
||||||
color = self.slidecolor if self.slidecolor is not None else self.fgcolor
|
|
||||||
tft.fillRectangle(x0, y0, x1, y1, color) # Draw slider
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def value(self, val=None):
|
|
||||||
if val is None:
|
|
||||||
return self._value
|
|
||||||
self._value = min(max(val, 0.0), 1.0)
|
|
||||||
if self._value != self._old_value:
|
|
||||||
self._old_value = self._value
|
|
||||||
self.cb_move(self, *self.cbm_args) # Callback not a bound method so pass self
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def touched(self, x, y): # Touched in bounding box. A drag will call repeatedly.
|
|
||||||
self.value((x - self.location[0]) / self.pot_dimension)
|
|
||||||
|
|
||||||
def untouched(self): # User has released touchpad or touched elsewhere
|
|
||||||
self.cb_end(self, *self.cbe_args) # Callback not a bound method so pass self
|
|
||||||
|
|
||||||
# *********** CONTROL KNOB CLASS ***********
|
|
||||||
|
|
||||||
class Knob(Touchable):
|
|
||||||
def __init__(self, objsched, tft, objtouch, location, *, height=100, arc=TWOPI, ticks=9, value=0.0,
|
|
||||||
fgcolor=None, bgcolor=None, color=None, border=None,
|
|
||||||
cb_end=lambda *_ : None, cbe_args=[], cb_move=lambda *_ : None, cbm_args=[]):
|
|
||||||
Touchable.__init__(self, objsched, tft, objtouch, location, None, height, height, fgcolor, bgcolor, None, border, True)
|
|
||||||
border = self.border # Geometry: border width
|
|
||||||
radius = height / 2 - border
|
|
||||||
arc = min(max(arc, 0), TWOPI)
|
|
||||||
self.arc = arc # Usable angle of control
|
|
||||||
self.radius = radius
|
|
||||||
self.xorigin = location[0] + border + radius
|
|
||||||
self.yorigin = location[1] + border + radius
|
|
||||||
ticklen = 0.1 * radius
|
|
||||||
self.pointerlen = radius - ticklen - 5
|
|
||||||
ticks = max(ticks, 2) # start and end of travel
|
|
||||||
|
|
||||||
self.cb_end = cb_end # Callbacks
|
|
||||||
self.cbe_args = cbe_args
|
|
||||||
self.cb_move = cb_move
|
|
||||||
self.cbm_args = cbm_args
|
|
||||||
|
|
||||||
self._old_value = None # data: invalidate
|
|
||||||
self._value = -1
|
|
||||||
|
|
||||||
self.color = color
|
|
||||||
for tick in range(ticks):
|
|
||||||
theta = (tick / (ticks - 1)) * arc - arc / 2
|
|
||||||
x_start = int(self.xorigin + radius * math.sin(theta))
|
|
||||||
y_start = int(self.yorigin - radius * math.cos(theta))
|
|
||||||
x_end = int(self.xorigin + (radius - ticklen) * math.sin(theta))
|
|
||||||
y_end = int(self.yorigin - (radius - ticklen) * math.cos(theta))
|
|
||||||
self.tft.drawLine(x_start, y_start, x_end, y_end, self.fgcolor)
|
|
||||||
if color is not None:
|
|
||||||
tft.fillCircle(self.xorigin, self.yorigin, radius - ticklen, color)
|
|
||||||
tft.drawCircle(self.xorigin, self.yorigin, radius - ticklen, self.fgcolor)
|
|
||||||
tft.drawCircle(self.xorigin, self.yorigin, radius - ticklen - 3, self.fgcolor)
|
|
||||||
self.value(value) # Cause the object to be displayed and callback to be triggered
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
tft = self.tft
|
|
||||||
if self._old_value is not None:
|
|
||||||
color = self.bgcolor if self.color is None else self.color
|
|
||||||
self.drawpointer(self._old_value, color) # erase old
|
|
||||||
self.drawpointer(self._value, self.fgcolor) # draw new
|
|
||||||
self._old_value = self._value # update old
|
|
||||||
self.restore_color()
|
|
||||||
|
|
||||||
def value(self, val=None):
|
|
||||||
if val is None:
|
|
||||||
return self._value
|
|
||||||
val = min(max(val, 0.0), 1.0)
|
|
||||||
if val != self._value:
|
|
||||||
self._value = val # Update value for callback
|
|
||||||
self.cb_move(self, *self.cbm_args) # Callback not a bound method so pass self
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def touched(self, x, y): # Touched in bounding box. A drag will call repeatedly.
|
|
||||||
dy = self.yorigin - y
|
|
||||||
dx = x - self.xorigin
|
|
||||||
if (dx**2 + dy**2) / self.radius**2 < 0.5:
|
|
||||||
return # vector too short
|
|
||||||
alpha = math.atan2(dx, dy) # axes swapped: orientate relative to vertical
|
|
||||||
arc = self.arc
|
|
||||||
alpha = min(max(alpha, -arc / 2), arc / 2) + arc / 2
|
|
||||||
self.value(alpha /arc)
|
|
||||||
|
|
||||||
def untouched(self): # User has released touchpad or touched elsewhere
|
|
||||||
self.cb_end(self, *self.cbe_args) # Callback not a bound method so pass self
|
|
||||||
|
|
||||||
def drawpointer(self, value, color):
|
|
||||||
arc = self.arc
|
|
||||||
length = self.pointerlen
|
|
||||||
angle = value * arc - arc / 2
|
|
||||||
x_end = int(self.xorigin + length * math.sin(angle))
|
|
||||||
y_end = int(self.yorigin - length * math.cos(angle))
|
|
||||||
self.tft.drawLine(int(self.xorigin), int(self.yorigin), x_end, y_end, color)
|
|
|
@ -1,255 +0,0 @@
|
||||||
# ledflash, pause, instrument, roundrobin work
|
|
||||||
|
|
||||||
# Lightweight threading library for the micropython board.
|
|
||||||
# Author: Peter Hinch
|
|
||||||
# V1.03 Implements gc
|
|
||||||
# Copyright Peter Hinch 2016 Released under the MIT license
|
|
||||||
|
|
||||||
import pyb, micropython, gc
|
|
||||||
micropython.alloc_emergency_exception_buf(100)
|
|
||||||
|
|
||||||
# TIMER ACCESS
|
|
||||||
|
|
||||||
TIMERPERIOD = 0x7fffffff # 35.79 minutes 2148 secs
|
|
||||||
MAXTIME = TIMERPERIOD//2 # 1073 seconds maximum timeout
|
|
||||||
MAXSECS = MAXTIME//1000000
|
|
||||||
|
|
||||||
class TimerException(Exception) : pass
|
|
||||||
|
|
||||||
def microsWhen(timediff): # Expected value of counter in a given no. of uS
|
|
||||||
if timediff >= MAXTIME:
|
|
||||||
raise TimerException()
|
|
||||||
return (pyb.micros() + timediff) & TIMERPERIOD
|
|
||||||
|
|
||||||
def microsSince(oldtime): # No of uS since timer held this value
|
|
||||||
return (pyb.micros() - oldtime) & TIMERPERIOD
|
|
||||||
|
|
||||||
def after(trigtime): # If current time is after the specified value return
|
|
||||||
res = ((pyb.micros() - trigtime) & TIMERPERIOD) # the no. of uS after. Otherwise return zero
|
|
||||||
if res >= MAXTIME:
|
|
||||||
res = 0
|
|
||||||
return res
|
|
||||||
|
|
||||||
def microsUntil(tim): # uS from now until a specified time (used in Delay class)
|
|
||||||
return ((tim - pyb.micros()) & TIMERPERIOD)
|
|
||||||
|
|
||||||
def seconds(S): # Utility functions to convert to integer microseconds
|
|
||||||
return int(1000000*S)
|
|
||||||
|
|
||||||
def millisecs(mS):
|
|
||||||
return int(1000*mS)
|
|
||||||
|
|
||||||
# WAITFOR CLASS
|
|
||||||
# This is a base class. User threads should use classes derived from this.
|
|
||||||
|
|
||||||
class Waitfor(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.uS = 0 # Current value of timeout in uS
|
|
||||||
self.timeout = microsWhen(0) # End value of microsecond counter when TO has elapsed
|
|
||||||
self.forever = False # "infinite" time delay flag
|
|
||||||
self.irq = None # Interrupt vector no
|
|
||||||
self.pollfunc = None # Function to be called if we're polling
|
|
||||||
self.pollfunc_args = () # Arguments for the above
|
|
||||||
self.customcallback = None # Optional custom interrupt handler
|
|
||||||
self.interruptcount = 0 # Set by handler, tested by triggered()
|
|
||||||
self.roundrobin = False # If true reschedule ASAP
|
|
||||||
|
|
||||||
def triggered(self): # Polled by scheduler. Returns a priority tuple or None if not ready
|
|
||||||
if self.irq: # Waiting on an interrupt
|
|
||||||
self.irq.disable() # Potential concurrency issue here (????)
|
|
||||||
numints = self.interruptcount # Number of missed interrupts
|
|
||||||
if numints: # Waiting on an interrupt and it's occurred
|
|
||||||
self.interruptcount = 0 # Clear down the counter
|
|
||||||
self.irq.enable()
|
|
||||||
if numints:
|
|
||||||
return (numints, 0, 0)
|
|
||||||
if self.pollfunc: # Optional function for the scheduler to poll
|
|
||||||
res = self.pollfunc(*self.pollfunc_args) # something other than an interrupt
|
|
||||||
if res is not None:
|
|
||||||
return (0, res, 0)
|
|
||||||
if not self.forever: # Check for timeout
|
|
||||||
if self.roundrobin:
|
|
||||||
return (0,0,0) # Priority value of round robin thread
|
|
||||||
res = after(self.timeout) # uS after, or zero if not yet timed out in which case we return None
|
|
||||||
if res: # Note: can never return (0,0,0) here!
|
|
||||||
return (0, 0, res) # Nonzero means it's timed out
|
|
||||||
return None # Not ready for execution
|
|
||||||
|
|
||||||
def _ussetdelay(self, uS=None): # Reset the timer by default to its last value
|
|
||||||
if uS: # If a value was passed, update it
|
|
||||||
self.uS = uS
|
|
||||||
self.timeout = microsWhen(self.uS) # Target timer value
|
|
||||||
return self
|
|
||||||
|
|
||||||
def setdelay(self, secs=None): # Method used by derived classes to alter timer values
|
|
||||||
if secs is None: # Set to infinity
|
|
||||||
self.forever = True
|
|
||||||
return self
|
|
||||||
else: # Update saved delay and calculate a new end time
|
|
||||||
if secs <= 0 or secs > MAXSECS:
|
|
||||||
raise ValueError('Invalid time delay')
|
|
||||||
self.forever = False
|
|
||||||
return self._ussetdelay(seconds(secs))
|
|
||||||
|
|
||||||
def __call__(self): # Convenience function allows user to yield an updated
|
|
||||||
if self.uS: # waitfor object
|
|
||||||
return self._ussetdelay()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def intcallback(self, irqno): # Runs in interrupt's context.
|
|
||||||
if self.customcallback:
|
|
||||||
self.customcallback(irqno)
|
|
||||||
self.interruptcount += 1 # Increments count to enable trigger to operate
|
|
||||||
|
|
||||||
class Roundrobin(Waitfor): # Compatibility only. A thread yielding a Roundrobin
|
|
||||||
def __init__(self): # will be rescheduled as soon as priority threads have been serviced
|
|
||||||
super().__init__()
|
|
||||||
self.roundrobin = True
|
|
||||||
|
|
||||||
# Intended for device drivers
|
|
||||||
class Timeout(Waitfor):
|
|
||||||
def __init__(self, tim):
|
|
||||||
super().__init__()
|
|
||||||
self.setdelay(tim)
|
|
||||||
|
|
||||||
# yield from wait
|
|
||||||
def wait(secs):
|
|
||||||
if secs <=0 :
|
|
||||||
raise TimerException()
|
|
||||||
count, tstart = divmod(secs, MAXSECS)
|
|
||||||
overshoot = 0
|
|
||||||
if tstart > 0:
|
|
||||||
res = yield Timeout(tstart)
|
|
||||||
overshoot = res[2]
|
|
||||||
while count:
|
|
||||||
res = yield Timeout(MAXSECS)
|
|
||||||
overshoot += res[2]
|
|
||||||
count -= 1
|
|
||||||
return (0, 0, overshoot)
|
|
||||||
|
|
||||||
# Block on an interrupt from a pin subject to optional timeout
|
|
||||||
class Pinblock(Waitfor):
|
|
||||||
def __init__(self, pin, mode, pull, customcallback = None, timeout = None):
|
|
||||||
super().__init__()
|
|
||||||
self.customcallback = customcallback
|
|
||||||
if timeout is None:
|
|
||||||
self.forever = True
|
|
||||||
else:
|
|
||||||
self.setdelay(timeout)
|
|
||||||
self.irq = pyb.ExtInt(pin, mode, pull, self.intcallback)
|
|
||||||
|
|
||||||
class Poller(Waitfor):
|
|
||||||
def __init__(self, pollfunc, pollfunc_args = (), timeout = None):
|
|
||||||
super().__init__()
|
|
||||||
self.pollfunc = pollfunc
|
|
||||||
self.pollfunc_args = pollfunc_args
|
|
||||||
if timeout is None:
|
|
||||||
self.forever = True
|
|
||||||
else:
|
|
||||||
self.setdelay(timeout)
|
|
||||||
|
|
||||||
# SCHEDULER CLASS
|
|
||||||
|
|
||||||
class Sched(object):
|
|
||||||
GCTIME = const(50000)
|
|
||||||
DEAD = const(0)
|
|
||||||
RUNNING = const(1)
|
|
||||||
PAUSED = const(2)
|
|
||||||
YIELDED = const(0)
|
|
||||||
FUNC = const(1)
|
|
||||||
PID = const(2)
|
|
||||||
STATE = const(3)
|
|
||||||
DUE = const(4)
|
|
||||||
def __init__(self, gc_enable = True):
|
|
||||||
self.lstThread = [] # Entries contain [Waitfor object, function, pid, state]
|
|
||||||
self.bStop = False
|
|
||||||
self.last_gc = 0
|
|
||||||
self.pid = 0
|
|
||||||
self.gc_enable = gc_enable
|
|
||||||
|
|
||||||
def __getitem__(self, pid): # Index by pid
|
|
||||||
threads = [thread for thread in self.lstThread if thread[PID] == pid]
|
|
||||||
if len(threads) == 1:
|
|
||||||
return threads[0]
|
|
||||||
elif len(threads) == 0:
|
|
||||||
raise ValueError('Unknown thread ID {}'.format(pid))
|
|
||||||
else:
|
|
||||||
raise OSError('Scheduler fault: duplicate thread {}'.format(pid))
|
|
||||||
|
|
||||||
def stop(self, pid=0):
|
|
||||||
if pid == 0:
|
|
||||||
self.bStop = True # Kill _runthreads method
|
|
||||||
return
|
|
||||||
self[pid][STATE] = DEAD
|
|
||||||
|
|
||||||
def pause(self, pid):
|
|
||||||
self[pid][STATE] = PAUSED
|
|
||||||
|
|
||||||
def resume(self, pid):
|
|
||||||
self[pid][STATE] = RUNNING
|
|
||||||
|
|
||||||
def add_thread(self, func): # Thread list contains [Waitfor object, generator, pid, state]
|
|
||||||
self.pid += 1 # Run thread to first yield to acquire a Waitfor instance
|
|
||||||
self.lstThread.append([func.send(None), func, self.pid, RUNNING, True]) # and put the resultant thread onto the threadlist
|
|
||||||
return self.pid
|
|
||||||
|
|
||||||
def _idle_thread(self): # Runs once then in roundrobin or when there's nothing else to do
|
|
||||||
if self.gc_enable and (self.last_gc == 0 or microsSince(self.last_gc) > GCTIME):
|
|
||||||
gc.collect()
|
|
||||||
self.last_gc = pyb.micros()
|
|
||||||
|
|
||||||
def triggered(self, thread):
|
|
||||||
wf = thread[YIELDED]
|
|
||||||
if wf is None:
|
|
||||||
return (0, 0, 0) # Roundrobin
|
|
||||||
if isinstance(wf, Waitfor):
|
|
||||||
return wf.triggered()
|
|
||||||
try:
|
|
||||||
tim = float(wf)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError('Thread yielded an invalid object')
|
|
||||||
waitfor = Timeout(tim)
|
|
||||||
thread[YIELDED] = waitfor
|
|
||||||
return waitfor.triggered()
|
|
||||||
|
|
||||||
def _runthread(self, thread, priority):
|
|
||||||
try: # Run thread, send (interrupt count, poll func value, uS overdue)
|
|
||||||
thread[YIELDED] = thread[FUNC].send(priority) # Store object yielded by thread
|
|
||||||
except StopIteration: # The thread has terminated:
|
|
||||||
thread[STATE] = DEAD # Flag thread for removal
|
|
||||||
|
|
||||||
def _get_thread(self):
|
|
||||||
p_run = None # priority tuple of thread to run
|
|
||||||
thr_run = None # thread to run
|
|
||||||
candidates = [t for t in self.lstThread if t[STATE] == RUNNING]
|
|
||||||
for thread in candidates:
|
|
||||||
priority = self.triggered(thread)
|
|
||||||
if priority is not None: # Ignore threads waiting on time or event
|
|
||||||
if priority == (0,0,0): # Roundrobin (RR)
|
|
||||||
if thr_run is None and thread[DUE]:
|
|
||||||
p_run = priority # Assign one, don't care which
|
|
||||||
thr_run = thread
|
|
||||||
else:
|
|
||||||
if p_run is None or priority > p_run:
|
|
||||||
p_run = priority
|
|
||||||
thr_run = thread
|
|
||||||
return thr_run, p_run
|
|
||||||
|
|
||||||
|
|
||||||
def _runthreads(self):
|
|
||||||
while not self.bStop:
|
|
||||||
thr_run, p_run = self._get_thread()
|
|
||||||
if thr_run is None: # All RR's have run, anything else is waiting
|
|
||||||
return
|
|
||||||
self._runthread(thr_run, p_run)
|
|
||||||
thr_run[DUE] = False # Only care if RR
|
|
||||||
|
|
||||||
def run(self): # Returns if the stop method is used or all threads terminate
|
|
||||||
while not self.bStop:
|
|
||||||
self.lstThread = [thread for thread in self.lstThread if thread[STATE] != DEAD] # Remove dead threads
|
|
||||||
self._idle_thread() # Garbage collect
|
|
||||||
if len(self.lstThread) == 0:
|
|
||||||
return
|
|
||||||
for thread in self.lstThread:
|
|
||||||
thread[DUE] = True # Applies only to roundrobin
|
|
||||||
self._runthreads() # Returns when all RR threads have run once
|
|
106
tft_gui/vst.py
106
tft_gui/vst.py
|
@ -1,106 +0,0 @@
|
||||||
# vst.py Demo/test program for vertical slider class for Pyboard TFT GUI
|
|
||||||
|
|
||||||
# The MIT License (MIT)
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 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 font10 import font10
|
|
||||||
from tft import TFT, LANDSCAPE
|
|
||||||
from usched import Sched
|
|
||||||
from touch import TOUCH
|
|
||||||
from ugui import Slider, Button, Dial, Label, CLIPPED_RECT, WHITE, BLACK, RED, GREEN, BLUE, YELLOW, GREY
|
|
||||||
from math import pi
|
|
||||||
|
|
||||||
# CALLBACKS
|
|
||||||
# cb_end occurs when user stops touching the control
|
|
||||||
def callback(slider, device):
|
|
||||||
print('{} returned {}'.format(device, slider.value()))
|
|
||||||
|
|
||||||
def to_string(val):
|
|
||||||
return '{:3.1f} ohms'.format(val * 10)
|
|
||||||
|
|
||||||
def master_moved(slider, slave1, slave2, label):
|
|
||||||
val = slider.value()
|
|
||||||
slave1.value(val)
|
|
||||||
slave2.value(val)
|
|
||||||
label.show(to_string(val))
|
|
||||||
|
|
||||||
# Either slave has had its slider moved (by user or by having value altered)
|
|
||||||
def slave_moved(slider, label):
|
|
||||||
val = slider.value()
|
|
||||||
label.show(to_string(val))
|
|
||||||
|
|
||||||
def doquit(button):
|
|
||||||
button.objsched.stop()
|
|
||||||
|
|
||||||
# THREADS
|
|
||||||
def mainthread(slider, dial):
|
|
||||||
angle = 0
|
|
||||||
yield
|
|
||||||
while True:
|
|
||||||
yield 0.1
|
|
||||||
delta = slider.value()
|
|
||||||
angle += pi * 2 * delta / 10
|
|
||||||
dial.show(angle)
|
|
||||||
dial.show(angle /10, 1)
|
|
||||||
|
|
||||||
# DATA
|
|
||||||
# Common args for the labels
|
|
||||||
labels = { 'width' : 70,
|
|
||||||
'fontcolor' : WHITE,
|
|
||||||
'border' : 2,
|
|
||||||
'fgcolor' : RED,
|
|
||||||
'bgcolor' : (0, 40, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
# '0', '1','2','3','4','5','6','7','8','9','10'
|
|
||||||
# Common arguments for all three sliders
|
|
||||||
table = {'fontcolor' : WHITE,
|
|
||||||
'legends' : ('0', '5', '10'),
|
|
||||||
'cb_end' : callback,
|
|
||||||
}
|
|
||||||
# 'border' : 2,
|
|
||||||
|
|
||||||
def test():
|
|
||||||
print('Test TFT panel...')
|
|
||||||
objsched = Sched() # Instantiate the scheduler
|
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
|
||||||
mytouch = TOUCH("XPT2046", objsched, confidence=50, margin = 50)
|
|
||||||
mytft.backlight(100) # light on
|
|
||||||
Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = RED,
|
|
||||||
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
|
||||||
dial1 = Dial(mytft, (350, 10), fgcolor = YELLOW, border = 2, pointers = (0.9, 0.7))
|
|
||||||
dial2 = Dial(mytft, (350, 120), fgcolor = YELLOW, bgcolor = GREY, border = 2, pointers = (0.9, 0.7))
|
|
||||||
lstlbl = []
|
|
||||||
for n in range(3):
|
|
||||||
lstlbl.append(Label(mytft, (80 * n, 240), font = font10, **labels))
|
|
||||||
y = 5
|
|
||||||
slave1 = Slider(objsched, mytft, mytouch, (80, y), font10,
|
|
||||||
fgcolor = GREEN, cbe_args = ('Slave1',), cb_move = slave_moved, cbm_args = [lstlbl[1]], **table)
|
|
||||||
slave2 = Slider(objsched, mytft, mytouch, (160, y), font10,
|
|
||||||
fgcolor = GREEN, cbe_args = ('Slave2',), cb_move = slave_moved, cbm_args = [lstlbl[2]], **table)
|
|
||||||
master = Slider(objsched, mytft, mytouch, (0, y), font10,
|
|
||||||
fgcolor = YELLOW, cbe_args = ('Master',), cb_move = master_moved, cbm_args = (slave1, slave2, lstlbl[0]), value=0.5, **table)
|
|
||||||
objsched.add_thread(mainthread(slave1, dial1))
|
|
||||||
objsched.add_thread(mainthread(slave2, dial2))
|
|
||||||
objsched.run() # Run it!
|
|
||||||
|
|
||||||
test()
|
|
Ładowanie…
Reference in New Issue