tft_gui demos added

pull/7/head
Peter Hinch 2016-04-22 16:05:30 +01:00
rodzic 9f4870a9ce
commit 702485d288
12 zmienionych plików z 2617 dodań i 0 usunięć

602
tft_gui/TFT_io.py 100644
Wyświetl plik

@ -0,0 +1,602 @@
#
# 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 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 just for writes is X1..X8 for data, Y9 for /Reset. Y11 for /WR and Y12 for /RS
# Then LED and /CS must be hard tied to Vcc and GND, and /RD is not used.
#
# 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.
#
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)
class TFT_io:
#
# display bitmap
#
@staticmethod
@micropython.viper
def displaySCR_bitmap(bits: ptr8, size: int, control: ptr8, bg_buf: ptr8):
gpioa = ptr8(stm.GPIOA)
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
gpioam = ptr16(stm.GPIOA + stm.GPIO_MODER)
#
transparency = control[6]
bm_ptr = 0
bg_ptr = 0
mask = 0x80
# rd_command = 0x2e ## start read
while size:
# if False: # transparency: # read back data
# gpioa[stm.GPIO_ODR] = rd_command # start/continue read command
# gpiob[1] = D_C | WR # set C/D and WR low
# gpiob[0] = D_C | WR # set C/D and WR high
# gpioam[0] = 0 # configure X1..X8 as Input
# gpiob[1] = RD # set RD low. C/D still high
# rd_command = 0x3e # continue read
# bg_red = gpioa[stm.GPIO_IDR] # get data from port A
# gpiob[0] = RD # set RD high again
# gpiob[1] = RD # set RD low. C/D still high
# delay = 1
# bg_green = gpioa[stm.GPIO_IDR] # get data from port A
# gpiob[0] = RD # set RD high again
# gpiob[1] = RD # set RD low. C/D still high
# delay = 1
# bg_blue = gpioa[stm.GPIO_IDR] # get data from port A
# gpiob[0] = RD # set RD high again
# gpioam[0] = 0x5555 # configure X1..X8 as Output
# gpioa[stm.GPIO_ODR] = 0x3c # continue write command
# gpiob[1] = D_C | WR # set C/D and WR low
# gpiob[0] = D_C | WR # set C/D and WR high
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: # map ptr advance on byte exhaust
mask = 0x80
bm_ptr += 1
size -= 1
bg_ptr += 3
#
# Set the address range for various draw commands and set the TFT for expecting data
#
@staticmethod
@micropython.viper
def setXY_P(x1: int, y1: int, x2: int, y2: int): ## set the adress range, Portrait
# set column address
gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR)
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
gpioa[0] = 0x2b # command
gpiob[1] = D_C | WR # set C/D and WR low
gpiob[0] = D_C | WR # set C/D and WR high
gpioa[0] = x1 >> 8 # high byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x1 & 0xff# low byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x2 >> 8 # high byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x2 & 0xff# low byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
# set row address
gpioa[0] = 0x2a # command
gpiob[1] = D_C | WR # set C/D and WR low
gpiob[0] = D_C | WR # set C/D and WR high
gpioa[0] = y1 >> 8 # high byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y1 & 0xff# low byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y2 >> 8 # high byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y2 & 0xff# low byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = 0x2c # Start data entry
gpiob[1] = D_C | WR # set C/D and WR low
gpiob[0] = D_C | WR # set C/D and WR high
@staticmethod
@micropython.viper
def setXY_L(x1: int, y1: int, x2: int, y2: int): ## set the adress range, Landscape
# set column address
gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR)
gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL)
gpioa[0] = 0x2a # command
gpiob[1] = D_C | WR # set C/D and WR low
gpiob[0] = D_C | WR # set C/D and WR high
gpioa[0] = x1 >> 8 # high byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x1 & 0xff# low byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x2 >> 8 # high byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = x2 & 0xff# low byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
# set row address
gpioa[0] = 0x2b # command
gpiob[1] = D_C | WR # set C/D and WR low
gpiob[0] = D_C | WR # set C/D and WR high
gpioa[0] = y1 >> 8 # high byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y1 & 0xff# low byte of x1
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y2 >> 8 # high byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = y2 & 0xff# low byte of x2
gpiob[1] = WR # set WR low. C/D still high
gpiob[0] = WR # set WR high again
gpioa[0] = 0x2c # Start data entry
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
# 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
#
@staticmethod
@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 OODR 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
#
@staticmethod
@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 OODR 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
#
@staticmethod
@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 OODR 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.
#
@staticmethod
@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
#
@staticmethod
@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 OODR 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
#
@staticmethod
@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
#
@staticmethod
@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 OODR 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
#
@staticmethod
@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
#
@staticmethod
@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
#
@staticmethod
@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)

