Micropython: Conectando un display LCD a un Wemos D1 mini usando I2C

En un artículo anterior explicaba como ampliar la cantidad de E/S (GPIO) de una placa D1 mini (o cualquier otra con ESP8266) usando el circuito integrado PCF8574 y mencione que había módulos con este chip creados específicamente para controlar displays LCD, los que habitualmente utilizan muchos pines. En este artículo veremos como usar uno de ellos para añadir este tipo de displays a nuestros proyectos en Micropython.

Los display LCD fueron utilizados durante muchos años como pantallas para una infinidad de proyectos. Desde los mas simples de una o dos líneas de texto hasta los mas grandes, con capacidad de mostrar gráficos o incluir una capa sensible al tacto (touchscreen) en distintas variedades de formatos, marcas y colores. En la actualidad se está popularizando el uso de pantallas OLED, que son capaces de mostrar gráficos de mayor resolución con gran nitidez, y también podemos usar algunos modelos de pantallas de tinta electrónica (e-ink o e-paper), tecnologías que ofrecen algunas ventajas pero también inconvenientes. El tiempo dirá si desplazan totalmente a los LCD o si se convertirán en una opción para algunas aplicaciones. En este artículo nos centraremos en los LCD de texto y veremos como controlarlos con el auxilio de un módulo con conexión I2C basado en el PCF8574.

LCD de texto

Aunque existe una gran variedad de display LCD, muchos de ellos comparten el mismo chip controlador: el HD44780 de Hitachi o una variante mas moderna del mismo. Este chip se hizo tan popular que estableció una especie de estándar de hecho en la interface eléctrica y de programación de los LCD, haciéndolos bastante compatibles, salvo algunas pequeñas diferencias introducidas por algunos fabricantes. Esto permitió el desarrollo de mucho código y librerías que nos facilitan enormemente la utilización de estos displays, así como los módulos I2C que nos permiten controlarlos con sólo un par de pines.

Fig. 1. El chip HD44780

Interface

Gracias a la compatibilidad de hecho antes mencionada, los display de texto presentan una interfaz electrica, unos pines de conexión similares entre distintos modelos, que tienen una distribución como la que se aprecia en la siguiente imagen (aunque siempre conviene asegurarse revisando la hoja de datos antes de conectar nuestro LCD):

Fig. 2. Conexiones de un LCD de texto

Es una gran cantidad de señales, entre las que tenemos:

GND y VCC: Alimentación (Masa y 5V)

Vo: Tensión de contraste

RS, RW, EN: Señales de control para acceder, leer y escribir en el LCD

D0 a D7: Señales de datos para enviar comandos y datos al display.

BL+, BL-: Alimentación del backlight (iluminación posterior). En algunos modelos estos pines no tienen conexión y el backlight se alimenta empleando otro conector.

Aunque existe la posibilidad de usar un modo en el que nos comunicamos con el LCD usando sólo 4 bits de datos, en lugar de 8, sigue siendo una gran cantidad de señales, sobre todo para un micro con una cantidad limitada de GPIO.

El módulo I2C

El módulo que utilizaremos viene a resolver el problema de la cantidad de pines del LCD, sobre todo en aquellos micros que tienen pocos pines disponibles, ya que cuenta con un circuito integrado PCF8574 que, como ya vimos antes, nos permite tener 8 pines bidireccionales que podemos controlar con sólo dos señales del bus I2C. Las salidas del PCF8574 tienen la misma disposición del LCD, por lo que la conexión es directa, pudiendo montarse frente al LCD o directamente sobre los mismos pines del conector. Además de los pines que van al LCD, la placa tiene otro conector de 4 pines por donde debemos alimentarlo (VCC y GND) y enviar los datos de control (SCL y SDA).

Fig. 3. Módulo I2C con PCF8574

También se incluye un preset para controlar el contraste del display y tres jumpers para soldar marcados A0, A1 y A2 que definen la dirección de la placa en el bus I2C según la siguiente tabla:

Fig. 4. Disposición de los jumper para fijar la dirección I2C

Alimentación

Si bien el PCF8574 incluido en la placa puede funcionar con 3,3V como un Nodemcu o un Wemos D1, el display usualmente no (hay algunos modelos que se pueden alimentar con 3,3V pero no son fáciles de conseguir). Por eso alimentaremos el módulo I2C con 5V. Las conexiones SCL y SDA pueden funcionar correctamente aunque las señales provenientes del micro no lleguen a 5V

Conexiones

Para esta demostración utilizaré un LCD de 2 filas y 16 columnas reciclado de un antiguo proyecto. Las conexiones son muy sencillas, ya que el LCD se conecta de forma directa al conector del módulo y sólo queda conectar VCC a 5V, GND con GND del D1 Mini y las señales SDA y SCL a D2 y D1 respectivamente. El jumper del backlight debe colocarse para iluminar el LCD. No son necesarias resistencias de pull-up en SDA y SCL porque el módulo ya las incluye.

Fig. 5. Conexiones

En la siguiente imagen se puede ver el montaje que utilice para las pruebas:

Fig. 6. El circuito armado

Programación

Hay mas de un módulo que podemos emplear para controlar esta placa desde Micropython. Para este artículo elegí el de Dave Hylands, que se puede encontrar en este repositorio, porque tiene muchas funciones para controlar el cursor, imprimir textos y utilizar caracteres definidos por el usuario.

En el mismo repositorio hay módulos para ESP8266, ESP32 y no sólo para soportar la placa I2C sino también para conectarse al LCD usando los bits de datos, tanto en el modo de 8 bits como de 4 bits.

Para este caso particular, donde emplearemos la conexión I2C usaremos dos módulos que se encuentran en la carpeta LCD del repositorio:

lcd_api.py – Implementa las funciones para controlar el LCD

esp8266_i2c_lcd.py – Implementa las funciones para comunicarse con la placa usando I2C

En nuestro programa utilizaremos los métodos definidos en lcd_api, que son los de mas alto nivel, que luego invocan a los que están contenidos en el otro módulo esp8266_i2c_lcd, que son los de mas bajo nivel e implementan los detalles de comunicación con la placa de expansión I2C.

Deberemos copiar ambos en la memoria de la D1 Mini. Usando Thonny, nos debe quedar algo así:

Fig. 7. Archivos en la memoria de la D1 Mini

Con los módulos en memoria ya estamos en condiciones de escribir un programa elemental para controlar el LCD.

Como siempre el primer paso es incluir los módulos que vamos a necesitar en nuestro código:

from time import sleep
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd

A continuación creamos (instanciamos) un objeto de la clase I2C para crear la interface, definiéndola en los pines D1 (SCL, GPIO5) y D2 (SDA, GPIO4) de la D1 Mini.

i2c = I2C (scl=Pin(5), sda=Pin(4), freq=400000)

El siguiente paso es crear el objeto correspondiente al LCD. Esto se hace a través del constructor I2cLcd que tiene el siguiente formato:

I2cLcd (interface_i2c, dirección_I2C, filas, columnas)

Donde:

interface_i2c: Nombre del objeto de la interface del bus I2C

Dirección_I2C: Dirección del LCD en el bus I2C (ver Fig. 4)

filas: Cantidad de filas del LCD

columnas: Cantidad de columnas del LCD

En este ejemplo, donde uso un LCD de 2×16, es decir 2 filas y 16 columnas y la dirección del módulo I2C es 0x27 (jumpers A0, A1 y A2 sin soldar), la llamada al constructor queda de la siguiente forma:

lcd = I2cLcd (i2c, 0x27, 2, 16)

A partir de aquí se pueden utilizar los métodos del objeto lcd recién creado para controlar al display. Este módulo nos provee de los siguientes métodos:

clear (): Limpia (borra) el LCD

show_cursor (): Hace visible el cursor

hide_cursor (): Hace invisible el cursor

blink_cursor_on (): Activa el parpadeo del cursor

blink_cursor_off (): Desactiva el parpadeo del cursor

display_on (): Activa el LCD

display_off (): Desactiva el LCD

move_to (cursor_x, cursor_y): Ubica el cursor en la posición cursor_x, cursor_y

putchar (char): Muestra el caracter char en el LCD y avanza el cursor una posición

putstr (string): Muestra la cadena de texto string en el LCD y avanza el cursor según la longitud de la cadena.

custom_char (location, charmap): Escribe la definición de un carácter redefinible en location (0 a 7). charmap es un objeto de tipo bytearray con los 8 patrones de bits. Luego explicaré con mas detalle como usar los caracteres redefinibles por el usuario (custom chars)

A continuación vemos un programa básico que muestra “Hola Mundo” en dos líneas en forma parpadeante:

from time import sleep
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
while (True):
    lcd.putstr("Hola\nMundo")
    sleep (3)
    lcd.clear()
    sleep (1)
Fig. 8. Hola mundo

En este otro ejemplo se muestra un contador, utilizando el método move_to para ubicar el cursor:

from time import sleep
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
contador = 0
lcd.putstr ("Cuenta:")
while (True):
    lcd.move_to (7,0)
    lcd.putstr (str(contador))  
    sleep (1)
    contador = contador + 1
    if (contador == 1000):
        contador = 0
Fig. 9. Contador

Caracteres especiales

Los controladores HD44780 y compatibles tienen los patrones de los caracteres a imprimir almacenados en una memoria ROM y por tanto no se pueden modificar. Sin embargo, existe un espacio de memoria denominado CGRAM (Character Generator RAM) donde podemos almacenar los patrones de hasta 8 caracteres definidos según nuestras necesidades.

Cada caracter se define con 8 bytes, de los cuales se emplean sólo los 5 bits mas bajos, puesto que cada uno se representa como una matriz de 5×8 puntos. En cada byte, un valor “1” ilumina un punto y un valor “0”. Lo apaga.

Por ejemplo, estos son los 8 bytes necesarios para definir el patrón de un corazón:

Fig. 10. Patrones para definir un corazón

Siempre conviene dejar la última línea en blanco porque es la posición que toma el cursor cuando está visible.

Luego debemos empaquetar estos valores en un objeto de tipo bytearray

corazon = bytearray ([0x00, 0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00])

Y luego lo pasamos al método custom_char especificando una dirección. Por ejemplo, la dirección 0:

lcd.custom_char(0, corazon)

Con esto no imprimimos nada en el LCD, solo guardamos la definición del caracter 0 a la tabla en la CGRAM. Para mostrarlo, debemos imprimirlo con putchar:

lcd.putchar (chr(0))

Con este método se pueden definir distintos caracteres y hasta hacer animaciones elementales.

from time import sleep
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
#Valores que definen el caracter
corazon = bytearray ([0x00, 0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00])
#Escribir en la CGRAM
lcd.custom_char(0, corazon)
lcd.putstr ("Hola")
#Imprimir el caracter
lcd.putchar (chr(0)) 
Fig. 11. Caracteres definidos por el usuario

Conclusión

En este artículo vimos el procedimiento básico para controlar un LCD de texto con una placa I2C basada en el PCF8574. Analizamos la forma de realizar la conexión con una placa Wemos D1 Mini y utilizamos un módulo de Micropython que incluye las funciones básicas para imprimir en el display.

Espero que el artículo les haya sido de utilidad y les sirva como punto de partida para ampliar el tema y profundizar en el mismo, así como experimentar, crear y practicar. Cualquier duda o sugerencia, pueden dejarla en la sección de comentarios.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Habilitar notificaciones OK No, gracias