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