Cómo Conectar una Pantalla LCD 128×64 con Controlador ST7920 a Arduino

Las pantallas LCD de caracteres 16×2 cumplen de sobra para mostrar un par de líneas de texto, pero en cuanto quieres dibujar un gráfico, un icono o una barra de progreso, se quedan cortas. Ahí es donde entra el clásico LCD gráfico 128×64 con controlador ST7920 (a veces etiquetado simplemente como «12864»): una pantalla monocroma barata, muy extendida en el mundo maker, capaz de dibujar cualquier píxel de sus 8.192 puntos de forma independiente. En este tutorial vas a ver cómo cablearla a un Arduino Uno usando su modo serie (solo 3 cables de datos), cómo instalar la librería U8g2 y cómo pasar de un simple «Hola Mundo» a un panel con textos, formas, gráficas de barras y hasta tu propio logo en bitmap.

Puntos clave

  • El modo serie del ST7920 simplifica mucho el cableado: con el pin PSB a GND solo necesitas 3 líneas de datos además de alimentación.
  • La RAM del Arduino Uno es el principal cuello de botella: un buffer completo de pantalla ocupa la mitad de sus 2 KB de SRAM; el modo de «buffer por páginas» de U8g2 lo resuelve.
  • El contraste mal ajustado es la causa nº1 de «pantalla en blanco»: antes de sospechar del cableado, prueba a girar el potenciómetro.
  • drawStr() posiciona el texto por su línea base, no por el borde superior: con Y=0 se corta la parte alta de las letras.

¿Qué es una pantalla gráfica 128×64?

A diferencia de un LCD de caracteres, donde solo puedes escribir letras en posiciones fijas de una rejilla, un LCD gráfico 128×64 controla cada uno de sus píxeles de forma individual: 128 columnas por 64 filas, 8.192 píxeles en total. Como cada píxel solo puede estar encendido o apagado, una imagen completa de pantalla ocupa exactamente 8.192 bits, es decir, 1.024 bytes. Ese dato importa más de lo que parece: un Arduino Uno solo tiene 2 KB de SRAM, así que un buffer de pantalla completo se comería la mitad de toda su memoria disponible. Más adelante veremos cómo la librería U8g2 resuelve esto con su modo de «buffer por páginas».

Los tres modos de comunicación del ST7920

El controlador ST7920 admite tres formas de hablar con un microcontrolador:

  • Paralelo de 8 bits: rápido, pero exige muchísimos pines de datos.
  • Paralelo de 4 bits: un compromiso intermedio, sigue usando bastantes pines.
  • Serie síncrono: solo necesita reloj, datos y selección de chip.

El pin PSB es el que decide el modo: conectado a VCC activa el modo paralelo, conectado a GND activa el modo serie. En este tutorial usamos siempre el modo serie, tanto porque libera muchísimos pines del Arduino como porque es el que espera U8g2 de forma más directa. Merece la pena aclarar que el «modo serie» del ST7920 no es SPI estándar (el orden y el empaquetado de bits son distintos), pero la librería U8g2 se encarga de toda esa traducción por ti, así que en la práctica no tienes que pensar en ello.

Qué necesitas

  • Un Arduino Uno o una placa compatible.
  • Un módulo LCD 128×64 con controlador ST7920 (a menudo vendido como «12864B»).
  • Un potenciómetro o trimmer de 10 kΩ para el contraste.
  • Una resistencia pull-up de 10 kΩ para el pin de reset.
  • Una resistencia de 100–220 Ω para el retroiluminado, si tu módulo no la lleva ya incorporada.
  • Cables de conexión y una protoboard.

Muchos módulos ya integran en su propia placa tanto el ajuste de contraste como la resistencia del retroiluminado. Antes de añadir componentes extra, revisa la serigrafía de tu módulo concreto: puede que ya lo traiga resuelto.

Pinout del ST7920 128×64