Wyświetl plik

@ -0,0 +1,245 @@
# asynctouch.py Asynchronous version of XPT2046 touchscreen controller.
# The MIT License (MIT)
#
# Copyright (c) 2016 Robert Hammelrath, 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.
#
# 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
#
def __init__(self, objsched, controller = "XPT2046", calibration = None, raw = False):
self.raw = raw
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.margin = 50 * 50 # tolerance margin: standard deviation of distance of touch from mean; store square
self.buff = [[-self.margin, -self.margin] for x in range(5)] # confidence == 5. Impossible coords
if calibration:
self.calibration = calibration
else: # default values for my tft
self.calibration = (-3917,-0.127,-3923,-0.1267,-3799,-0.07572,-3738,-0.07814)
self.position = [0, 0]
self.ready = False
self.touched = False
objsched.add_thread(self.main_thread())
#
# Now I'm up to get my touches
#
#
# set parameters for get_touch()
# 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
#
def touch_parameter(self, confidence = 5, margin = 50, calibration = None):
confidence = max(min(confidence, 25), 5)
margin = max(min(margin, 100), 1)
m = margin * margin
if confidence != len(self.buff):
self.buff = [[-m, -m] for x in range(confidence)] # impossible values
self.margin = margin * margin # store the square value
if calibration:
self.calibration = calibration
#
# get_touch(): 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
# updates self.position
def main_thread(self):
buff = self.buff
buf_length = len(buff)
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
if self.raw:
self.position[0] = meanx
self.position[1] = meany
else:
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
def get_touch(self):
if self.ready:
self.ready = False
return self.position
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)
self.position[0] = int((touch[0] + xadd) * xmul)
self.position[1] = int((touch[1] + yadd) * ymul)
#
# 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

171
tft_gui/button.py 100644
Wyświetl plik

@ -0,0 +1,171 @@
from delay import Delay
from ui import get_stringsize, print_centered, touchable, CIRCLE, RECTANGLE, CLIPPED_RECT
# 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
class Button(touchable):
def __init__(self, objsched, tft, objtouch, location, *, shape=CIRCLE, height=50, width=50, fill=True,
fgcolor=None, bgcolor=None, fontcolor=None, litcolor=None, text='', font=None, show=True, callback=lambda x : None,
args=[]):
super().__init__(objsched, objtouch)
self.objsched = objsched
self.tft = tft
self.location = location
self.shape = shape
self.height = height
self.width = width
self.radius = height // 2
self.fill = fill
self.fgcolor = fgcolor
self.bgcolor = bgcolor
self.font = font if font is not None else tft.text_font
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
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
self.busy = False
if show:
self.show()
def show(self):
tft = self.tft
fgcolor = tft.getColor() # save old colors
bgcolor = tft.getBGColor()
x = self.location[0]
y = self.location[1]
if not self.visible: # erase the button
tft.setColor(bgcolor)
tft.fillRectangle(x, y, x + self.width, y + self.height)
tft.setColor(fgcolor)
return
if self.fgcolor is not None:
tft.setColor(self.fgcolor)
if self.bgcolor is not None:
tft.setBGColor(self.bgcolor)
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)
else:
tft.drawCircle(x, y, self.radius)
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)
else:
tft.drawRectangle(x, y, x1, y1)
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)
else:
tft.drawClippedRectangle(x, y, x1, y1)
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)
tft.setColor(fgcolor) # restore them
tft.setBGColor(bgcolor)
def shownormal(self):
self.fgcolor = self.orig_fgcolor
self.show()
def touched(self, x, y): # If touched, process it otherwise do nothing
is_touched = False
if self.shape == CIRCLE:
r = self.radius
dx = r - (x - self.location[0])
dy = r - (y - self.location[1])
if (dx * dx + dy * dy) < (r * r): # Pythagoras is alive!
is_touched = True
elif self.shape in (RECTANGLE, CLIPPED_RECT): # rectangle
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:
is_touched = True
if is_touched and self.litcolor is not None:
self.fgcolor = self.litcolor
self.show()
self.delay.trigger(1)
if is_touched and not self.busy: # Respond once to a press
self.callback(self, self.callback_args) # Callback not a bound method so pass self
self.busy = True # Ensure no response to continued press
def untouched(self): # User has released touchpad or touched elsewhere
self.busy = False
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

