Pular para o conteúdo

Lopaka Basics 05: do navegador à placa ​

Um módulo OLED SSD1306 ligado por GND, VCC, SCL e SDA, mostrando o painel matinal com FRI 24 OCT, 07:45 e Sunny 23C

Você desenhou uma tela, enviou o código, e o display fica preto. O display raramente está quebrado. Geralmente é uma de cinco coisas, e quando você corrige a certa, a tela acende.

Neste episódio, você vai levar o painel matinal do episódio 2 do navegador para uma placa: instalar a biblioteca na Arduino IDE, montar o sketch ao redor do código do Lopaka e conferir quanta memória ele usa. Depois você vai passar pelos cinco motivos para um display novo ficar preto, e a correção para cada um.

O que você vai construir: um sketch do Arduino que mostra as telas Morning e Night do episódio 2 em um OLED SSD1306 de 128×64, além de uma lista de verificação para uma tela preta. Do que você precisa: um navegador, a Arduino IDE gratuita e o projeto Morning Dashboard do episódio 2. Um Arduino UNO e um módulo OLED são opcionais; executamos o sketch no simulador do episódio 1.

Como a placa conversa com a tela ​

Quatro fios: energia, clock e dados ​

Um OLED pequeno como o nosso precisa de quatro fios. Dois são energia: VCC e GND. Os outros dois são um barramento chamado I2C:

  • SCL é o clock. A placa envia um tique constante nele.
  • SDA carrega os dados, um bit por tique.

O barramento I2C entre um Arduino UNO e o OLED: o clock SCL tiquetaqueia acima dos bits de dados SDA, que formam o endereço 0x3C; um sensor em 0x76 e um clock em 0x68 compartilham os mesmos dois fios, e só o display responde a 0x3C

Cada mensagem começa com um endereço ​

Outras peças podem compartilhar os mesmos dois fios: um sensor de temperatura, um relógio de tempo real e o display podem todos ficar em SCL e SDA. Então cada mensagem começa com um endereço, e só a peça com aquele endereço responde.

A maioria desses displays responde a 0x3C. Alguns respondem a 0x3D. Em muitos módulos, um resistor minúsculo na parte de trás define o endereço, ao lado de uma etiqueta como "IIC ADDRESS SELECT 0x78 / 0x7A". Esses dois números são os mesmos endereços dobrados: 0x3C × 2 = 0x78 e 0x3D × 2 = 0x7A. Lembre disso para o motivo 2 abaixo.

A parte de trás do módulo OLED: a etiqueta IIC ADDRESS SELECT com um resistor no lado 0x78 e a nota 0x78 = 0x3C × 2

Telas coloridas maiores usam SPI ​

Telas coloridas como o TFT do episódio 4 geralmente usam SPI: mais fios (MOSI, SCK, CS, DC e RST), muito mais rápido, e um fio de seleção, CS, em vez de um endereço.

Um Arduino UNO ao lado do TFT colorido do episódio 4, com os fios SPI MOSI, SCK, CS, DC e RST; mais fios, muito mais rápido

Etapa 1: copie o código do Lopaka ​

Abra o Morning Dashboard do episódio 2: um OLED de 128×64 com a biblioteca U8g2 e duas telas, Morning e Night.

O Lopaka escreve as funções de desenho, uma por tela: drawMorning() e drawNight(). O sketch ao redor delas, com a configuração do display e loop(), você escreve uma vez na Arduino IDE.

Clique em Copiar na guia Morning do painel de código, depois mude para a guia Night e copie aquela também. O Copiar pega o código da guia aberta. O código gerado é o mesmo do episódio 2.

O Morning Dashboard no Lopaka com as telas Morning e Night na tela de desenho; o painel de código mostra a guia Morning com a função drawMorning, o botão Copiar e Declare as PROGMEM ativado

Etapa 2: instale a U8g2 ​

Na Arduino IDE, abra o Library Manager (o ícone de livros à esquerda), busque U8g2 e clique em INSTALL na biblioteca U8g2 de oliver. O episódio usou a versão 2.36.19.

Etapa 3: escolha a placa ​

Clique em Select Board no topo, escolha Select other board and port, digite uno e escolha Arduino UNO. Sem uma placa conectada, a IDE ainda consegue compilar o sketch; você só precisa da porta para enviar.

Etapa 4: monte o sketch ​

Um sketch novo já tem setup() e loop(). Cole o código do Lopaka no topo e depois adicione três linhas: inclua a biblioteca, crie o display e inicie ele em setup(). Em loop(), limpe o buffer, desenhe e envie o buffer para a tela:

cpp
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// Paste the Lopaka code here: drawMorning() and drawNight()

void setup() {
  // put your setup code here, to run once:
  u8g2.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  u8g2.clearBuffer();
  if (millis() / 3000 % 2) drawNight(); else drawMorning();
  u8g2.sendBuffer();
}

setup() executa uma vez, quando a placa começa. loop() executa de novo e de novo. A linha if mostra cada tela por três segundos, como no episódio 2, e a ordem limpar, desenhar, enviar é o buffer de quadro do episódio 1.

A IDE com o sketch finalizado: o fim de drawNight, depois setup com u8g2.begin() e loop com clearBuffer, a linha millis e sendBuffer

A linha do construtor descreve o seu display ​

A linha longa sob o #include é o construtor. Ela descreve o seu display, e cada parte do nome dela significa algo:

  • SSD1306: o chip controlador no módulo.
  • 128X64: o tamanho em pixels.
  • NONAME: um módulo genérico, e não uma marca específica.
  • F: o modo de buffer. F é um buffer de quadro completo, 1.024 bytes de RAM. 1 desenha a tela em páginas e precisa de só 128 bytes.
  • HW_I2C: a fiação, I2C de hardware nos pinos SDA e SCL da placa.

U8G2_R0 significa sem rotação, e U8X8_PIN_NONE diz que nenhum pino de reset está ligado. A biblioteca U8g2 tem um construtor para cada combinação de chip, tamanho, buffer e fiação. Escolha o errado, e a tela fica preta.

O construtor U8G2_SSD1306_128X64_NONAME_F_HW_I2C dividido em cartões: chip controlador, tamanho, F para um buffer completo de 1 KB e I2C de hardware; uma linha é o seu display, e uma linha errada significa uma tela preta

Etapa 5: verifique e execute ​

Verifique ​

Clique em Verify, a marca de seleção no canto superior esquerdo. A IDE compila o sketch e informa:

Sketch uses 15394 bytes (47%) of program storage space. Maximum is 32256 bytes.
Global variables use 1600 bytes (78%) of dynamic memory, leaving 448 bytes for local variables. Maximum is 2048 bytes.

O sketch ocupa menos da metade da flash e mais de três quartos da RAM. O buffer de quadro sozinho tem 1.024 dos 2.048 bytes do UNO; todo o resto ocupa 576. Fique de olho nesses 78%: eles voltam no motivo 5.

A saída de Verify: 15394 bytes (47%) de flash e 1600 bytes (78%) de memória dinâmica, e uma barra dos 2.048 bytes de RAM do UNO com o buffer de quadro de 1.024 bytes ocupando metade

Envie ou execute no simulador ​

Com uma placa conectada, escolha a porta dela na mesma caixa de diálogo Select Board e clique em Upload, a seta ao lado de Verify.

Sem placa? Abra o projeto do simulador do episódio 1: um Arduino UNO com um display SSD1306 ligado GND ao GND, VCC ao 5V, SDA ao A4 e SCL ao A5, e a biblioteca U8g2. Cole o sketch e pressione Play. O display mostra a tela Morning e muda para Night a cada três segundos.

O simulador executando o sketch: um Arduino UNO ligado ao OLED com SDA ao A4 e SCL ao A5, e o display mostrando 07:45 e Sunny 23C

Cinco motivos para uma tela ficar preta ​

As verificações vão de fora para dentro: fios, endereço, chip, código e memória. Em cada uma, quebramos a configuração que funciona no simulador, vemos a tela ficar preta e corrigimos.

1. Os fios ​

Em um UNO, SDA vai para A4 e SCL vai para A5. Troque eles, e a placa está falando com ninguém: a tela fica preta.

Confira energia e terra também. Outras placas usam outros pinos: na maioria das placas ESP32, SDA é o pino 21 e SCL é o pino 22.

Motivo 1, fios: os fios SDA e SCL trocados em A4 e A5, identificados como trocados, falando com ninguém, e o OLED fica preto

2. O endereço ​

A maioria dos módulos responde a 0x3C, alguns a 0x3D. A U8g2 conversa com 0x3C, a menos que você diga o contrário. Se o módulo estiver definido como 0x3D, nada responde, e a tela fica preta.

Para descobrir qual você tem, execute um sketch scanner I2C. Ele pergunta a cada endereço no barramento e imprime os que respondem:

cpp
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("Scanning I2C...");
  for (byte a = 1; a < 127; a++) {
    Wire.beginTransmission(a);
    if (Wire.endTransmission() == 0) {
      Serial.print("Found a device at 0x");
      Serial.println(a, HEX);
    }
  }
  Serial.println("Done.");
}