PinNombreFunción
1GNDMasa
2VCCAlimentación (normalmente 5V)
3V0Tensión de contraste (cursor del potenciómetro)
4RS / CSSelección de chip (serie) / registro (paralelo)
5R/W / SIDDato serie (SID) en modo serie
6E / SCLKReloj serie (SCLK) en modo serie
7–14DB0–DB7Bus de datos paralelo; sin uso en modo serie
15PSBSelector de modo: VCC = paralelo, GND = serie
16NCSin conexión
17RSTReset (activo en bajo); no debe quedar al aire
18VOUTSalida de tensión negativa interna, referencia del contraste
19BLAÁnodo del retroiluminado (+)
20BLKCátodo del retroiluminado (−)

Como en modo serie no usamos el bus paralelo, los pines 7 a 14 quedan simplemente sin conectar.

Cómo cablear el Arduino Uno al LCD

ST7920Arduino UnoFunción
GNDGNDMasa común
VCC5VAlimentación
V0Cursor del potenciómetroContraste
RS / CSD10Selección de chip
R/W / SIDD11Dato serie
E / SCLKD13Reloj serie
PSBGNDActiva el modo serie
RST5V a través de una resistencia de 10 kΩMantiene el reset inactivo
BLA5V a través de una resistencia de 100–220 ΩRetroiluminado (+)
BLKGNDRetroiluminado (−)
Diagrama de cableado entre un Arduino Uno y una pantalla LCD 128x64 con controlador ST7920 en modo serie

Con PSB a GND, el ST7920 solo necesita tres líneas de datos: chip select, dato serie y reloj.

El circuito del potenciómetro de contraste

El pin V0 no se conecta directamente a nada fijo: recibe la tensión del cursor de un potenciómetro de 10 kΩ, con sus dos extremos entre 5V y GND. Girando el potenciómetro cambias esa tensión intermedia y, con ella, el contraste del panel.

Diagrama del circuito con potenciometro de 10 kilohmios para ajustar el contraste de una pantalla LCD ST7920

El cursor del potenciómetro entrega a V0 un valor intermedio entre 0V y 5V: ese es el ajuste de contraste.

Un consejo práctico: tras el primer montaje, gira el potenciómetro despacio de un extremo a otro observando la pantalla. Si el módulo recibe alimentación y el cableado de datos es correcto, en algún punto del recorrido deberían aparecer bloques oscuros o el texto de la prueba. Un contraste mal ajustado es, con diferencia, la causa más habitual de que una pantalla «no funcione» cuando en realidad sí lo hace.

Por qué D10, D11 y D13

Esos tres pines no son arbitrarios: coinciden con las señales estándar CS, MOSI y SCK del Arduino Uno. El constructor de software-SPI de U8g2 te permite indicar explícitamente qué pines usar, así que si tu proyecto ya tiene esos pines ocupados, puedes elegir otros distintos siempre que actualices el constructor en el código.

Instalar la librería U8g2

  1. Abre el IDE de Arduino.
  2. Ve a Programa > Incluir Librería > Gestionar Bibliotecas.
  3. Busca «U8g2».
  4. Instala la librería «U8g2 by olikraus».
  5. Si los ejemplos no aparecen enseguida en el menú, reinicia el IDE.

U8g2 incluye constructores distintos según el controlador, el método de comunicación y el tipo de buffer de memoria. Para el ST7920 concretamente, busca los nombres que empiezan por U8G2_ST7920_128X64, tanto para SPI por software como por hardware o para el bus paralelo.

Tu primer «Hola Mundo» en la pantalla

Con el cableado hecho y la librería instalada, este sketch conecta con el LCD por SPI software y escribe dos líneas de texto:

#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>

U8G2_ST7920_128X64_1_SW_SPI u8g2(
  U8G2_R0,
  /* clock=*/ 13,
  /* data=*/  11,
  /* cs=*/    10,
  /* reset=*/ U8X8_PIN_NONE
);

void setup() {
  u8g2.begin();
}

void loop() {
  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_ncenB14_tr);
    u8g2.drawStr(5, 24, "Descubre");
    u8g2.drawStr(20, 48, "Arduino");
  } while (u8g2.nextPage());
  delay(1000);
}

Cómo se construye el objeto u8g2

El nombre del constructor ya cuenta buena parte de la historia: U8G2 (la API), ST7920 (el controlador), 128X64 (la resolución), 1 (buffer por páginas) y SW_SPI (SPI implementado por software, sin usar el periférico SPI del chip). Los parámetros que recibe son, en orden: la rotación, el pin de reloj, el pin de datos, el pin de selección de chip y el pin de reset.

U8G2_R0 es la orientación normal; U8G2_R1, U8G2_R2 y U8G2_R3 giran la imagen 90°, 180° y 270° respectivamente, muy útil si montas la pantalla girada dentro de una carcasa.

Inicializar la pantalla

u8g2.begin() envía la secuencia de comandos de arranque al controlador, limpia la pantalla y la deja en modo normal, lista para dibujar.

Entendiendo el bucle de página

Con el constructor «1» (buffer por páginas), todo lo que quieras dibujar tiene que ir dentro de este patrón:

u8g2.firstPage();
do {
  // aquí van tus instrucciones de dibujo
} while (u8g2.nextPage());

La ventaja es que U8g2 solo mantiene en RAM una franja pequeña de la pantalla a la vez, y va enviando el resto por partes; la contrapartida es que todas las instrucciones de dibujo se ejecutan varias veces, una por cada franja o «página» que se procesa internamente. No es un problema para la mayoría de sketches, pero conviene saber que ese bucle se repite.

Elegir una fuente

La llamada u8g2.setFont(...) selecciona qué tipografía usar antes de escribir texto. Algunas fuentes compactas y muy prácticas para pantallas pequeñas:

  • u8g2_font_5x7_tr
  • u8g2_font_6x10_tr
  • u8g2_font_ncenB08_tr
  • u8g2_font_ncenB14_tr

Las fuentes más grandes ocupan más memoria flash del Arduino, así que si tu sketch ya anda justo de espacio, merece la pena probar con una fuente más compacta.

El sistema de coordenadas de la pantalla

La esquina superior izquierda es el origen (0, 0); el eje X va de 0 a 127 y el eje Y de 0 a 63, creciendo hacia abajo.

Diagrama del sistema de coordenadas de una pantalla LCD grafica 128x64 con el origen arriba a la izquierda

El origen está arriba a la izquierda; el eje Y crece hacia abajo, al contrario que en unos ejes matemáticos habituales.

Hay un detalle que suele pillar a todo el mundo la primera vez: en drawStr(x, y, texto), la Y representa la línea base del texto, no su borde superior. Si usas Y=0, la parte alta de las letras queda cortada fuera de la pantalla porque no hay sitio por encima de la línea base para dibujarla.

Buffer completo frente a buffer por páginas

El sufijo del constructor también controla cuánta memoria RAM reserva U8g2 para la pantalla. Cambiando de:

U8G2_ST7920_128X64_1_SW_SPI u8g2(...);

a:

U8G2_ST7920_128X64_F_SW_SPI u8g2(...);

pasas del buffer por páginas (constructor «1») al buffer completo (constructor «F»), y entonces el patrón de dibujo cambia a:

u8g2.clearBuffer();
// instrucciones de dibujo
u8g2.sendBuffer();

El buffer completo simplifica mucho las animaciones, porque solo dibujas una vez por fotograma en lugar de varias veces por el bucle de páginas, pero exige reservar unos 1.024 bytes de RAM de forma permanente.

Grafico comparando el uso de memoria RAM en un Arduino Uno entre el buffer completo y el buffer por paginas de la libreria U8g2

En un Arduino Uno, el buffer completo se lleva la mitad de la SRAM disponible; el buffer por páginas apenas roza el 6%.

Para un Arduino Uno o Nano, con solo 2 KB de SRAM totales, el buffer por páginas suele ser la opción más segura: deja mucha más memoria libre para tus variables, arrays de lecturas de sensores o buffers de comunicación. Si trabajas con un ESP32 o una placa con más RAM, el buffer completo deja de ser un problema y puede simplificarte bastante el código de animaciones.

Dibujar formas geométricas

Además de texto, U8g2 incluye funciones de dibujo vectorial: líneas, rectángulos, círculos, triángulos… Este sketch dibuja un marco, una cabecera con texto invertido, un círculo, un disco relleno, un triángulo y un rectángulo de esquinas redondeadas:

void loop() {
  u8g2.firstPage();
  do {
    u8g2.drawFrame(0, 0, 128, 64);

    u8g2.drawBox(0, 0, 128, 14);
    u8g2.setDrawColor(0);
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(38, 11, "GRAFICOS");
    u8g2.setDrawColor(1);

    u8g2.drawCircle(24, 40, 12);
    u8g2.drawDisc(56, 40, 10);
    u8g2.drawTriangle(78, 50, 90, 24, 102, 50);
    u8g2.drawRFrame(6, 24, 0, 0, 0);
  } while (u8g2.nextPage());
}

Algunas de las funciones de dibujo más usadas de la librería:

FunciónQué dibuja
drawPixel(x, y)Un único píxel
drawLine(x1, y1, x2, y2)Una línea entre dos puntos
drawHLine / drawVLineUna línea horizontal o vertical
drawFrame(x, y, ancho, alto)Un rectángulo hueco
drawBox(x, y, ancho, alto)Un rectángulo relleno
drawCircle / drawDiscUn círculo hueco o relleno
drawTriangle(…)Un triángulo relleno
drawRFrame(…)Un rectángulo hueco de esquinas redondeadas

Crear un panel de sensor analógico

Un caso de uso muy habitual es leer un sensor analógico y mostrar su valor tanto en número como en una barra gráfica. Este ejemplo lee un potenciómetro en A0 (puedes sustituirlo por un sensor de temperatura, luz o lo que necesites) y dibuja un pequeño panel:

const int pinSensor = A0;

void loop() {
  int valorCrudo   = analogRead(pinSensor);
  int porcentaje   = map(valorCrudo, 0, 1023, 0, 100);
  int anchoBarra   = map(valorCrudo, 0, 1023, 0, 122);

  u8g2.firstPage();
  do {
    u8g2.drawBox(0, 0, 128, 12);
    u8g2.setDrawColor(0);
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(2, 10, "SENSOR A0");
    u8g2.setDrawColor(1);

    u8g2.setFont(u8g2_font_ncenB08_tr);
    u8g2.setCursor(2, 28);
    u8g2.print(valorCrudo);
    u8g2.setCursor(70, 28);
    u8g2.print(porcentaje);
    u8g2.print("%");

    u8g2.drawFrame(2, 40, 124, 12);
    u8g2.drawBox(3, 41, anchoBarra, 10);

    u8g2.setFont(u8g2_font_5x7_tr);
    u8g2.drawStr(2, 62, "0");
    u8g2.drawStr(60, 62, "50");
    u8g2.drawStr(118, 62, "100");
  } while (u8g2.nextPage());
}

Un detalle importante: los cálculos de valorCrudo, porcentaje y anchoBarra están fuera del bucle de página, hechos una sola vez por vuelta de loop(). Como el bucle de página ejecuta las instrucciones de dibujo varias veces, si leyeras el sensor dentro de él podrías obtener valores ligeramente distintos entre una pasada y otra, con el consiguiente parpadeo o desajuste visual.

Este mismo esquema sirve para mostrar temperatura, voltaje de batería, velocidad de un motor, nivel de un depósito, presión, intensidad de luz, intensidad de señal o la posición de un servo: cualquier magnitud que puedas leer como número.

Mostrar una imagen bitmap

Para mostrar un logo o un icono, primero hay que convertir la imagen a un array de bytes en C. Existen varias herramientas online para esto (busca «LCD bitmap converter»); el resultado es un array como este:

#define logo_width 64
#define logo_height 32

static const unsigned char logo_bits[] U8X8_PROGMEM = {
  // datos hexadecimales generados por la herramienta
};

Recomendaciones para preparar la imagen

  • Usa imágenes simples, en blanco y negro puro.
  • Elimina detalles finos: se pierden a baja resolución.
  • Ajusta el tamaño al área real donde se va a mostrar.
  • Busca buen contraste entre el motivo y el fondo.
  • Evita convertir directamente una fotografía a color: el resultado en monocromo suele ser ilegible.

Como referencia de tamaños: un logo a pantalla completa serían 128 x 64 px; un logo pequeño, unos 64 x 32 px.

Diagrama que explica como centrar un logo bitmap de 64 por 32 pixeles en una pantalla LCD de 128 pixeles de ancho

Para centrar un logo de 64 px en una pantalla de 128 px, el margen a cada lado es (128 − 64) / 2 = 32 px.

void loop() {
  u8g2.firstPage();
  do {
    u8g2.drawXBM(32, 4, logo_width, logo_height, logo_bits);
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(28, 50, "DescubreArduino");
    u8g2.drawFrame(14, 56, 100, 6);
    u8g2.drawBox(14, 56, 60, 6);
  } while (u8g2.nextPage());
}

Un detalle que suele confundir: si usas drawBitmap() en lugar de drawXBM(), el parámetro de ancho se espera en bytes, no en píxeles (ancho_px / 8). Un logo de 64 píxeles de ancho equivale, en ese caso, a un valor de 8. Un bitmap de 64×32 ocupa 256 bytes en la memoria del programa: nada comparado con la RAM de vídeo, pero conviene tenerlo en cuenta si guardas varias imágenes.

Usar SPI por hardware

Si prefieres aprovechar el periférico SPI del propio chip en lugar de bit-banging por software, existe un constructor equivalente:

U8G2_ST7920_128X64_1_HW_SPI u8g2(
  U8G2_R0,
  /* cs=*/ 10,
  /* reset=*/ U8X8_PIN_NONE
);

En un Arduino Uno, el cableado no cambia: SID sigue yendo a D11 (MOSI), SCLK a D13 (SCK) y CS a D10. La ventaja del SPI por hardware es, potencialmente, una transferencia de pantalla algo más rápida; la desventaja es que el constructor por software es más fácil de entender y te permite elegir libremente cualquier pin digital.

Controlar el pin de reset desde el Arduino

En lugar de dejar RST fijo a 5V con una resistencia pull-up, puedes gestionarlo desde un pin digital del Arduino (por ejemplo D8):

U8G2_ST7920_128X64_1_SW_SPI u8g2(
  U8G2_R0,
  /* clock=*/ 13,
  /* data=*/  11,
  /* cs=*/    10,
  /* reset=*/ 8
);

Nunca dejes el pin RST al aire (sin conectar): un reset flotante provoca comportamientos de arranque inconsistentes, con la pantalla funcionando unas veces sí y otras no.

Solución de problemas comunes

El retroiluminado se enciende, pero no aparece nada

  • Ajusta el potenciómetro de contraste, despacio, de un extremo a otro.
  • Verifica que PSB está conectado a GND (modo serie).
  • Repasa el cableado de CS, SID y SCLK.
  • Confirma que el LCD y el Arduino comparten la misma masa.
  • Comprueba que RST está en nivel alto (o correctamente controlado por un pin).
  • Verifica que usas el constructor U8g2 correcto para tu tipo de conexión.

Solo aparecen bloques oscuros o píxeles aleatorios

Suele apuntar a: contraste incorrecto, masa floja, PSB al aire, orden de pines equivocado en el constructor, reset inestable, cables demasiado largos o una alimentación de 5V débil. Como norma general, mantén los cables de reloj, datos y masa razonablemente cortos.