Wyświetl plik

@ -0,0 +1,106 @@
import gc
from font14 import font14
from tft import TFT, LANDSCAPE
from usched import Sched
from asynctouch import TOUCH
from button import Button, Buttonset, RadioButtons
from ui import CIRCLE, RECTANGLE, CLIPPED_RECT
#gc.collect()
def callback(button, args):
arg = args[0]
print('Returned: ', arg)
tft = button.tft
tft.setTextPos(0, 240)
tft.setTextStyle(None, None, 0, font14)
tft.printString('Button argument zero: {} '.format(arg))
if arg == 'Q':
button.objsched.stop()
#gc.collect()
# These tables contain args that differ between members of a set of related buttons
table = [
{'fgcolor' : (0, 128, 0), 'litcolor' : (0, 255, 0), 'text' : 'Yes', 'args' : ('A'), 'fontcolor' : (0, 0, 0)},
{'fgcolor' : (255, 0, 0), 'text' : 'No', 'args' : ('B')},
{'fgcolor' : (0, 0, 255), 'text' : '???', 'args' : ('C'), 'fill': False},
{'fgcolor' : (128, 128, 128), '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' : (0, 255, 0), 'shape' : CLIPPED_RECT, 'text' : 'Start', 'args' : ['Live']},
{'fgcolor' : (255, 0, 0), 'shape' : CLIPPED_RECT, 'text' : 'Stop', 'args' : ['Die']},
]
table4 = [
{'text' : '1', 'args' : ('1')},
{'text' : '2', 'args' : ('2')},
{'text' : '3', 'args' : ('3')},
{'text' : '4', 'args' : ('4')},
]
#gc.collect()
# THREADS
def stop(fTim, objsched): # Stop the scheduler after fTim seconds
yield fTim
objsched.stop()
# USER TEST FUNCTION
def test(duration = 0):
if duration:
print("Test TFT panel for {:3d} seconds".format(duration))
else:
print('Testing TFT...')
objsched = Sched() # Instantiate the scheduler
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
mytouch = TOUCH(objsched, "XPT2046")
mytft.backlight(100) # light on
# Button assortment
x = 50
for t in table:
Button(objsched, mytft, mytouch, (x, 0), font = font14, callback = callback, **t)
x += 70
# Highlighting buttons
x = 50
for t in table2:
Button(objsched, mytft, mytouch, (x, 60), font = font14, fgcolor = (128, 128, 128),
fontcolor = (0, 0, 0), litcolor = (255, 255, 255), callback = callback, **t)
x += 70
# On/Off toggle
x = 50
bs = Buttonset(callback)
for t in table3: # Buttons overlay each other at same location
bs.add_button(objsched, mytft, mytouch, (x, 120), font = font14, fontcolor = (0, 0, 0), **t)
bs.run()
# Radio buttons
x = 50
rb = RadioButtons(callback, (0, 0, 255)) # color of selected button
for t in table4:
rb.add_button(objsched, mytft, mytouch, (x, 180), font = font14, fontcolor = (255, 255, 255),
fgcolor = (0, 0, 90), height = 30, **t)
x += 40
rb.run()
# Start scheduler
if duration:
objsched.add_thread(stop(duration, objsched)) # Commit suicide after specified no. of seconds
objsched.run() # Run it!
test() # Forever: we have a Quit button!

42
tft_gui/delay.py 100644
Wyświetl plik

@ -0,0 +1,42 @@
# 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

118
tft_gui/font10.py 100644
Wyświetl plik

@ -0,0 +1,118 @@
# 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,
}

118
tft_gui/font14.py 100644
Wyświetl plik

