PANTALLAS LCD COLOR ST7789
Las pantallas LCD (Liquid-Crystal Display o Pantalla de Cristal Líquido) son una excelente opción para mostrar datos en color de proyectos de electrónica.
En esta práctica se explicará el uso de las pantallas basadas en el chip ST7789 controladas mediante bus SPI. Lo interesante de esta familia de pantallas es alta densidad de pixeles, pudiendo encontrar modelos de hasta 220ppi (pixeles por pulgada) y una resolución gráfica de 65.356 colores. Son pantallas tipo IPS (In-Plane Switching) o “de variación en el plano” con un amplio ángulo de visión, hasta 80 grados, lo que permite que se vean extremadamente nítidas desde cualquier posición.
En el mercado hay muchos modelos disponibles, pero aquí se utilizará la primera de la imagen (TTGO t-display ESP32 con pantalla de 1,14″) por su sencillez de uso, dado que al estar integrada en la propia placa no es necesario realizar ningún cableado y se puede empezar a trabajar con ella directamente.
Hay varios modelos de la placa TTGO t-display, es recomendable elegir una versión igual o superior a la V1.1 (la versión está serigrafiada en el reverso de la placa) y con la mayor QSPI flash posible (se pueden encontrar modelos con 4Mb y 16Mb).
Este es el mapa de pines de la placa:
LIBRERÍAS ST7789
MicroPython dispone de varias librerías (no estándares) para este tipo de pantallas, pero de entre ellas destacan:
- la escrita por devbis en Python, por su simplicidad.
- la escrita por russhughes en C, por su funcionalidad y velocidad de procesamiento.
Siguiendo los enlaces se puede acceder al repositorio de GitHub para utilizarlas.
En el caso de la primera bastará con descargarla desde GitHub (Code → Download ZIP) y copiar el fichero st7789py.py al microcontrolador. Las instrucciones para utilizarlas también están en GitHub.
La segunda, que es la que se va a utilizar en este tutorial, se puede encontrar tanto precompilada con el firmare de MicroPython como sin compilar (ficheros en C). Evidentemente lo más sencillo es utilizar la precompilada.
Para hacerlo se descargará la rama completa desde GitHub (Code → Download ZIP). Una vez descomprimida se puede localizar el fichero firmware.bin en la carpeta firmware/esp32 que es el que se debe grabar en el microcontrolador.
Una de las formas más sencillas es hacerlo es desde uPyCraft. Se debe conectar la placa al ordenador a través de un puerto USB-C, abrir el programa y hacer clic en Tools/BurmFirmware y marcar las siguientes opciones en el menú:
- board: esp32
- burn_addr: 0x1000
- erase_flash: yes (es siempre recomendable borrar los datos de la memoria flash antes de instalar cualquier nuevo firmware)
- com: puerto COM en el que esté conectada la placa . uPyCraft listará los posibles puertos COM del ordenador, en el que está conectada la placa con el cable USB.
- Firmware Choose/Users: ruta del ordenador en la que se ha descargado el firmware de MicroPython.
Por último habrá que pulsar OK.
LA PROGRAMACIÓN
Antes de comenzar…
- En el sketch es necesario configurar los pines que utiliza la pantalla e inicializarla para poder empezar a dibujar en ella.
Para configura la pantalla se utiliza el método st7789.ST7789(spi, width, height, reset, dc, cs, backlight, rotation).
Los argumentos requeridos son:
-
- spi: pines del bus SPI. Para el ESP32 únicamente está disponible el VSPI (CLK – Pin18 y MOSI – Pin 23 con una frecuencia inferior a 40 MHz).
- width: anchura en pixeles del dispositivo.
- height: altura en pixeles del dispositivo.
Los argumentos opcionales son:
-
- reset: pin para reiniciar.
- dc: pin DC (Data/Command).
- cs: pin CS (Chip Select).
- backlight: luz de fondo.
- rotation: 0-0 grados, 1-90 grados, 2-180 grados, 3-270 grados.
Para inicializarla se utiliza el método init().
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), dc=Pin(16, Pin.OUT), cs=Pin(5, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init()
- El sistema de coordenadas que utiliza la librería para localizar las representaciones es el cartesiano (unidades en –píxels–). Su eje horizontal es es el X y el eje vertical el Y.
- El origen (0,0) está en la esquina superior izquierda.
- El vértice opuesto en una pantalla de 135×240 píxeles estará en la coordenada (134×239).
- La librería da soporte a pantallas de 240×240, 135×240 y 240×320 pixeles.
- La pantalla se puede rotar con el método .rotation(r), siendo r el giro de la pantalla, pudiendo tomar los siguientes valores: 0-Retrato (0º), 1-Paisaje (90º), 2-Retrato inverso (180º), 3-Paisaje inverso (270º).
- Cuando la pantalla se rota, si se utiliza una pantalla diferente a la de 240×320 pixeles es necesario configurar un nuevo punto de origen para el sistema de coordenadas para compensar y que los píxeles se escriban en el área de memoria que corresponde a la pantalla visible. Para ello se utiliza el método .ST7789.offset (x_origen, y_origen). La pantalla TTGO-TD es de 135×240 y utiliza las siguientes compensaciones.
- La pantalla trabaja con colores en el sistema RGB565 (utiliza 2 bytes para indicar la intensidad del rojo, verde y azul). Es un sistema de compresión del RGB888:
En la librería tiene variables con los colores básicos para facilitar su uso. Son los siguientes: blanco (.WHITE), negro (.BLACK), azul (.BLUE), rojo (.RED), verde (.GREEN), cian (.CYAN), magenta (.MAGENTA) y amarillo (.YELLOW).
También se puede utilizar el método .color565(r, g, b) para convertir colores de RGB888 a RGB565.
RELLENO DE PANTALLA |
.fill(color) |
Rellena la pantalla con el color indicado. Se pueden utilizar los colores predefinidos o el método color565(r, g, b). Ejemplo: |
from machine import Pin, SPI from time import sleep import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.fill(st7789.WHITE) # relleno blanco sleep(1) tft.fill(st7789.color565(255,0,0)) # relleno rojo
PUNTOS -PIXELS- |
.pixel(x0, y0 , color) |
Dibuja un punto (píxel) en las coordenadas y con el color indicado. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.pixel(15,15,st7789.YELLOW) tft.pixel(20,15,st7789.MAGENTA) tft.pixel(25,15,st7789.color565(0,255,0))
MATRICES DE PUNTOS (ICONOS) |
.pixel(x0, y0) |
El método .pixel(x0, y0) se puede utilizar también para dibujar una matriz de puntos en la pantalla. No existe ninguna limitación en cuanto al tamaño de la matriz (siempre y cuando no se supere el tamaño de la pantalla). Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() ICONO = [ # Matriz de puntos [0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0], [0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0], [0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0], [0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0], [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1], [1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1], [1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1], [1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1], [1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1], [0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0], [0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0], [0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0], ] for y, fila in enumerate(ICONO): # Dibuja los puntos de la matriz for x, c in enumerate(fila): if c==1: tft.pixel(x,y,st7789.YELLOW)
LÍNEAS |
.line(x0, y0, x1, y1, color) |
Dibuja una línea con origen en (x0, y0) y final en (x1, y1) y color establecido. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.line(0,0,134,239,st7789.MAGENTA) tft.line(134,0,0,239,st7789.CYAN)
LÍNEAS HORIZONTALES |
.hline(x0, y0, ancho, color) |
Dibuja una línea horizontal de origen (x0, y0), anchura y color establecidos. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.hline(0,0,135,st7789.CYAN) tft.hline(0,239,135,st7789.color565(255,255,0))
LÍNEAS VERTICALES |
.vline(x0, y0, alto, color) |
Dibuja una línea vertical de origen (x0, y0), altura y color establecidos. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.vline(0,0,240,st7789.GREEN) tft.vline(134,0,240,st7789.color565(255,255,255))
RECTÁNGULOS |
.rect(x0, y0, ancho, alto, color) |
Dibuja un rectángulo con origen en (x0, y0), de anchura, altura y color establecidos. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.rect(10,10,115,220,st7789.RED)
RECTÁNGULOS RELLENOS |
.fill_rect(x0, y0, ancho, alto, color) |
Dibuja un rectángulo relleno con origen en (x0, y0), de anchura, altura y color establecidos. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.fill_rect(10,10,115,220,st7789.RED)
TEXTO |
.text(texto, x0, y0 , color_texto, color_fondo) |
Escribe un texto con punto de origen en (x0, y0), con el color de texto y fondo establecidos. El tamaño de letra será el de la fuente seleccionada, estando disponibles por defecto fuentes de 128 y 256 caracteres:
Ejemplo: |
from machine import Pin, SPI import st7789 # FUENTES DISPONIBLES # import vga1_8x8 as font # import vga2_8x8 as font import vga1_8x16 as font # Fuente seleccionada # import vga2_8x16 as font # import vga1_16x16 as font # import vga2_16x16 as font # import vga1_bold_16x16 as font # import vga2_bold_16x16 as font # import vga1_16x32 as font # import vga2_16x32 as font # import vga1_bold_16x32 as font # import vga2_bold_16x32 as font tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() tft.text(font, "HOLA MUNDO!", 25, 100, st7789.BLUE, st7789.YELLOW)
IMÁGENES |
.blit_buffer(buf, x0, y0 , anchura, altura) |
Dibuja una imagen en la pantalla almacenada en un búfer (buf), con punto de origen de representación en (x0, y0) y de dimensiones anchura x altura. TRATAMIENTO DE LAS IMAGENES (Aquí se pueden descargar imágenes de prueba) El búfer de la imagen debe estar en formato RGB565 (2 bytes por pixel), con los pixeles ordenados de izquierda a derecha y de arriba hacia abajo. Hacerlo de forma manual es prácticamente imposible, por lo que se será necesario utilizar un módulo de conversión para automatizar el trabajo. Para utilizarlo hay que seguir los siguientes pasos:
Como resultado se obtienen dos ficheros. Uno con los datos de la imagen para enviar al búfer y otro para visualizarla (el visor). Bastará ejecutar el visor para poder ver la imagen en la pantalla.
|
OTROS MÉTODOS DE LA LIBRERÍA ST7789
ANCHURA / ALTURA DE LA PANTALLA |
.width() – .height() |
Los métodos .width() y .heght() devuelven la anchura y altura de la pantalla. Ejemplo: |
from machine import Pin, SPI import st7789 tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) tft.init() print ("Anchura:", tft.width(), "pixeles - Altura:", tft.height(),"pixeles")
CONVERSOR BITARRAY – RGB565 |
map_bitarray_to_rgb565(bitarray, buffer, width, color, bg_color) |
Es una función para convertir un bitarray a un búfer de color RGB565 para utilizarlo con la función .blit_buffer(). En la función buffer el nombre del búfer de salida, width la anchura de cada línea, color el color de cada bit 1 del bitarray y bg_color el color de cada bit 0 del bitarray. Esta función tiene especial aplicación para imprimir texto con una fuente de alta resolución. Para hacerlo se puede usar la aplicación de Peter Hinch https://github.com/peterhinch/micropython-font-to-py para generar fuentes de mapa de bits desde .ttf . Ejemplo: |
''' bitarray.py Ejemplo de uso de map_bitarray_to_rgb565 dibujando sprites para TTGO t-display ''' import time, random, st7789 from machine import Pin, SPI SPRITES = 75 SPRITE_WIDTH = 16 SPRITE_HEIGHT = 16 SPRITE_STEPS = 3 SPRITE_BITMAPS = [ bytearray([ 0b00000000, 0b00000000, 0b00000001, 0b11110000, 0b00000111, 0b11110000, 0b00001111, 0b11100000, 0b00001111, 0b11000000, 0b00011111, 0b10000000, 0b00011111, 0b00000000, 0b00011110, 0b00000000, 0b00011111, 0b00000000, 0b00011111, 0b10000000, 0b00001111, 0b11000000, 0b00001111, 0b11100000, 0b00000111, 0b11110000, 0b00000001, 0b11110000, 0b00000000, 0b00000000, 0b00000000, 0b00000000]), bytearray([ 0b00000000, 0b00000000, 0b00000011, 0b11100000, 0b00001111, 0b11111000, 0b00011111, 0b11111100, 0b00011111, 0b11111100, 0b00111111, 0b11110000, 0b00111111, 0b10000000, 0b00111100, 0b00000000, 0b00111111, 0b10000000, 0b00111111, 0b11110000, 0b00011111, 0b11111100, 0b00011111, 0b11111100, 0b00001111, 0b11111000, 0b00000011, 0b11100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000]), bytearray([ 0b00000000, 0b00000000, 0b00000111, 0b11000000, 0b00011111, 0b11110000, 0b00111111, 0b11111000, 0b00111111, 0b11111000, 0b01111111, 0b11111100, 0b01111111, 0b11111100, 0b01111111, 0b11111100, 0b01111111, 0b11111100, 0b01111111, 0b11111100, 0b00111111, 0b11111000, 0b00111111, 0b11111000, 0b00011111, 0b11110000, 0b00000111, 0b11000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000]), bytearray([ 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000])] class pacman(): # clase de pacman para realizar un seguimiento de la ubicación y el paso de los sprites def __init__(self, x, y, step): self.x = x self.y = y self.step = step def move(self): self.step += 1 self.step %= SPRITE_STEPS self.x += 1 if self.x == 135-17: self.step = SPRITE_STEPS self.x %= 135-16 def main(): # Dibuja en la pantalla utilizando map_bitarray_to_rgb565 # Configura la pantalla tft = st7789.ST7789( SPI(2, baudrate=30000000, polarity=1, phase=1, sck=Pin(18), mosi=Pin(19)), 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=0) # Hbilita la pantalla y la limpia tft.init() tft.fill(st7789.BLACK) sprite = bytearray(512) # Dibuja sprites de pacman en posiciones aleatorias sprites = [] for man in range(SPRITES): sprites.append( pacman( random.randint(0, tft.width()-SPRITE_WIDTH), random.randint(0, tft.height()-SPRITE_HEIGHT), random.randint(0, SPRITE_STEPS-1) ) ) # Mueve y dibuja los sprites while True: for man in sprites: # Mueve el sprite man.move() # Convierte el bitmap en un búfer rgb565 para la función blit_buffer st7789.map_bitarray_to_rgb565(SPRITE_BITMAPS[man.step], sprite, SPRITE_WIDTH, st7789.YELLOW,st7789.BLACK) # Dibuja el búfer en la pantalla tft.blit_buffer(sprite, man.x, man.y, SPRITE_WIDTH, SPRITE_HEIGHT) time.sleep(0.1) main()
Leave a Reply
Tu correo electrónico está seguro.
You must be logged in to post a comment.