Componentes · 19 min de leitura

MAX7219 com Arduino: matriz de LED e texto rolante com MD_Parola

Guia do MAX7219 com Arduino: ligue matrizes de LED 8x8 e módulos 4 em 1 de 8x32, faça texto rolante com MD_Parola, corrija texto espelhado com FC16_HW e veja códigos de relógio e ESP32.

O MAX7219 é um chip driver barato que controla sozinho 64 LEDs, e é o jeito mais fácil de usar uma matriz de LED com Arduino. Ele costuma aparecer em duas versões: um módulo com uma única matriz de pontos 8x8 e um módulo de matriz de pontos 4 em 1 de 8x32, com quatro matrizes lado a lado. O MAX7219 precisa de apenas 3 fios de sinal do Arduino (DIN, CS, CLK); ele mesmo varre as linhas centenas de vezes por segundo, ajusta o brilho e guarda o estado de cada LED. Assim o Arduino fica livre para texto rolante, relógios, placares ou animações, e você pode deixar o display tão comprido quanto quiser encadeando módulos um após o outro.

Neste guia você vai aprender como o MAX7219 funciona, como ligar o módulo a um Arduino Uno, a um ESP32 e a um Raspberry Pi Pico por SPI, como encadear módulos e como mostrar seu primeiro texto rolante com a biblioteca MD_Parola. Depois você vai resolver o problema do texto de cabeça para baixo ou espelhado, que pega a maioria dos iniciantes, escolhendo o tipo de hardware certo (FC16_HW), calcular o brilho e o consumo de energia, mostrar caracteres acentuados e personalizados e montar um relógio com módulo RTC e uma animação pixel por pixel. As últimas seções tratam do envio de mensagens pelo celular com um ESP32, do código MicroPython para o Pico e das soluções para os erros mais comuns.

Módulo de LED de matriz de pontos MAX7219 4 em 1 de 8x32
Módulo de matriz de pontos MAX7219 4 em 1: quatro matrizes 8x8 com um chip MAX7219 embaixo de cada uma; ele é alimentado pelo lado IN e se liga ao próximo módulo pelo lado OUT.

O que é o MAX7219 e como ele funciona?

Não dá para ligar 64 LEDs em pinos separados do Arduino, por isso as matrizes de LED usam multiplexação. Em uma matriz 8x8, os LEDs ficam em uma grade de 8 linhas e 8 colunas. A cada instante o MAX7219 ativa apenas um grupo de LEDs (um “digit” no datasheet, que em uma matriz é uma linha ou uma coluna), usa as outras vias para escolher quais dos oito LEDs desse grupo acendem e então passa para o grupo seguinte. Os oito grupos são varridos cerca de 800 vezes por segundo; o olho não acompanha essa velocidade, então a imagem parece fixa. Enquanto isso, o chip guarda o padrão de cada grupo em sua própria RAM de 8x8 bits, limita a corrente dos LEDs com um único resistor e ajusta o brilho em 16 níveis.

O Arduino conversa com o MAX7219 por uma interface serial de 3 fios que funciona de forma muito parecida com o SPI. Cada comando é um pacote de 16 bits: o byte superior escolhe o registrador a ser escrito e o byte inferior leva o valor. Enquanto o CS (LOAD no datasheet) está em LOW, os bits entram pelo DIN a cada borda de subida do CLK; quando o CS volta para HIGH, o pacote é gravado no registrador. É a mesma ideia do registrador de deslocamento 74HC595, mas, como o MAX7219 também cuida da varredura e da limitação de corrente, dá muito menos trabalho. Você encontra como o SPI funciona em geral em O que é SPI?.

CaracterísticaValor
Tensão de alimentação4,0–5,5 V (5 V na prática)
InterfaceSerial de 3 fios (DIN, CLK, CS), até 10 MHz
LEDs controlados64 (uma matriz 8x8 ou oito dígitos de 7 segmentos)
Taxa de varredura800 Hz típico (8 dígitos)
Corrente por segmentoDefinida pelo resistor RSET, 40 mA no máximo (normalmente 10 kΩ nos módulos)
Brilho16 níveis, ciclo de trabalho de 1/32 a 31/32
Corrente em shutdownCerca de 150 µA
Limiar de HIGH na entradaNo mínimo 3,5 V (com alimentação de 5 V)
Ligação em cascataSaída DOUT para a entrada DIN do próximo chip

O MAX7219 tem 14 registradores endereçáveis, mas os que você precisa para uma matriz de LED estão na tabela abaixo. As bibliotecas ajustam esses registradores por você; mesmo assim, saber para que servem ajuda muito na hora de procurar defeitos. Por exemplo, quando o chip é ligado pela primeira vez, ele começa no modo shutdown (desligamento), com o display apagado; por isso, se o código de inicialização não rodar, o módulo parece totalmente morto. O irmão dele, o MAX7221, tem os mesmos registradores, mas acrescenta um pino CS de fato compatível com SPI e drivers de segmento com taxa de variação (slew rate) limitada, que reduzem a interferência eletromagnética.

EndereçoRegistradorFunção
0x00No-OpDeixa este chip inalterado enquanto um pacote vai para outro chip da cadeia
0x01–0x08Digit 0–7O padrão de LEDs de 8 bits de cada uma das oito linhas (ou colunas)
0x09Decode ModeDecodificação BCD para dígitos de 7 segmentos; desligada (0x00) para uma matriz
0x0AIntensityBrilho, de 0x0 a 0xF
0x0BScan LimitNúmero de dígitos a varrer; 7 (todos os oito) para uma matriz
0x0CShutdown0: modo shutdown, 1: funcionamento normal
0x0FDisplay Test1: todos os LEDs acendem com brilho máximo

Módulo 8x8 simples ou módulo 4 em 1 de 8x32?