@ -0,0 +1,118 @@
# 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,
}

146
tft_gui/slider.py 100644
Wyświetl plik

@ -0,0 +1,146 @@
# slider.py
# A slider's text items lie outside its bounding box (area sensitive to touch)
from ui import touchable
from TFT_io import TFT_io
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, cb_end=lambda x, y : None,
cbe_args=[], cb_move=lambda x, y : None, cbm_args=[], to_string=lambda x : str(x), value=0.0):
super().__init__(objsched, objtouch)
self.objsched = objsched
self.tft = tft
self.location = location
self.height = height
self.width = width
self.divisions = divisions
self.legends = legends
self.fgcolor = fgcolor
self.bgcolor = bgcolor
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
self.font = font
self.slidecolor = slidecolor
self.cb_end = cb_end
self.cbe_args = cbe_args
self.cb_move = cb_move
self.cbm_args = cbm_args
self.to_string = to_string # Applied to display at bottom: user converts 0-1.0 to string with any scaling applied
self.was_touched = False
self.old_text_end = False
self.slidewidth = int(width / 1.3)
self.slidewidth += self.slidewidth % 2 # Ensure divisible by 2
self.slideheight = 6 # must be divisible by 2
# We draw an odd number of pixels:
self.slidebytes = (self.slideheight + 1) * (self.slidewidth + 1) * 3
self.slidebuf = bytearray(self.slidebytes)
self.slide_x = -1
self.slide_y = -1 # Invalidate old position
self.border_y = min(self.slideheight // 2, 10) # Allow space above and below slot
self._value = min(max(value, 0.0), 1.0) # User supplies 0-1.0
self.show()
objsched.add_thread(self.mainthread())
def show(self):
tft = self.tft
fgcolor = tft.getColor() # save old colors
bgcolor = tft.getBGColor()
mybgcolor = bgcolor
x = self.location[0]
y = self.location[1] + self.border_y
if self.bgcolor is not None:
tft.setColor(self.bgcolor)
else:
tft.setColor(bgcolor)
tft.setTextStyle(self.fontcolor, None, 2, self.font)
if self.fgcolor is not None:
tft.setColor(self.fgcolor)
else:
tft.setColor(fgcolor)
if self.bgcolor is not None:
mybgcolor = self.bgcolor
tft.setBGColor(mybgcolor)
height = self.height
width = self.width
dx = width // 3
xcentre = x + width // 2
tft.drawRectangle(x + dx, y, x + 2 * dx, y + height)
if self.divisions > 0:
dy = height // self.divisions # Tick marks
ytick = y
fhdelta = self.font.bits_vert // 2
for tick in range(self.divisions + 1):
tft.drawHLine(x, ytick, dx)
tft.drawHLine(x + 2 * dx, ytick, dx)
ytick += dy
if self.legends is not None: # Legends
if len(self.legends) <= 1:
dy = 0
else:
dy = height // (len(self.legends) -1)
yl = y + height # Start at bottom
for legend in self.legends:
tft.setTextPos(x + width, yl - fhdelta)
tft.printString(legend)
yl -= dy
sw = self.slidewidth # Handle slider
sh = self.slideheight
sliderpos = int(y + height - self._value * height)
if self.slidecolor is not None:
tft.setColor(self.slidecolor)
if self.slide_x >= 0: # Restore background
tft.setXY(self.slide_x, self.slide_y, self.slide_x + sw, self.slide_y + sh)
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
x0 = xcentre - sw // 2
y0 = sliderpos - sh // 2
x1 = xcentre + sw // 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_x = x0
self.slide_y = y0
tft.fillRectangle(x0, y0, x1, y1) # Draw slider
textx = x
texty = y + height + 2 * self.border_y
tft.setTextPos(textx, texty)
if self.old_text_end:
tft.setColor(mybgcolor)
tft.fillRectangle(textx, texty, self.old_text_end, texty + self.font.bits_vert)
tft.printString(self.to_string(self._value))
self.old_text_end = tft.getTextPos()[0]
tft.setColor(fgcolor) # restore them
tft.setBGColor(bgcolor)
def value(self, val=None):
if val is None:
return self._value
self._value = min(max(val, 0.0), 1.0)
self.show()
def touched(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
self._value = (y1 - y) / self.height
def untouched(self): # User has released touchpad or touched elsewhere
if self.was_touched:
self.cb_end(self, self.cbe_args) # Callback not a bound method so pass self
self.was_touched = False
def mainthread(self):
old_value = self._value
while True:
yield
val = self._value
if val != old_value:
old_value = val
self.cb_move(self, self.cbm_args) # Callback not a bound method so pass self
self.show()

Wyświetl plik

@ -0,0 +1,102 @@
import gc
from font10 import font10
from tft import TFT, LANDSCAPE
from usched import Sched
from asynctouch import TOUCH
from slider import Slider
from button import Button
from ui import CLIPPED_RECT
import math
#gc.collect()
class Dial(object):
def __init__(self, objsched, tft, x, y, r, l):
self.objsched = objsched
self.tft = tft
self.xorigin = x
self.yorigin = y
self.radius = r
self.pointerlen = l
self.angle = None
self.delta = 1
tft.drawCircle(x, y, r)
objsched.add_thread(self.mainthread())
def update(self, angle):
tft = self.tft
fgcolor = tft.getColor() # save old colors
bgcolor = tft.getBGColor()
if self.angle is not None:
tft.setColor(bgcolor)
self.drawpointer(self.angle) # erase old
tft.setColor(fgcolor)
self.drawpointer(angle) # draw new
self.angle = angle # update old
tft.setColor(fgcolor) # restore them
tft.setBGColor(bgcolor)
def drawpointer(self, radians):
x_end = int(self.xorigin + self.pointerlen * math.sin(radians))
y_end = int(self.yorigin - self.pointerlen * math.cos(radians))
self.tft.drawLine(self.xorigin, self.yorigin, x_end, y_end)
def mainthread(self):
while True:
yield 0.1
angle = self.angle if self.angle is not None else 0
angle += math.pi * 2 * self.delta / 10
self.update(angle)
# CALLBACKS
# cb_end occurs when user stops touching the control
def callback(slider, args):
print('{} returned {}'.format(args[0], slider.value()))
def to_string(val):
return '{:4.1f}ohms'.format(val * 10)
def master_moved(slider, args):
val = slider.value()
slave1 = args[0]
slave1.value(val)
slave2 = args[1]
slave2.value(val)
# Either slave has had its slider moved (by user or by having value altered)
def slave_moved(slider, args):
dial = args[0]
dial.delta = slider.value()
def doquit(button, args):
button.objsched.stop()
# USER TEST FUNCTION
# '0', '1','2','3','4','5','6','7','8','9','10'
# Common arguments for all three sliders
table = {'fontcolor' : (255, 255, 255),
'legends' : ('0', '5', '10'),
'to_string' : to_string,
'cb_end' : callback,
'value' : 0.5}
def test(duration = 0):
print('Test TFT panel...')
objsched = Sched() # Instantiate the scheduler
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
mytouch = TOUCH(objsched, "XPT2046")
mytft.backlight(100) # light on
Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = (255, 0, 0),
height = 30, text = 'Quit', shape = CLIPPED_RECT)
dial1 = Dial(objsched, mytft, 350, 60, 50, 48)
dial2 = Dial(objsched, mytft, 350, 170, 50, 48)
y = 5
slave1 = Slider(objsched, mytft, mytouch, (80, y), font10,
fgcolor = (0, 255, 0), cbe_args = ('Slave1',), cb_move = slave_moved, cbm_args = (dial1,), **table)
slave2 = Slider(objsched, mytft, mytouch, (160, y), font10,
fgcolor = (0, 255, 0), cbe_args = ('Slave2',), cb_move = slave_moved, cbm_args = (dial2,), **table)
Slider(objsched, mytft, mytouch, (0, y), font10,
fgcolor = (255, 255, 0), cbe_args = ('Master',), cb_move = master_moved, cbm_args = (slave1, slave2), **table)
objsched.run() # Run it!
test()

668
tft_gui/tft.py 100644
Wyświetl plik

@ -0,0 +1,668 @@
#
# 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 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 just for writes is X1..X8 for data, Y9 for /Reset. Y11 for /WR and Y12 for /RS
# Then LED and /CS must be hard tied to Vcc and GND, and /RD is not used.
#
# 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.
#
import pyb, stm
from uctypes import addressof
from TFT_io 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 (does not seem to work)
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
else:
self.setXY = TFT_io.setXY_L
# ----------
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.setTextPos(0,0)
self.setScrollArea(0, self.disp_y_size + 1, 0)
self.text_font = None
self.setTextStyle(None, None, 0, None, 0)
#
# Init done. clear Screen and switch BG LED on
#
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
#
# 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
# Rather slow at 40µs/Pixel
#
def drawPixel(self, x, y):
self.setXY(x, y, x, y)
TFT_io.displaySCR_AS(self.colorvect, 1) #
#
# clear screen, set it to BG color.
#
def clrSCR(self):
self.clrXY()
TFT_io.fillSCR_AS(self.BGcolorvect, (self.disp_x_size + 1) * (self.disp_y_size + 1))
self.text_x = self.text_y = self.scroll_start = 0
self.setScrollStart(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):
if y1 == y2:
self.drawHLine(x1, y1, x2 - x1 + 1)
elif x1 == x2:
self.drawVLine(x1, y1, y2 - y1 + 1)
else:
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)
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)
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): # draw horiontal Line
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(self.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): # draw horiontal Line
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(self.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):
if x1 > x2:
t = x1; x1 = x2; x2 = t
if y1 > y2:
t = y1; y1 = y2; y2 = t
self.drawHLine(x1, y1, x2 - x1 + 1)
self.drawHLine(x1, y2, x2 - x1 + 1)
self.drawVLine(x1, y1, y2 - y1 + 1)
self.drawVLine(x2, y1, y2 - y1 + 1)
#
# 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:
t = x1; x1 = x2; x2 = t
if y1 > y2:
t = y1; y1 = y2; y2 = t
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):
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:
self.drawPixel(x1 + 2,y1 + 1)
self.drawPixel(x1 + 1,y1 + 2)
self.drawPixel(x2 - 2,y1 + 1)
self.drawPixel(x2 - 1,y1 + 2)
self.drawPixel(x1 + 2,y2 - 1)
self.drawPixel(x1 + 1,y2 - 2)
self.drawPixel(x2 - 2,y2 - 1)
self.drawPixel(x2 - 1,y2 - 2)
self.drawHLine(x1 + 3, y1, x2 - x1 - 5)
self.drawHLine(x1 + 3, y2, x2 - x1 - 5)
self.drawVLine(x1, y1 + 3, y2 - y1 - 5)
self.drawVLine(x2, y1 + 3, y2 - y1 - 5)
#
# 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):
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)
self.drawHLine(x1 + 3, y2 - i, x2 - x1 - 5)
elif i == 1:
self.drawHLine(x1 + 2, y1 + i, x2 - x1 - 3)
self.drawHLine(x1 + 2, y2 - i, x2 - x1 - 3)
elif i == 2:
self.drawHLine(x1 + 1, y1 + i, x2 - x1 - 1)
self.drawHLine(x1 + 1, y2 - i, x2 - x1 - 1)
else:
self.drawHLine(x1, y1 + i, x2 - x1 + 1)
self.drawHLine(x1, y2 - i, x2 - x1 + 1)
#
# draw a circle at x, y with radius
# Straight port from the UTFT Library at Rinky-Dink Electronics
#
def drawCircle(self, x, y, radius):
f = 1 - radius
ddF_x = 1
ddF_y = -2 * radius
x1 = 0
y1 = radius
self.drawPixel(x, y + radius)
self.drawPixel(x, y - radius)
self.drawPixel(x + radius, y)
self.drawPixel(x - radius, y)
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)
self.drawPixel(x - x1, y + y1)
self.drawPixel(x + x1, y - y1)
self.drawPixel(x - x1, y - y1)
self.drawPixel(x + y1, y + x1)
self.drawPixel(x - y1, y + x1)
self.drawPixel(x + y1, y - x1)
self.drawPixel(x - y1, y - x1)
#
# fill a circle at x, y with radius
# Straight port from the UTFT Library at Rinky-Dink Electronics
# Instead of caluclating x = sqrt(r*r - y*y), it searches the x
# for r*r = x*x + x*x
#
def fillCircle(self, x, y, radius):
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))
self.drawHLine(x + x1i, y - y1i, 2 * (-x1i))
break;
#
# Draw a bitmap at x,y with size sx, sy
# mode determines the type of expected data
# mode = 0: The data must contain 3 bytes/pixel red/green/blue
# mode = 1: The data must contain 2 packed bytes/pixel blue/green/red in 565 format
# mode = 2: The data contains 1 bit per pixel, mapped to fg/bg color
#
def drawBitmap(self, x, y, sx, sy, data, mode = 0):
self.setXY(x, y, x + sx - 1, y + sy - 1)
if mode == 0:
TFT_io.displaySCR_AS(data, sx * sy)
elif mode == 1:
TFT_io.displaySCR565_AS(data, sx * sy)
elif mode == 2:
control = bytearray(self.BGcolorvect + self.colorvect + chr(self.transparency))
TFT_io.displaySCR_bitmap(data, sx*sy, control, 0)
#
# 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_fta = tfa
self.scroll_vsa = vsa
self.scroll_bfa = bfa
#
# set the line which is displayed first
#
def setScrollStart(self, lline):
TFT_io.tft_cmd_data_AS(0x37, bytearray([(lline >> 8) & 0xff, lline & 0xff]), 2)
self.scroll_start = lline # store the logical first line
#
# Set text position
#
def setTextPos(self, x, y, clip = False, scroll = True):
self.text_width, self.text_height = self.getScreensize()
self.text_x = x
self.text_y = y
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_y)
#
# Set Text Style
#
def setTextStyle(self, fgcolor = None, bgcolor = None, transparency = None, font = None, gap = None):
if font != None:
self.text_font = font
if font:
self.text_rows, self.text_cols, nchar, first = font.get_properties() #
if transparency != None:
self.transparency = transparency
if gap != None:
self.text_gap = gap
self.text_color = bytearray(0)
if bgcolor != None:
self.text_color += bytearray(bgcolor)
else:
self.text_color += self.BGcolorvect
if fgcolor != None:
self.text_color += bytearray(fgcolor)
else:
self.text_color += self.colorvect
if transparency != None:
self.transparency = transparency
self.text_color += bytearray([self.transparency])
if gap != None:
self.text_gap = gap
#
# Check, if a new line is to be opened
# if yes, advance, including scrolling, and clear line, if flags is set
#
def printNewline(self):
self.text_y += self.text_rows
if (self.text_y + self.text_rows) > self.scroll_vsa: # does the line fit?
self.text_y = 0
newline = self.text_rows
self.setScrollStart(newline)
elif self.scroll_start > 0: # Scrolling has started
newline = (self.scroll_start + self.text_rows) % self.scroll_vsa
self.setScrollStart(newline)
#
# Carriage Return
#
def printCR(self): # clear to end of line
self.text_x = 0
#
# clear to end-of-line
#
def printClrEOL(self): # clear to end of line
self.setXY(self.text_x, self.text_y,
self.text_width - self.text_x - 1, self.text_y + self.text_rows - 1) # set display window
TFT_io.fillSCR_AS(self.text_color, self.text_width * self.text_rows)
#
# 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() # NL: advance to the next line
self.printClrEOL() # clear to end of line
else:
return 0
# set data arrays & XY-Range
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
# 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_bitmap(fontptr, pix_count, self.text_color, bg_buf) # display char!
#advance pointer
self.text_x += (cols + self.text_gap)
return cols + self.text_gap

44
tft_gui/ui.py 100644
Wyświetl plik

@ -0,0 +1,44 @@
# ui.py Base classes and utilities for TFT GUI
CIRCLE = 1
RECTANGLE = 2
CLIPPED_RECT = 3
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 touch-enabled classes.
class touchable(object):
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()
for obj in cls.touchlist:
if obj.enabled:
obj.touched(x, y)
elif not mytouch.touched:
for obj in cls.touchlist:
obj.untouched()
def __init__(self, objsched, objtouch):
touchable.touchlist.append(self)
self.enabled = True # Available to user/subclass
if touchable.objtouch is None: # Initialising class and thread
touchable.objtouch = objtouch
objsched.add_thread(self.touchtest()) # One thread only

255
tft_gui/usched.py 100644
Wyświetl plik

@ -0,0 +1,255 @@
# 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