Animación en LCD HD44780
Otro proyecto con el LCD HD44780! Ahora vamos a crear una animación utilizando la CGRAM, que nos ofrece 8 huecos libres para customizar nuestros propios caracteres (o píxeles en nuestro caso). Todos los detalles del pin layout, configuraciones, etc están en el anterior artículo del LCD HD44780, aquí
He hecho 3 pruebas, las dos primeras están comentadas y solo se ejecutará la tercera:
- pruebaCustomFont(). Primera prueba para dibujar un 0 y un 1 con una fuente personalizada pixel a pixel.
- bolaRebotando(). Se trata de un cuadrado de 4 píxeles que traza una trayectoria hacia abajo y luego hacia arriba, rebotando en la pared derecha de este caracter de 5×7 puntos.
- naveEspacial(). Es una animación de un cohete espacial con dos propulsores en su base, animada pixel a pixel.
Recordad que hay que instalar los siguientes paquetes:
sudo apt-get install python-setuptools
sudo apt-get install python-pip
sudo pip install rpi.gpio
Dibujando con el LCD HD44780
He usado el LCD como si fuera un display de matriz de puntos, dibujando un cohete con dos propulsores. Para ello he creado «nuevos caracteres» en la memoria CGRAM y los he ido mostrando y sobreescribiendo. Como solo tengo 8 huecos para custom characters, he agrupado mi figura en 3 partes: primero dos cuadrados de 4 píxeles cada uno (los propulsores), luego la parte externa del cohete con 8 píxeles más, y finalmente la parte interior con 8 píxeles más. Para dibujar las partes me ha sido de mucha utilidad esta web https://www.quinapalus.com/hd44780udg.html, que permite marcar los píxeles que quieres en tu caracter y te ofrece el array hexadecimal que lo define.