La imagen sale invertida o del revés

Cambia el parámetro de rotación del constructor: U8G2_R0 (normal), U8G2_R1 (90°), U8G2_R2 (180°) o U8G2_R3 (270°).

El bitmap se ve corrupto

  • Comprueba que el ancho y el alto declarados coinciden con los de la imagen generada.
  • Verifica que pasas el nombre correcto del array a la función de dibujo.
  • Confirma que el array está guardado con U8X8_PROGMEM.
  • Revisa que la imagen usa el orden de bytes esperado (formato XBM).
  • Asegúrate de que las coordenadas de dibujo no se salen de la pantalla.

La pantalla deja de funcionar al añadir otro dispositivo SPI

Cada dispositivo SPI necesita su propio pin de selección de chip independiente. Verifica que los dispositivos inactivos mantienen su CS en el estado correcto, y confirma que ningún otro periférico interfiere con el reloj o los datos del ST7920 durante la inicialización.

Preguntas frecuentes

¿Puedo alimentar el ST7920 a 3,3V?

La mayoría de módulos 12864 están pensados para 5V. Revisa la hoja de datos o la serigrafía de tu módulo concreto. Si tu microcontrolador trabaja a 3,3V, puede que necesites un adaptador de niveles según los umbrales de entrada del LCD.

¿Puedo usarlo con un Arduino Mega?

Sí. El constructor de SPI por software funciona con cualquier pin digital; solo tienes que actualizar el cableado y los valores del constructor. El SPI por hardware, en cambio, usa los pines dedicados del Mega.

¿Y con un Arduino Nano?

Sí, sin cambios relevantes: el Nano clásico usa el mismo ATmega328P y la misma disposición de pines D10, D11 y D13 que el Uno.

¿Puede el ST7920 mostrar imágenes en color?

No. Es monocromo: cada píxel está encendido o apagado. El tono azul, verde o amarillo que a veces se ve viene del propio panel LCD y de su retroiluminado, no de un control de color por píxel.

¿Puedo mostrar gráficas en tiempo real?

Sí. Guarda las lecturas del sensor en un array y dibuja líneas entre puntos consecutivos con drawLine(). En un Arduino Uno, conviene mantener ese histórico pequeño para no consumir demasiada SRAM.

¿Puedo usar números más grandes?

Sí, U8g2 incluye fuentes numéricas de mayor tamaño, pero ocupan más memoria flash y más espacio en pantalla.

¿Por qué mi módulo pone «12864B» en vez de «ST7920»?

«12864B» es la designación comercial del módulo o de la placa, no del controlador. Revisa la serigrafía del chip, la documentación del vendedor o los nombres de los pines: la presencia de PSB, SID, SCLK y VOUT suele indicar un módulo de estilo ST7920, pero conviene confirmarlo antes de conectar nada.

Conclusión

Un LCD gráfico 128×64 con ST7920 abre muchas puertas frente a una pantalla de caracteres: menús, paneles con varias magnitudes a la vez, gráficas, iconos y una interfaz bastante más cuidada, todo con un módulo barato y fácil de encontrar. El modo serie mantiene el cableado manejable (solo tres líneas de datos), y la librería U8g2 se encarga tanto de la comunicación de bajo nivel como de todas las funciones de dibujo. Con el «Hola Mundo» ya funcionando, el siguiente paso natural es enchufar un sensor real al panel de ejemplo y, cuando le cojas el punto, crear tu propio logo con un conversor de bitmaps. La resolución de 128×64 se queda corta comparada con las pantallas modernas, pero con las fuentes y los gráficos adecuados da para interfaces embebidas perfectamente cuidadas: medidores digitales, estaciones meteorológicas, monitores de batería, controladores con menú, registradores de datos, paneles de control de motores o instrumentos de prueba portátiles.

Pin It on Pinterest

Shares