Animación en LCD HD44780

octubre 13th, 2019 Categories: GPIO

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:

  1. pruebaCustomFont(). Primera prueba para dibujar un 0 y un 1 con una fuente personalizada pixel a pixel.
  2. 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.
  3. 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 🙂

Tags: , , , ,
No comments yet.

Leave a Comment

Solve this *