Y aquí el código megacomentado:
#!/usr/bin/python
import RPi.GPIO as GPIO
from time import sleep
class HD44780:
'''
Inicializador
'''
def __init__(self):
#Establecer pines GPIO de la Raspberry Pi para los pines RS, E y datos
self.pin_rs = 2
self.pin_e = 3
self.pins_db = [25, 8, 9, 11]
self.dinamico = False #Modo estático activado (el texto aparece escrito instantáneamente)
#Establecer los pines como salida
GPIO.setmode(GPIO.BCM) #Numeración BCM (numeración GPIO en lugar de numeración física de la placa)
GPIO.setup(self.pin_e, GPIO.OUT) #Establecer pin Enable como salida
GPIO.setup(self.pin_rs, GPIO.OUT) #Establecer pin Commando/Dato como salida
for pin in self.pins_db: #Establecer pines de datos como salida
GPIO.setup(pin, GPIO.OUT)
self.clear() #Llamar a función local para vaciar la pantalla y setearla
'''
Vaciar pantalla y configurarla
'''
def clear(self):
self.cmd(0x28) # Function set: Modo 4 bits, 2 líneas, 5x7 pixels (DL = 0: 4 bits, N = 1: 2 lines, F = 0: 5x7 dots, # = 0: Not 24x4 module)
self.cmd(0x0C) # Display ON/OFF Control: Display activado, cursor y parpadeo desactivado (D = 1: Display On, C = 0: Cursor Off, B: Cursor blink off)
self.cmd(0x06) # Entry Mode Set: Dirección del cursor (I/D = 1: Increment, S = 0: Do not accompany display shift)
self.cmd(0x01) # Clear Display: Vaciar pantalla
'''
Enviar comando
bits: comando a enviar
char_mode: modo comando/dato. Por defecto en false (comando)
'''
def cmd(self, bits, char_mode=False):
sleep(0.001) #Esperar 1ms
bits = bin(bits) #Castear a binario
bits = bits[2:] #Descartar los primeros 2 bits (0b)
zeros = (8 - len(bits)) * "0" #Generar "00"
bits = zeros + bits #Insertar los dos "00" a la izquierda de bits
GPIO.output(self.pin_rs, char_mode) #Establecer pin RS = modo comando / modo dato
#Primer paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4): #Se envian los datos, de 0 a 3
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
#Segundo paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4, 8): #Se envian los datos, de 4 a 7
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i-4], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
'''
Inicializar datos
'''
def inicializarDatos(self):
for pin in self.pins_db:
GPIO.output(pin, False) #Inicializar todos los pines de datos a 0
'''
Refrescar enable
'''
def refrescarEnable(self):
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, True) #Enable = 1
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, False) #Enable = 0
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
'''
Establecer fuente personalizada
'''
def customfont(self, posicion, cero, uno, dos, tres, cuatro, cinco, seis, siete):
self.cmd(0x40 + posicion * 8) #(0100, 0000) Acceder 0x00+posicion en CGRAM
self.cmd(cero, True) #Enviar posición 0
self.cmd(uno, True) #Enviar posición 1
self.cmd(dos, True) #Enviar posición 2
self.cmd(tres, True) #Enviar posición 3
self.cmd(cuatro, True) #Enviar posición 4
self.cmd(cinco, True) #Enviar posición 5
self.cmd(seis, True) #Enviar posición 6
self.cmd(siete, True) #Enviar posición 7
'''
Resetear un caracter en CGRAM
'''
def resetear(self, posicion):
self.cmd(0x40 + posicion * 8) #Acceder a CGRAM en la posición dada
for i in range(8):
self.cmd(0x0, True) #Setear a 0 todas las líneas del caracter
'''
Resetear todos los caracter en CGRAM
'''
def resetearCGRAM(self):
for i in range(8):
self.resetear(i)
'''
Mostrar CGRAM
'''
def mostrarCGRAM(self):
for i in range(8):
self.cmd(0x80 + i) #Posicionar
self.cmd(0x00 + i, True) #Cargar elementos entre 0x00 y 0x07
'''
Mostrar caracter
'''
def mostrar(self, posicion):
lcd.cmd(0x80) #Mostrarlo en la primera línea
lcd.cmd(0x0 + posicion, True) #Mostrar caracter
'''
Setear y mostrar un 0 y un 1 con fuente custom
'''
def pruebaCustomFont():
lcd.resetearCGRAM()
lcd.customfont(0, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f) #Fuente personalizada de posición 0 (Un número 0)
lcd.customfont(1, 0x4, 0xc, 0x14, 0x4, 0x4, 0x4, 0x4, 0x1f) #Fuente personalizada en posición 1 (Un número 1)
lcd.mostrarCGRAM()
'''
Setear y mostrar un cuadrado de 4 píxeles que empieza en la esquina superior izquierda y se desplaza 45 grados hacia abajo-derecha trazando un > y vuelve al origen
'''
def bolaRebotando():
#lcd.resetearCGRAM()
lcd.customfont(0, 0x18, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(1, 0x0, 0xc, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(2, 0x0, 0x0, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(3, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x0)
lcd.customfont(4, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x0, 0x0)
lcd.customfont(5, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0)
lcd.customfont(6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18)
while True:
for i in range(6):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
for j in range(6):
lcd.cmd(0x01)
lcd.mostrar(6-j)
sleep(0.1)
'''
Setear y mostrar nave espacial
'''
def naveEspacial():
#Primera parte - Propulsores. Dos cuadros en la base de 4 píxeles cada uno en los extremos
lcd.customfont(0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0)
lcd.customfont(1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x0)
lcd.customfont(2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x10)
lcd.customfont(3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18)
lcd.customfont(4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x18)
lcd.customfont(5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x18)
lcd.customfont(6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x19)
lcd.customfont(7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1b)
#Mostrar parte 1
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
#Segunda parte - Parte izquierda. Línea izquierda desde los propulsores hacia arriba, con parte de la derecha
lcd.customfont(0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x1b, 0x1b)
lcd.customfont(1, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(2, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(3, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(4, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(5, 0x4, 0x8, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(6, 0x4, 0xa, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(7, 0x4, 0xa, 0xa, 0x8, 0x8, 0x8, 0x1b, 0x1b)
#Mostrar parte 2
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
#Tercera parte - Parte derecha y relleno. Acaba línea derecha y rellena el centro.
lcd.customfont(0, 0x4, 0xa, 0xa, 0xa, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(1, 0x4, 0xa, 0xa, 0xa, 0xa, 0x8, 0x1b, 0x1b)
lcd.customfont(2, 0x4, 0xa, 0xa, 0xa, 0xa, 0xa, 0x1b, 0x1b)
lcd.customfont(3, 0x4, 0xa, 0xa, 0xa, 0xa, 0xe, 0x1b, 0x1b)
lcd.customfont(4, 0x4, 0xa, 0xa, 0xa, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(5, 0x4, 0xa, 0xa, 0xe, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(6, 0x4, 0xa, 0xe, 0xe, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(7, 0x4, 0xe, 0xe, 0xe, 0xe, 0xe, 0x1b, 0x1b)
#Mostrar parte 3
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
'''
Función principal
'''
if __name__ == '__main__':
lcd = HD44780() #Cargar clase principal
#lcd.clear()
#lcd.resetearCGRAM()
#pruebaCustomFont() #Primera prueba con CustomFont
#bolaRebotando() #Segunda prueba con CustomFont
while True: naveEspacial() #Tercera prueba con CustomFOnt
GPIO.cleanup()
Así es como queda en vídeo 🙂
Null Games