Os dois módulos que você mais vai encontrar usam o mesmo circuito; a diferença está em como a matriz de LED é ligada ao MAX7219 e no formato da placa. O módulo simples, de placa verde, tem um MAX7219 DIP (24 pinos) e uma matriz 8x8 como a 1088AS, com os pinos de entrada (VCC, GND, DIN, CS, CLK) em uma ponta e os de saída (VCC, GND, DOUT, CS, CLK) na outra. No módulo 4 em 1 azul, quatro chips MAX7219 SMD ficam embaixo das matrizes e os quatro blocos já vêm encadeados de fábrica; a placa normalmente traz a marcação FC-16. Para um texto rolante longo, o módulo 4 em 1 é mais barato e mais organizado.

Módulo de matriz de LED MAX7219 8x8 simples
Módulo MAX7219 8x8 simples: placa verde, MAX7219 DIP e uma matriz de pontos 8x8; em geral ele mostra a imagem certa com o tipo de hardware GENERIC_HW no código.
CaracterísticaMódulo 8x8 simplesMódulo 4 em 1 de 8x32 (FC-16)
Resolução8x8 = 64 LEDs32x8 = 256 LEDs
Quantidade de MAX72191 (encapsulamento DIP)4 (encapsulamento SMD)
Tipo de hardware no códigoNormalmente GENERIC_HWNormalmente FC16_HW
Caracteres visíveis ao mesmo tempo14–6 (conforme a largura das letras)
Corrente máxima com brilho totalCerca de 330 mACerca de 1,3 A
Ideal paraÍcones, dados, carinhas sorridentes, joguinhosTexto rolante, relógios, placares

Ligação do MAX7219: Arduino Uno, ESP32 e Pico

O módulo tem cinco pinos: VCC e GND para a alimentação, DIN para os dados, CLK para o clock e CS para a seleção do chip (load). Por padrão, as bibliotecas MD_Parola e MD_MAX72XX usam SPI por hardware, então o DIN tem de ir ao pino MOSI da sua placa e o CLK ao pino SCK, enquanto o CS pode ser qualquer pino digital. Você pode conferir onde ficam os pinos de SPI por hardware da sua placa na ferramenta Pinagem. Ligue os fios sempre no lado de entrada (IN) do módulo, a ponta marcada DIN; a ponta marcada DOUT é para o próximo módulo.

Pino do móduloArduino UnoESP32Raspberry Pi Pico
VCC5V5V (VIN)VBUS (5 V)
GNDGNDGNDGND
DIND11 (MOSI)GPIO 23 (MOSI)GP19 (SPI0 TX)
CSD10GPIO 5GP17
CLKD13 (SCK)GPIO 18 (SCK)GP18 (SPI0 SCK)
Não alimente um módulo MAX7219 pelo pino de 3,3 V; o chip precisa de pelo menos 4 V. Mesmo em 5 V, um único módulo 8x8 pode puxar mais de 300 mA com brilho máximo, e um módulo 4 em 1 até 1,3 A. A porta USB de um computador e o fusível USB do Arduino são limitados a 500 mA. Se o Arduino reinicia ou a imagem pisca quando você aumenta o brilho, alimente o módulo com um adaptador externo de 5 V (no mínimo 2 A para um módulo 4 em 1) e sempre ligue o GND do adaptador ao GND do Arduino.

O ESP32 e o Pico têm pinos de 3,3 V, enquanto o datasheet do MAX7219 pede pelo menos 3,5 V como nível HIGH com alimentação de 5 V. Ou seja, sinais de 3,3 V ficam tecnicamente um pouco abaixo do limite. A maioria dos módulos funciona mesmo assim, mas com fios longos ou em certos lotes de chips podem aparecer pontos aleatórios e imagens corrompidas. A solução confiável é um buffer como o 74HCT125, que aceita 3,3 V como HIGH e entrega 5 V na saída, ou um conversor de nível lógico rápido nas linhas DIN, CLK e CS. Manter os fios de sinal com menos de 20 cm também costuma resolver o problema.

Encadeando módulos (daisy chain)

O pino DOUT do MAX7219 entrega os dados que entraram pelo DIN 16,5 ciclos de clock depois. Quando você liga o lado OUT de um módulo ao lado IN do seguinte, o DOUT vai para o DIN do próximo chip, enquanto CLK e CS são compartilhados por todos os chips. Assim continuam saindo apenas 3 fios de sinal do Arduino, mas o display pode ter 8, 16 ou 32 blocos de comprimento. A cada atualização, a biblioteca envia pacotes por toda a cadeia e, quando o CS sobe, todos se atualizam no mesmo instante. Nos módulos 4 em 1 essa ligação já está na placa; para montar um display de 8 blocos (64x8) com dois módulos 4 em 1, basta ligar os pinos OUT do primeiro módulo aos pinos IN do segundo e definir MAX_DEVICES como 8 no código.

Na biblioteca, a numeração dos blocos começa no módulo mais próximo da entrada DIN e aumenta para a esquerda; para a MD_MAX72XX, a origem é o canto superior direito do display. Por isso a entrada DIN tem de ficar do lado direito para o texto ser lido corretamente. Conforme a cadeia cresce, a alimentação vira o verdadeiro limite: reforce 5 V e GND na cadeia com fios grossos a cada 4–8 blocos e coloque um capacitor eletrolítico de 10 µF e um cerâmico de 100 nF na entrada de alimentação de cada módulo. O datasheet também recomenda essa dupla, para suavizar a ondulação causada pelos pulsos de corrente dos LEDs.

Instalando as bibliotecas: MD_MAX72XX e MD_Parola

As duas bibliotecas mais populares para o MAX7219 são do mesmo autor (majicDesigns) e funcionam juntas. A MD_MAX72XX faz o trabalho de baixo nível: inicializa os módulos, escreve pixels e colunas e gerencia a fonte. A MD_Parola se apoia nela e acrescenta texto rolante, efeitos, alinhamento e zonas. A biblioteca LedControl, que aparece em exemplos mais antigos, também funciona, mas você mesmo tem de programar a rolagem. Se você nunca instalou uma biblioteca, leia primeiro Como instalar uma biblioteca no Arduino.

  1. Na Arduino IDE, abra o Gerenciador de Bibliotecas (Ferramentas > Gerenciar Bibliotecas) e busque por MD_Parola.
  2. Instale a biblioteca MD_Parola da majicDesigns. Quando ela perguntar sobre as dependências, escolha Instalar tudo para que a MD_MAX72XX também seja instalada.
  3. Ligue o módulo conforme a tabela acima e, para um módulo 4 em 1, defina HARDWARE_TYPE como FC16_HW e MAX_DEVICES como 4 no código.
  4. Carregue o sketch. Se o texto aparecer de cabeça para baixo, espelhado ou girado, não se preocupe; você vai corrigir isso com uma única linha na seção sobre o tipo de hardware, mais abaixo.

Primeiro programa: texto rolante com MD_Parola

C++ (Arduino IDE)
1// MAX7219 4'lü modülde kayan yazı (MD_Parola) / Scrolling text on a 4-in-1 MAX7219 module (MD_Parola)
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW   // mavi PCB'li 4'lü modül / blue-PCB 4-in-1 module
7#define MAX_DEVICES 4                       // zincirdeki 8x8 blok sayısı / number of 8x8 blocks in the chain
8#define CS_PIN 10                           // DIN = D11 (MOSI), CLK = D13 (SCK)
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12void setup() {
13  display.begin();
14  display.setIntensity(4);                  // parlaklık 0–15 / brightness 0–15
15  display.displayClear();
16  // metin, hizalama, hız (ms/kare), bekleme (ms), giriş efekti, çıkış efekti
17  // text, alignment, speed (ms/frame), pause (ms), entry effect, exit effect
18  display.displayText("Merhaba BlueGrays!", PA_CENTER, 50, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
19}
20
21void loop() {
22  if (display.displayAnimate()) {           // efekt bittiğinde true döner / returns true when the effect ends
23    display.displayReset();                 // aynı yazıyı baştan başlat / restart the same text
24  }
25}

A linha mais importante é displayText(): ela recebe, nesta ordem, o texto, o alinhamento (PA_LEFT, PA_CENTER, PA_RIGHT), o tempo entre quadros em milissegundos, a pausa depois que o texto se acomoda e os efeitos de entrada e de saída. Quanto menor o valor de velocidade, mais rápido o texto se move; de 25 a 60 ms a rolagem fica legível. Já displayAnimate() avança no máximo um quadro a cada chamada e retorna na hora. Por isso ela precisa ser chamada o tempo todo dentro de loop(), e você não deve usar delay() no resto do código; caso contrário, o texto anda aos trancos. Você encontra como esperar sem travar o programa em millis() vs delay().

FunçãoO que faz
begin()Inicializa os módulos, tira-os do modo shutdown e limpa o display
displayText(texto, alinhamento, velocidade, pausa, efeitoEntrada, efeitoSaida)Define o texto e os efeitos a mostrar e inicia a animação
displayAnimate()Desenha o próximo quadro; retorna true quando o efeito termina
displayReset()Toca de novo o mesmo texto e o mesmo efeito desde o começo
print("...")Mostra um texto fixo, sem efeito
setIntensity(0–15)Ajusta o brilho de todos os módulos
setInvert(true)Troca os LEDs acesos pelos apagados (imagem negativa)
displayShutdown(true)Coloca o display em modo shutdown; false o acorda de novo
begin(2) e setZone(0, 0, 1)Divide o display em zonas independentes, cada uma com seu próprio texto e efeito

Além de PA_SCROLL_LEFT, há muitos efeitos de entrada e saída: PA_PRINT (o texto aparece de uma vez), PA_SCROLL_UP e PA_SCROLL_DOWN (rolagem vertical), PA_WIPE, PA_OPENING e PA_CLOSING (abrindo a partir do centro e fechando para o centro), PA_FADE (esmaecendo pelo brilho), PA_DISSOLVE, PA_BLINDS, PA_RANDOM e PA_GROW_UP. Para ver todos, carregue o exemplo Parola_Animation_Catalog em Arquivo > Exemplos > MD_Parola; não se esqueça de ajustar as linhas HARDWARE_TYPE e MAX_DEVICES de acordo com o seu módulo.

Escolhendo o tipo de hardware (FC16_HW) e corrigindo texto espelhado

Os fabricantes ligam a matriz 8x8 ao MAX7219 de jeitos diferentes: em algumas placas as linhas “digit” do chip vão para as linhas da matriz, em outras para as colunas, e a ordem das linhas ou colunas pode estar invertida. A biblioteca compensa isso com a configuração HARDWARE_TYPE. Se você escolher o tipo errado, o módulo não está com defeito, mas as letras de cada bloco 8x8 aparecem espelhadas, de cabeça para baixo ou giradas 90 graus. Como a maior parte do código antigo na internet usa PAROLA_HW ou GENERIC_HW, esse é o problema mais comum com os módulos 4 em 1 azuis vendidos hoje. A tabela abaixo mostra o que mudar para cada sintoma; ela foi feita com base no módulo 4 em 1 FC-16.

O que você vêCausa provávelSolução
As letras aparecem espelhadas da esquerda para a direita em cada blocoPAROLA_HW selecionado, ordem das colunas invertidaUse FC16_HW
As letras aparecem giradas 90 graus em cada blocoGENERIC_HW selecionado, linhas e colunas trocadasUse FC16_HW
As letras aparecem de cabeça para baixo em cada blocoICSTATION_HW selecionado, ordem das linhas e das colunas invertidaUse FC16_HW
O texto inteiro está de cabeça para baixo e rola da esquerda para a direitaO módulo está virado ao contrário, com a entrada DIN à esquerdaGire o módulo 180 graus para que a entrada DIN fique à direita
Só alguns blocos estão certos, os outros ficam apagados ou embaralhadosMAX_DEVICES não corresponde ao número real de blocosDefina MAX_DEVICES com o número de blocos
Em um módulo 8x8 verde simples, tente primeiro GENERIC_HW; em um módulo 4 em 1 azul, tente primeiro FC16_HW. Se a imagem continuar errada, teste os quatro tipos um de cada vez; só uma linha muda a cada tentativa. Para descobrir com certeza o tipo do seu módulo, carregue o sketch MD_MAX72xx_HW_Mapper, que está nos exemplos da MD_MAX72XX: ele acende os LEDs um a um e mostra no Monitor Serial qual linha e qual coluna estão ligadas a quê.

Brilho e consumo de energia

O brilho depende de duas coisas. A primeira é o resistor RSET do módulo: o chip envia aos LEDs cerca de 100 vezes a corrente que sai do pino ISET. Os módulos costumam usar 10 kΩ, o que dá uma corrente de pico um pouco acima de 40 mA para LEDs vermelhos e de cerca de 40 mA para os azuis e brancos. A segunda é o registrador Intensity, que você ajusta pelo software: um valor n de 0 a 15 mantém os LEDs acesos durante (2n+1)/32 de cada intervalo de varredura, então 0 é o brilho mais baixo (1/32) e 15 o mais alto (31/32). E, como cada linha só fica acesa um oitavo do tempo, mesmo com brilho máximo um único LED consome em média cerca de 5 mA. Você pode consultar as tensões diretas por cor de LED na ferramenta Tensões dos LEDs.

Dá para estimar por alto a corrente média de um módulo assim: I ≈ 8 mA + 5 mA × (2n+1)/32 × L. Aqui, 8 mA é o consumo do próprio chip, n é o valor de brilho e L é o número de LEDs acesos naquele módulo (0–64); 5 mA é um oitavo da corrente de pico de 40 mA. Por exemplo, com brilho 15 e os 64 LEDs acesos, você chega a 8 + 5 × 0,97 × 64 ≈ 318 mA, o que bate com os 330 mA do datasheet. Um texto rolante normalmente acende só de 25 a 35 por cento dos LEDs, então o consumo real é bem menor. A tabela abaixo mostra valores típicos para um módulo 4 em 1.

CondiçãoPor móduloMódulo 4 em 1 (32x8)
Todos os LEDs acesos, brilho 15≈ 320 mA≈ 1,3 A
Todos os LEDs acesos, brilho 7≈ 160 mA≈ 630 mA
Texto rolante (~30% dos LEDs), brilho 7≈ 55 mA≈ 210 mA
Texto rolante (~30% dos LEDs), brilho 2≈ 25 mA≈ 90 mA
Modo shutdown (displayShutdown)≈ 0,15 mA≈ 0,6 mA

Para deixar o display mais fraco à noite, você pode ajustar o brilho automaticamente com um sensor de luz LDR e pôr o display para dormir com displayShutdown(true) quando não houver ninguém por perto. Se você está projetando um letreiro alimentado por bateria, coloque os valores acima na ferramenta Autonomia da bateria para calcular quantas horas ele vai durar. Em ambientes internos, um brilho por volta de 2–4 em vez de 15 costuma bastar; cansa menos a vista e reduz a corrente dos LEDs para algo entre um terço e um sexto.

Caracteres acentuados e especiais (UTF-8)

A Arduino IDE salva o código em UTF-8, então uma letra como “é” ou “ã” não ocupa um byte no texto, e sim dois. A MD_Parola procura cada byte como um número de caractere na fonte, por isso as letras acentuadas viram símbolos sem sentido. A fonte padrão da biblioteca tem todo o conjunto Latin-1 (é, è, à, ñ, ç, ä, ö, ü, ß e outros), que inclui todas as letras acentuadas do português (á, â, ã, à, ç, é, ê, í, ó, ô, õ, ú); então basta uma pequena função que converta UTF-8 nos códigos da fonte. Letras fora do Latin-1, como as turcas ğ, ı e ş do exemplo abaixo, precisam ser desenhadas pixel por pixel e adicionadas com addChar(). O sketch faz as duas etapas.

C++ (Arduino IDE)
1// MAX7219 + MD_Parola: Türkçe karakterli kayan yazı / Scrolling text with Turkish letters
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
7#define MAX_DEVICES 4
8#define CS_PIN 10
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12// Fontta olmayan 6 harf: ilk bayt genişlik, sonrakiler sütunlar (bit 0 = en üst satır)
13// 6 letters missing from the font: first byte is the width, then the columns (bit 0 = top row)
14const uint8_t trG[] = {5, 56, 69, 70, 85, 116};  // Ğ
15const uint8_t trg[] = {4, 25, 166, 166, 125};    // ğ
16const uint8_t trI[] = {3, 68, 125, 68};          // İ
17const uint8_t tri[] = {1, 124};                  // ı
18const uint8_t trS[] = {5, 70, 73, 201, 73, 49};  // Ş
19const uint8_t trs[] = {4, 88, 212, 84, 52};      // ş
20
21// UTF-8 metni fontun kodlarına çevirir / converts UTF-8 text to the font's codes
22void utf8ToFont(const char *src, char *dst, size_t size) {
23  size_t n = 0;
24  while (*src && n < size - 1) {
25    uint8_t c = *src++;
26    if (c < 0x80) { dst[n++] = c; continue; }            // ASCII aynen kalır / ASCII stays as is
27    if ((c & 0xE0) == 0xC0 && *src) {                    // 2 baytlık harf / 2-byte letter
28      uint16_t u = ((c & 0x1F) << 6) | (*src++ & 0x3F);
29      switch (u) {
30        case 0x11E: dst[n++] = (char)0x80; break;        // Ğ
31        case 0x11F: dst[n++] = (char)0x81; break;        // ğ
32        case 0x130: dst[n++] = (char)0x82; break;        // İ
33        case 0x131: dst[n++] = (char)0x83; break;        // ı
34        case 0x15E: dst[n++] = (char)0x84; break;        // Ş
35        case 0x15F: dst[n++] = (char)0x85; break;        // ş
36        default: dst[n++] = (u < 0x100) ? (char)u : '?'; // ç ö ü Ç Ö Ü ° fontta var / already in the font
37      }
38      continue;
39    }
40    while ((*src & 0xC0) == 0x80) src++;                 // desteklenmeyen karakteri atla / skip an unsupported character
41    dst[n++] = '?';
42  }
43  dst[n] = '\0';
44}
45
46char message[96];
47
48void setup() {
49  display.begin();
50  display.setIntensity(3);
51  display.addChar(0x80, trG);                // addChar begin()'den sonra / addChar after begin()
52  display.addChar(0x81, trg);
53  display.addChar(0x82, trI);
54  display.addChar(0x83, tri);
55  display.addChar(0x84, trS);
56  display.addChar(0x85, trs);
57  utf8ToFont("Günaydın İstanbul! Şehirde hava 22°C, çok güzel bir gün.", message, sizeof(message));
58  display.displayText(message, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
59}
60
61void loop() {
62  if (display.displayAnimate()) display.displayReset();
63}

Em cada array de caractere personalizado, o primeiro byte é a largura da letra em colunas e os bytes seguintes são as colunas da esquerda para a direita; o bit 0 de cada byte é a linha de cima. Por exemplo, o ı turco (i sem ponto) é uma única coluna, e o valor 124 (0b01111100) acende as linhas 2 a 6; a única diferença para um i normal é a falta do ponto em cima. Nas maiúsculas Ğ e İ, o corpo da letra foi encurtado em duas linhas (5 linhas em vez de 7) para abrir espaço para o sinal em cima. addChar() não copia os dados, só guarda o endereço deles, por isso os arrays precisam ser globais e adicionados depois de begin(). Os códigos 0x80–0x85 foram escolhidos porque nunca aparecem em um texto normal. Do mesmo jeito, você pode adicionar qualquer símbolo que quiser, como um sinal de grau, uma seta ou um coração.

Exemplo de relógio: um relógio digital com módulo RTC

Módulo de relógio RTC DS1307
Módulo RTC DS1307: graças à bateria tipo moeda, ele continua contando o tempo mesmo com o Arduino desligado; ele se conecta ao barramento I2C pelos pinos SDA e SCL.

O contador millis() do Arduino zera quando falta energia e, por causa do ressonador cerâmico do Uno, pode se adiantar ou atrasar vários minutos por dia. Para um relógio de verdade, use um módulo de relógio RTC. Ligue o módulo DS1307 ao barramento I2C (SDA → A4, SCL → A5, VCC → 5V, GND → GND); o MAX7219 usa os pinos SPI, então os dois módulos não entram em conflito. Para o código, instale a biblioteca RTClib da Adafruit pelo Gerenciador de Bibliotecas.

C++ (Arduino IDE)
1// MAX7219 + DS1307 RTC ile dijital saat / Digital clock with MAX7219 + DS1307 RTC
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5#include <RTClib.h>                         // Adafruit RTClib
6
7#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
8#define MAX_DEVICES 4
9#define CS_PIN 10
10
11MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
12RTC_DS1307 rtc;                             // SDA = A4, SCL = A5
13
14char timeText[8];                           // "12:34" + sonlandırıcı / + terminator
15uint8_t lastSecond = 99;
16
17void setup() {
18  display.begin();
19  display.setIntensity(2);
20  display.setTextAlignment(PA_CENTER);      // print() bu hizalamayı kullanır / print() uses this alignment
21  if (!rtc.begin()) {
22    display.print("RTC?");                  // modül bulunamadı / module not found
23    while (true) {}
24  }
25  if (!rtc.isrunning()) {
26    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));   // ilk kurulumda derleme saati / compile time on first run
27  }
28}
29
30void loop() {
31  DateTime now = rtc.now();
32  if (now.second() != lastSecond) {         // saniyede bir güncelle / update once a second
33    lastSecond = now.second();
34    // ':' ve boşluk aynı genişlikte, yazı kaymaz / ':' and space are equally wide, nothing shifts
35    sprintf(timeText, (lastSecond % 2) ? "%02d %02d" : "%02d:%02d", now.hour(), now.minute());
36    display.print(timeText);
37  }
38}

O sketch lê a hora uma vez por segundo e faz os dois-pontos piscarem. Na fonte padrão, os dois-pontos (:) e o espaço têm a mesma largura (2 colunas), então os dígitos não ficam pulando. Quando o RTC roda pela primeira vez, ele é acertado para o momento em que o sketch foi compilado. Um DS1307 pode desviar cerca de um minuto por mês; para um relógio mais preciso, use um módulo DS3231, troque RTC_DS1307 por RTC_DS3231 e mude a condição if (!rtc.isrunning()) para if (rtc.lostPower()). Graças ao cristal com compensação de temperatura, o DS3231 desvia só cerca de um minuto por ano. Você também pode dividir o display em duas zonas para mostrar a hora de um lado e a temperatura do outro.

Animação: desenhando pixel por pixel com a MD_MAX72XX

Para ir além do texto rolante, você pode usar a biblioteca MD_MAX72XX diretamente. Ela trata a cadeia inteira como um único display de pixels: um módulo 4 em 1 tem 32 colunas e 8 linhas. setPoint(linha, coluna, estado) acende ou apaga um único LED, setColumn() escreve de uma vez os 8 bits de uma coluna e clear() apaga o display. A origem (0, 0) é o canto superior direito; os números das colunas aumentam para a esquerda e os das linhas aumentam para baixo. O código abaixo desenha uma bola que quica nas bordas do display:

C++ (Arduino IDE)
1// MD_MAX72XX ile seken top animasyonu / Bouncing ball animation with MD_MAX72XX
2#include <MD_MAX72xx.h>
3#include <SPI.h>
4
5#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
6#define MAX_DEVICES 4
7#define CS_PIN 10
8
9MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
10
11int x = 0, y = 0;                           // topun konumu (sütun, satır) / ball position (column, row)
12int dx = 1, dy = 1;                         // hareket yönü / direction of travel
13
14void setup() {
15  mx.begin();
16  mx.control(MD_MAX72XX::INTENSITY, 2);
17  mx.clear();
18}
19
20void loop() {
21  const int lastCol = mx.getColumnCount() - 1;          // 4 modülde 31 / 31 with 4 modules
22  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);      // çizim bitene kadar gönderme / hold updates while drawing
23  mx.setPoint(y, x, false);                             // eski konumu sil / erase the old position
24  x += dx;
25  y += dy;
26  if (x <= 0 || x >= lastCol) dx = -dx;                 // kenara çarpınca dön / bounce off the edge
27  if (y <= 0 || y >= 7) dy = -dy;
28  mx.setPoint(y, x, true);                              // yeni konumu yak / light the new position
29  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);       // hepsini tek seferde gönder / send everything at once
30  delay(40);
31}

Em cada quadro, a atualização automática (UPDATE) é desligada primeiro, o pixel antigo é apagado e o novo é desenhado; depois o UPDATE é religado para que as mudanças sigam para os módulos de uma vez só. Isso impede que um quadro desenhado pela metade apareça no display. Com a mesma abordagem você pode desenhar dados, carinhas sorridentes, um jogo da cobrinha ou barras de nível de áudio. Os exemplos da MD_MAX72XX também trazem animações prontas, como Pac-Man, olhos de robô e uma ampulheta. Quando estiver usando a MD_Parola, você chega às mesmas funções de desenho com getGraphicObject().

Mensagens pela internet com ESP32: texto rolante enviado do celular

Com um ESP32 você pode deixar seu letreiro de texto rolante sem fio. O código abaixo se conecta à sua rede de casa, primeiro faz o próprio endereço IP rolar pelo display e depois mostra a hora que obtém da internet (NTP). Abra esse endereço IP no navegador de um celular conectado ao mesmo Wi-Fi e você verá uma caixa de texto; a mensagem que você digitar começa a rolar na volta seguinte e se alterna com o relógio. O módulo é ligado aos pinos VSPI, como na tabela: CLK no GPIO 18, DIN no GPIO 23 e CS no GPIO 5. Se você está começando com o ESP32, leia antes O que é o ESP32?.

C++ (Arduino IDE)
1// ESP32: telefondan mesaj + internet saati / ESP32: message from your phone + internet time
2#include <WiFi.h>
3#include <WebServer.h>
4#include <time.h>
5#include <MD_Parola.h>
6#include <MD_MAX72xx.h>
7#include <SPI.h>
8
9#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
10#define MAX_DEVICES 4
11#define CS_PIN 5                            // VSPI: CLK = GPIO 18, DIN = GPIO 23
12
13const char* ssid = "AgAdi";                 // Wi-Fi adın / your Wi-Fi name
14const char* password = "Sifre123";          // Wi-Fi şifren / your Wi-Fi password
15
16MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
17WebServer server(80);
18
19char message[128];
20char nextMessage[128];
21char clockText[8] = "--:--";
22bool newMessage = false;
23bool showClock = false;
24
25const char PAGE[] = R"rawliteral(<!DOCTYPE html><html><head><meta charset="utf-8">
26<meta name="viewport" content="width=device-width"><title>MAX7219</title></head><body>
27<h2>Kayan yazı / Scrolling text</h2>
28<form action="/"><input name="m" maxlength="100"> <button>Gönder / Send</button></form>
29</body></html>)rawliteral";
30
31void handleRoot() {
32  if (server.hasArg("m")) {                 // formdan gelen mesaj / message from the form
33    server.arg("m").toCharArray(nextMessage, sizeof(nextMessage));
34    newMessage = true;
35  }
36  server.send(200, "text/html; charset=utf-8", PAGE);
37}
38
39void setup() {
40  display.begin();
41  display.setIntensity(3);
42  display.print("WiFi..");
43  WiFi.begin(ssid, password);
44  while (WiFi.status() != WL_CONNECTED) delay(500);
45  configTime(3 * 3600, 0, "pool.ntp.org");  // Türkiye UTC+3 / Turkey is UTC+3
46  snprintf(message, sizeof(message), "IP: %s", WiFi.localIP().toString().c_str());
47  server.on("/", handleRoot);
48  server.begin();
49  display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
50}
51
52void loop() {
53  server.handleClient();
54  if (display.displayAnimate()) {           // önceki gösterim bitti / the previous show has ended
55    showClock = !showClock;
56    if (newMessage) {                       // yeni mesaj sırayı keser / a new message jumps the queue
57      strlcpy(message, nextMessage, sizeof(message));
58      newMessage = false;
59      showClock = false;
60    }
61    if (showClock) {
62      struct tm t;
63      if (getLocalTime(&t, 10)) strftime(clockText, sizeof(clockText), "%H:%M", &t);
64      display.displayText(clockText, PA_CENTER, 40, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
65    } else {
66      display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
67    }
68  }
69}

handleRoot() não joga a mensagem do formulário direto no display; ela fica guardada em nextMessage. A mensagem só é copiada quando a animação atual termina (quando displayAnimate() retorna true), assim o texto que a MD_Parola está lendo nunca muda no meio do caminho. O valor 3 × 3600 em configTime() é o fuso UTC+3 usado na Turquia; no Brasil, para o horário de Brasília (UTC−3, sem horário de verão), troque esse valor por -3 * 3600. No Reino Unido, substitua essa linha por configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org"), que também cuida do horário de verão. Para mensagens com acentos, acrescente a este sketch as linhas de addChar() e a função utf8ToFont() vistas acima e troque a linha de strlcpy() por utf8ToFont(nextMessage, message, sizeof(message)). Para hora pela internet e painéis web em tempo real, veja Relógio NTP com ESP32 e servidor web e WebSocket no ESP32.

MAX7219 com Raspberry Pi Pico e MicroPython

No Pico com MicroPython, o caminho mais prático é copiar para o Pico, pelo Thonny, o driver de código aberto max7219.py (micropython-max7219). Como o driver usa o módulo framebuf do MicroPython, funções de desenho como text(), pixel(), line() e rect() já vêm prontas, e ele funciona na orientação certa nos módulos 4 em 1 FC-16. Alimente o módulo pelo VBUS (5 V) e ligue o DIN ao GP19, o CLK ao GP18 e o CS ao GP17. O MicroPython também reserva o pino MISO do SPI0 (GP16), então não use esse pino para mais nada. A fonte do framebuf tem 8x8 pixels e só ASCII, por isso um módulo 4 em 1 mostra 4 letras por vez e as letras acentuadas não aparecem. Para a configuração, veja O que é o Raspberry Pi Pico? e fundamentos do MicroPython.

MicroPython
1# Pico + 4'lü MAX7219 modül: kayan yazı / Pico + 4-in-1 MAX7219 module: scrolling text
2from machine import Pin, SPI
3import time
4import max7219                        # max7219.py Pico'da olmalı / must be on the Pico
5
6# GP18 = SCK -> CLK, GP19 = MOSI -> DIN, GP17 -> CS; GP16 (MISO) boş kalır / GP16 (MISO) stays free
7spi = SPI(0, baudrate=5_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19))
8display = max7219.Matrix8x8(spi, Pin(17, Pin.OUT), 4)
9display.brightness(3)                 # parlaklık 0–15 / brightness 0–15
10
11def scroll(text, delay_ms=35):
12    width = len(text) * 8             # her harf 8 piksel / each letter is 8 px wide
13    for x in range(32, -width, -1):   # sağdan girip solda kaybolur / enters right, leaves left
14        display.fill(0)
15        display.text(text, x, 0, 1)
16        display.show()
17        time.sleep_ms(delay_ms)
18
19while True:
20    scroll("BlueGrays + Pico")
21    display.fill(0)
22    display.text("Pico", 0, 0, 1)     # 4 harf tam sığar / exactly 4 letters fit
23    display.show()
24    time.sleep(1)
25    display.fill(0)
26    display.rect(0, 0, 32, 8, 1)      # ekran çerçevesi / display frame
27    display.show()
28    time.sleep_ms(500)

Ideias de projetos com MAX7219

  • Letreiro de mensagens rolantes: monte um painel de mensagens para uma loja, uma sala de aula ou a porta do quarto e troque a mensagem pelo celular com um módulo Bluetooth HC-05.
  • Relógio, calendário e temperatura: mostre, alternadamente, a hora e a data de um RTC e a temperatura e a umidade de um sensor DHT11.
  • Relógio pela internet e previsão do tempo: faça rolar a hora NTP e a temperatura de uma API de previsão do tempo com um ESP32.
  • Placar: controle um display de duas zonas para uma partida de tênis de mesa ou de basquete com um controle remoto IR.
  • Minijogos: jogue cobrinha, Pong ou um jogo de desviar de obstáculos no display de 32x8 com um joystick; mais ideias em projetos de jogos com Arduino.
  • Medidor de nível de áudio: desenhe as leituras de um sensor de som como um gráfico de barras de 32 colunas.
  • Painel de animação 32x32: empilhe quatro módulos 4 em 1, um sobre o outro, e rode animações como chuva, estrelas e fogos de artifício.

Erros comuns e como corrigi-los

SintomaCausa provávelSolução
Nenhum LED acendeSem alimentação, fios no lado OUT do módulo ou pino CS diferente do códigoLigue os fios no lado IN marcado DIN e confira CS_PIN e a alimentação de 5 V
Todos os LEDs acesos no máximo e nada mudaOs dados não chegam: DIN e CLK trocados ou o chip preso no modo de testeLigue o DIN ao MOSI e o CLK ao SCK e depois desligue e religue a alimentação do módulo
Letras espelhadas, de cabeça para baixo ou giradas 90 grausHARDWARE_TYPE erradoTeste FC16_HW em um módulo 4 em 1 e GENERIC_HW em um módulo simples
O texto inteiro está de cabeça para baixo e rola da esquerda para a direitaO módulo foi montado ao contrário, com a entrada DIN à esquerdaGire o módulo 180 graus; a entrada DIN tem de ficar à direita
Só um bloco funciona ou o texto parece cortadoMAX_DEVICES errado ou ligação interrompida entre os módulosDefina MAX_DEVICES com o número de blocos e confira os fios OUT → IN
O Arduino reinicia ou a imagem pisca com brilho mais altoO limite de 500 mA da USB é ultrapassado e a tensão caiUse um adaptador externo de 5 V e 2 A, una os GNDs e diminua o brilho
Pontos aleatórios e caracteres corrompidos em um ESP32O sinal de 3,3 V está abaixo do limiar de HIGH de 3,5 V, e os fios são longosEleve o nível com um 74HCT125 e encurte os fios
Letras acentuadas aparecem como símbolos sem sentidoOs bytes UTF-8 vão direto para a fonteUse o método com utf8ToFont() e addChar()
O texto rolante trava e anda aos trancosHá um delay() ou uma tarefa demorada em loop()Chame displayAnimate() com frequência e faça as esperas com millis()
Quando algo não funcionar, comece pelo teste mais simples: deixe no código só display.begin() e display.print("8888"). Se nenhum LED acender, o problema está na ligação ou na alimentação; use um multímetro para conferir se há 4,5–5 V entre os pinos VCC e GND do módulo. Se os LEDs acendem mas os dígitos saem embaralhados, o problema é o tipo de hardware; se só o primeiro bloco funciona, olhe o MAX_DEVICES ou as ligações entre os módulos.

O MAX7219 é um dos jeitos mais baratos de ter um display chamativo com poucos fios: o chip cuida da varredura, da limitação de corrente e do brilho, e você só precisa pensar no que mostrar. Comece desenhando ícones em um único módulo 8x8 e depois passe para texto rolante, relógio e mensagens sem fio com um ESP32 em um módulo 4 em 1. Se você quer LEDs coloridos e controlados um a um, os LEDs WS2812B NeoPixel são uma boa escolha e, para um projeto que só mostra números, um display de 7 segmentos. No app BlueGrays você encontra projetos com MAX7219, como relógio e calendário em matriz de pontos, estação meteorológica, animações 32x8 e texto rolante, passo a passo, com esquemas do circuito e código.

Perguntas frequentes

Por que o texto no meu módulo MAX7219 aparece de cabeça para baixo ou espelhado?

Os fabricantes ligam a matriz 8x8 ao MAX7219 de jeitos diferentes, e a biblioteca compensa isso com a configuração HARDWARE_TYPE. A maioria dos módulos 4 em 1 azuis vendidos hoje mostra a imagem certa com FC16_HW, e os módulos verdes simples com GENERIC_HW. Se a imagem continuar errada, teste também PAROLA_HW e ICSTATION_HW; só uma linha do código muda.

Quantos módulos MAX7219 dá para ligar em cascata?

O chip em si não tem limite e, como a MD_MAX72XX guarda o número de módulos em um byte, o software permite até 255. Na prática, são a alimentação e a qualidade do sinal que definem o limite: reforce 5 V e GND na cadeia a cada 4–8 blocos, mantenha os fios curtos e coloque um capacitor de 10 µF e um de 100 nF em cada módulo. Quanto mais longa a cadeia, mais tempo leva cada atualização e mais RAM ela usa.

O MAX7219 funciona com um ESP32 em 3,3 V?

O MAX7219 precisa de uma alimentação de pelo menos 4 V, então alimente o módulo pelo pino 5V (VIN) do ESP32. O datasheet pede 3,5 V como nível HIGH com alimentação de 5 V; os sinais de 3,3 V do ESP32 funcionam na maioria dos módulos, mas ficam abaixo desse limite. Para um funcionamento confiável, coloque um conversor de nível como o 74HCT125 nas linhas DIN, CLK e CS.

Quanta corrente uma matriz de LED MAX7219 consome?

Um único módulo 8x8 com todos os LEDs acesos no brilho máximo consome cerca de 330 mA, e um módulo 4 em 1 de 8x32 até 1,3 A. Com texto rolante, só parte dos LEDs fica acesa e o brilho costuma ficar entre 2 e 7, então um módulo 4 em 1 consome tipicamente algo em torno de 100–250 mA. No modo shutdown, cada chip gasta só cerca de 150 µA.

Como mostrar caracteres acentuados ou especiais em um MAX7219?

A fonte padrão da MD_Parola tem os caracteres Latin-1, como é, ã, ç, ñ, ä, ö, ü e ß, mas a Arduino IDE os guarda como dois bytes UTF-8. Converta o texto nos códigos da fonte com uma pequena função antes de mostrá-lo. Caracteres fora do Latin-1 você mesmo pode desenhar e adicionar com addChar().

O MAX7219 consegue controlar um display de 7 segmentos?

Sim. Na verdade, o MAX7219 foi projetado para displays de 7 segmentos de cátodo comum com até 8 dígitos, e tem um modo BCD que decodifica os dígitos sozinho. Você pode usar com facilidade os módulos de display MAX7219 prontos, de 8 dígitos, com a função setDigit() da biblioteca LedControl. Displays de ânodo comum não podem ser ligados diretamente a este chip.

Ver todos os projetos
Relógio e calendário de matriz de LEDs (MAX7219, DS1307)
#84Intermediário
Premium

Relógio e calendário de matriz de LEDs (MAX7219, DS1307)

Um painel luminoso para a mesa ou a parede que mostra a hora e a data em texto deslizante. O display de LEDs 8x32 exibe um relógio com dígitos grandes e dois pontos que piscam a cada segundo; no iníci

5.0(37)
59
Relógio, calendário e estação meteorológica de matriz de pontos (MAX7219, DS1307)
#85Intermediário
Premium

Relógio, calendário e estação meteorológica de matriz de pontos (MAX7219, DS1307)

Um painel com texto em rolagem para sua mesa ou parede que mostra, em sequência, a hora, a data e as condições do ambiente. No display LED 8x32, você vê primeiro a temperatura em Celsius e Fahrenheit,

5.0(50)
66
Animação e texto em 4 matrizes LED 32x8
#90Intermediário
Premium

Animação e texto em 4 matrizes LED 32x8

Empilhe quatro módulos longos de matriz LED para formar um display quadrado com mais de mil pontos de luz. Primeiro, letras caem do alto como chuva, como nos filmes. Depois, uma cortina se abre e duas

4.6(40)
66
4x matriz de LED 32x8 - Pacote de 6 animações
#91Intermediário
Premium

4x matriz de LED 32x8 - Pacote de 6 animações

Quatro painéis de LED 32x8 empilhados formam um display quadrado que exibe seis animações em sequência: chuva digital, uma espiral que cresce, células que nascem e morrem sozinhas, ondas circulares na

4.5(35)
48
Cobrinha em pixels: matriz 8x8
#88Avançado
Premium

Cobrinha em pixels: matriz 8x8

Monte o clássico jogo da cobrinha com as próprias mãos e jogue. Em uma matriz de luzes coloridas 8x8, você guia uma cobra verde com um joystick e come maçãs vermelhas; a cada maçã, a cobra cresce, ace

4.7(39)
57
Hora exata pela internet com ESP32 e NTP
#128Avançado
Premium

Hora exata pela internet com ESP32 e NTP

O ESP32 se conecta ao Wi-Fi e obtém a hora exata com precisão de milissegundos de servidores NTP (Network Time Protocol). Com a função configTzTime(), o fuso horário configurado (TRT, UTC+3 neste exemplo) é aplicado automaticamente, e as mudanças nos países que adotam horário de verão são gerenciadas automaticamente por uma string POSIX TZ. A hora é exibida tanto no Monitor Serial quanto em um LCD I2C opcional.

4.5(13)
21
Relógio digital com ESP32 e RTC DS1307 (I2C + LCD)
#120Intermediário
Premium

Relógio digital com ESP32 e RTC DS1307 (I2C + LCD)

Neste projeto, exibimos a hora e a data em um display LCD I2C 16x2 usando o módulo de relógio de tempo real (RTC) DS1307. O DS1307 é um RTC com bateria de reserva que mantém a hora mesmo quando o ESP32 é desligado. Os dois módulos compartilham o mesmo barramento I2C (SDA/SCL), permitindo aprender a controlar vários dispositivos com apenas dois fios de sinal. Você aprenderá na prática o protocolo de comunicação I2C, o funcionamento dos módulos RTC e a exibição de dados dinâmicos em um LCD.

4.6(26)
40
Relógio digital com display de 7 segmentos de 4 dígitos
#48Intermediário
Premium

Relógio digital com display de 7 segmentos de 4 dígitos

Um relógio digital luminoso de quatro dígitos, parecido com o de um forno de cozinha. O módulo de relógio DS1302 mantém a hora e, a cada segundo, o Arduino lê as horas e os minutos e os exibe no displ

4.8(56)
73