void loop() {}

Envie ele e abra o Serial Monitor a 9600 baud.

Motivo 2, endereço: o sketch scanner I2C executando, com Found a device at 0x3D no Serial Monitor

Depois defina o endereço no seu sketch do painel, antes de begin():

cpp
void setup() {
  u8g2.setI2CAddress(0x3D * 2);
  u8g2.begin();
}

A U8g2 quer o endereço dobrado, então 0x3D vira 0x3D * 2, 0x7A: o mesmo número impresso na parte de trás do módulo.

A correção em setup: u8g2.setI2CAddress(0x3D * 2) antes de u8g2.begin(), a U8g2 quer ele dobrado: 0x7A, e o OLED mostra o painel de novo

3. O chip ​

Dois módulos podem parecer exatamente iguais e usar controladores diferentes. A maioria das telas pequenas de 0,96 polegada usa o SSD1306. Muitas de 1,3 polegada usam o SH1106. Execute código SSD1306 em um SH1106, e você obtém uma tela em branco ou uma faixa de ruído na borda.

A correção é o nome do chip no construtor:

cpp
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

Motivo 3, chip: um módulo SSD1306 de 0,96 polegada com uma imagem limpa ao lado de um módulo SH1106 de 1,3 polegada executando código SSD1306, com uma faixa de ruído na borda direita

4. O código ​

A U8g2 desenha em um buffer na memória, e só sendBuffer() move a imagem para a tela. Esqueça essa linha, ou esqueça u8g2.begin() em setup(), e a tela fica escura. O sketch ainda compila sem erro, então confira estas linhas primeiro:

cpp
u8g2.begin();          // in setup()
u8g2.clearBuffer();    // in loop()
drawMorning();
u8g2.sendBuffer();

Motivo 4, código: o quadro é desenhado no buffer de quadro da RAM, mas com begin e sendBuffer riscados ele nunca chega à tela, que fica preta; Done compiling, sem erro

5. A memória ​

Lembra daqueles 78%? Um UNO tem 2.048 bytes de RAM, e o buffer de quadro ocupa metade.

O Lopaka mantém imagens na flash com PROGMEM: o interruptor Declare as PROGMEM ao lado do código vem ativado por padrão, e os arrays recebem U8X8_PROGMEM. Desative ele, e as imagens se movem para a RAM. A lua sozinha tem 48 × 48 / 8 = 288 bytes, e as quatro imagens do painel ocupam 416. Agora o sketch usa 2.016 bytes, 98% da RAM, com 32 bytes restantes para tudo o que o programa faz enquanto executa. Ele ainda compila, mas a tela desmorona: no simulador, o texto some e as imagens quebram.

Motivo 5, memória: a RAM do UNO em 98%, buffer de quadro 1.024, todo o resto 576 e imagens 416, para 2.016 bytes; a saída de Verify informa 2016 bytes (98%) e o OLED mostra um painel quebrado

Deixe o PROGMEM ativado. Se mesmo assim faltar RAM, mude para o modo por páginas: coloque um 1 em vez do F no construtor, e o buffer encolhe de 1.024 bytes para 128.

cpp
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

No modo por páginas, a biblioteca desenha a tela em oito faixas, uma depois da outra, então loop() também muda. Em vez de clearBuffer() e sendBuffer(), o desenho vai dentro de um loop firstPage() / nextPage():

cpp
void loop() {
  u8g2.firstPage();
  do {
    if (millis() / 3000 % 2) drawNight(); else drawMorning();
  } while (u8g2.nextPage());
}

Com o modo por páginas, o mesmo painel usa 704 bytes, 34% da RAM.

Modo por páginas: a RAM do UNO em 34%; os construtores U8G2_SSD1306_128X64_NONAME_F_HW_I2C para um buffer completo e U8G2_SSD1306_128X64_NONAME_1_HW_I2C para o modo por páginas, buffer de 1.024 para 128 bytes

Recapitulação ​

  1. O Lopaka desenha a tela, a IDE monta o sketch ao redor dela: instale a U8g2, escolha a placa, cole as funções de desenho e adicione o include, o construtor, begin() e o loop limpar, desenhar, enviar.
  2. Uma linha de construtor descreve o seu display: chip, tamanho, buffer e fiação.
  3. Tela preta? Confira cinco coisas: os fios, o endereço, o chip, o código e a memória.

Próximo episódio: Screens & Menus. Mais de uma tela, com menus e um botão. Leia o episódio 6 ou assista no YouTube.

Pronto para testar? Abra o Lopaka e coloque sua tela em uma placa real.