2025-07-04
MAX7219 y matriz de leds
En esta entrada veremos como funciona una matriz de leds y como hacerla funcionar con un MAX7219.
¿Qué es un matriz de led?
Una matriz de leds es un dispositivo electrónico compuesto de leds colocados de forma cuadrada (o rectangular) formando filas y columnas. Los leds que forman una fila tienen un lado común, ánodo o cátodo (según configuración) y el otro lado se unen pero esta vez por columnas. En la siguiente imagen vemos un ejemplo:
Como vemos, por cada fila se han unido los cátodos, mientras que en cada columna se han unido los ánodos.
Con esta distribución, si colocamos un nivel alto en una columna y un nivel bajo en una fila se encenderá el led correspondiente.
También se puede hacer un montaje con los diodos invertidos:
En este caso, el niveles de tensión se invierten (nivel alto en fila y nivel bajo en columna) para conseguir el mismo efecto.
Generalmente montar esto en placa suele ser engorroso y complicado y los fabricantes montan módulos donde los leds vienen encapsulados, teniendo disponibles los pines que conforman las columnas y las filas.
Uno de los módulos mas usados en electrónica es el 1088as que se compone de 8 filas y 8 columnas conformando una matriz de 64 leds de 3mm de diámetro.
¿Cómo hacemos funcionar el módulo 1088as?.
Este módulo tiene los cátodos comunes por columnas mientras que las filas son los ánodos, esto quiere decir que para encender un led debemos poner en la columna correspondiente un nivel alto y en la fila un nivel bajo:
Lo primero que nos viene a la cabeza es conectar nuestro Arduino, manejando las columnas con unos pines y las filas con otros, lo mismo que se muestra en la siguiente imagen:
Pero este sistema tiene pegas. Cuando ponemos una columna a HIGH el pin correspondiente debe ser capaz de suministrar corriente para iluminar todos los leds de cada fila que se va a encender. Y lo mismo ocurre para todas las columnas que en ese momento se encienden. Teniendo en cuenta las limitaciones de corriente que los microcontroladores pueden "suministrar" se hace necesario el uso de drivers de corriente.
Generalmente, para usar este tipo de displays se aprovecha la persistencia de la visión. Nuestro ojo retiene una imagen durante un tiempo, si la imagen cambia en menor tiempo da la sensación de que la imagen es continua. Esto es lo que se hace en los televisores o monitores, donde se muestra una pantalla completa durante 25 fotogramas por segundo, dando la sensación de continuidad.
Si encendemos la columnas una detrás de otra a una velocidad alta, nuestro ojo pensará que todos los diodos leds estarán encendidos.
Aquí dejo el pinout del display:
El MAX7219.
Como vimos antes debemos aprovechar la persistencia de la visión y usar muchos pines del microcontrolador para controlar un display. Sin embargo el MAX7219 se encarga de todo ello y solo con 3 pines usando el bus SPI.
En realidad el MAX7219 es un controlador de displays de 7 segmentos, capaz de controlar hasta 8 displays, pero también se puede usar para controlar un display como el 1088as.
Su pinout es el siguiente:
Los pines DIN, LOAD y CLK son la entrada de datos, carga y reloj. En el bus SPI son la señal MOSI, SS y SCK. En realidad no es necesario el uso del bus SPI si no que simplemente se puede usar bitbanging.
Cada pin Dx es el encargado de controlar el digito en la posición "x". Mientras que SEGx se corresponde al segmento del display correspondiente.
DOUT es la señal que nos permite conectar en cascada varios MAX7219 de tal forma que aumentamos el número de displays conectados y controlados por la misma línea. DOUT irá conectado a DIN del siguiente.
El pin ISET se encarga de controlar la corriente que circula por los leds haciendo que luzcan mas o menos según su valor.
Además de esta resistencia solo se necesita un condensador de desacoplo para tener en marcha el circuito. Incluso ya venden módulos con la versión SMD del chip que tiene los conectores para acoplar el 1088as directamente encima, siendo justamente del tamaño de este.
E incluso tenemos que nos venden el conjunto con cuatro módulos conectados directamente para usar.
Programación del chip
Internamente el MAX7219 posee una serie de direcciones donde se guarda la información de funcionamiento.
El mapa de memoria es el siguiente:
| 0x00 | No operación. |
| 0x01 | Digito 0. |
| 0x02 | Digito 1. |
| 0x03 | Digito 2. |
| 0x04 | Digito 3. |
| 0x05 | Digito 4. |
| 0x06 | Digito 5. |
| 0x07 | Digito 6. |
| 0x08 | Digito 7. |
| 0x09 | Modo decodificación |
| 0x0A | Intensidad |
| 0x0B | Limite de escaneo. |
| 0x0C | Apagado. |
| 0x0F | Test. |
La primera dirección no hace nada, es un no operación, cuando lo recibe se queda tal y como estaba el chip.
Las siguientes direcciones son los dígitos del 0 al 8. En estas direcciones se pone la información de los segmentos, que ya veremos mas adelante.
La posición 0x09 es el modo de decodificación, si se activa, debemos escribir el valor BCD del display. Para matrices de leds no se usa.
El registro 0x0A indica la intensidad del display, podemos poner un valor del 0 al 15, siendo 0 apagado y 15 encendido al máximo.
El limite de escaneo (0x0A) limita el número de dígitos a mostrar, en nuestro caso debe estar "desactivado".
El registro 0x0C apaga el módulo.
El registro 0x0F realiza un test, encendiendo todos los leds.
Cada posición de digito especifica una fila del display. Mientras que cada segmento representa una columna. Así que si queremos encender un led en especifico debemos enviar un byte con el bit de la columna que queremos a 1 y enviarlo a dicha dirección de digito. En código sería así.
digitalWrite(cspin,LOW); SPI.transfer(direccion); SPI.transfer(valor); digitalWrite(cspin,HIHG);
Así podemos enviar un comando al chip de cualquier tipo (no operación, intensidad, poner un digito, etc.).
He escrito una librería simple para el manejo de varios displays en cadena.
#ifndef _ledmatrix_h_ #define _ledmatrix_h_ #include <SPI.h> class ledmatrix { private: uint8_t cspin; // Pin CS uint8_t numdevices; // Número de chips en cadena. uint8_t *bufer; // Búfer de memoria. public: ledmatrix(uint8_t _cspin, uint8_t _numdevices=4); void sendToAll(byte reg, byte data); void sendToSpecific(int target, byte reg, byte data); void setPixel(int x, int y, bool state); void display(); void clear(); uint8_t reverse(uint8_t b); void setIntensity(uint8_t i); }; ledmatrix::ledmatrix(uint8_t _cspin, uint8_t _numdevices) { cspin = _cspin; numdevices = _numdevices; bufer = new uint8_t [ numdevices * 8 ]; for (int i=0; i<numdevices*8; i++) bufer[i]=0; pinMode(cspin, OUTPUT); digitalWrite(cspin, HIGH); SPI.begin(); //SPI.setClockDivider(SPI_CLOCK_DIV16); for (int i=0; i<numdevices; i++) { sendToAll(0x0f, 0x00); // Display test off. sendToAll(0x0C, 0x01); // normal operation. sendToAll(0x0B, 0x07); // all rows. sendToAll(0x0A, 0x00); // intensity. sendToAll(0x09, 0x00); // no decode mode. } } void ledmatrix::sendToAll(byte reg, byte data) { digitalWrite(cspin, LOW); for (int i = 0; i < numdevices; i++) { SPI.transfer(reg); SPI.transfer(data); } digitalWrite(cspin, HIGH); } void ledmatrix::sendToSpecific(int target, byte reg, byte data) { digitalWrite(cspin, LOW); // Se envÃan desde el último (más lejano al Arduino) al primero for (int i = numdevices - 1; i >= 0; i--) { if (i == target) { SPI.transfer(reg); SPI.transfer(data); } else { SPI.transfer(0x00); // NOP SPI.transfer(0x00); } } digitalWrite(cspin, HIGH); } void ledmatrix::clear() { for (int r=0; r<=8; r++) sendToAll(r, 0x00); for (int r=0; r<8*numdevices; r++) bufer[r]=0; } void ledmatrix::setPixel(int x, int y, bool state) { if (x < 0 || x >= numdevices*8 || y < 0 || y >= 8) return; int byteIndex = (x / 8) + (y * numdevices); byte bitPos = 7 - (x % 8); if (state) bufer[byteIndex] |= (1 << bitPos); else bufer[byteIndex] &= ~(1 << bitPos); } void ledmatrix::display() { for (int row = 0; row < 8; row++) { digitalWrite(cspin, LOW); for (int m = numdevices - 1; m >= 0; m--) { byte address = (7-row) + 1; // MAX7219: filas 1–8 byte data = bufer[m + row * numdevices]; data = reverse(data); SPI.transfer(address); SPI.transfer(data); } digitalWrite(cspin, HIGH); } } void ledmatrix::setIntensity(uint8_t i) { sendToAll(0x0a, i); } uint8_t ledmatrix::reverse(uint8_t b) { return (uint8_t)(((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); } #endif
Referencias
Gammon Forum - Interfacing LED displays with the MAX7219 driver.
Arduino Bluetooth controled 8x32 LED matrix.
Librerias: