A tecnologia OLED permite uma maior legibilidade, principalmente em ambientes mal iluminados, por emitirem diretamente luz. Este display, comprado na DX, é bem pequeno e apresenta uma faixa superior na cor amarela (16 pontos de altura) e o restante na cor azul. Existe uma separação entre as duas faixas, com altura de um ponto.
Para quem quiser sair usando direto com o Arduino, tem uma biblioteca da Adafruit no github:
https://github.com/adafruit/Adafruit_SSD1306
Os exemplos desta biblioteca usam uma outra biblioteca da Adafruit que implementa várias funções gráficas:
https://github.com/adafruit/Adafruit-GFX-Library
O controlador do display é o SSD1306, cujo datasheet pode ser visto aqui. O endereço I2C pode ser 0x3C ou 0x3D (o módulo veio configurado para 0x3C, tem uns pontos de solda atrás que devem permitir alterar o endereço).
Cada transferência de iniciar com um byte de controle. Neste byte apenas os dois bits mais significativos são usados: Co (bit 7) e D/C (bit 6). O bit D/C indica se o byte seguinte é um comando (0) ou dado a ser escrito na memória de vídeo(1). Se Co for 1, o byte de controle se aplica apenas ao byte seguinte e um novo byte de controle deve ser fornecido após o dado ou comando. Se Co for 0, os bytes seguintes serão todos tratados como dado ou comando. O datasheet detalha os comandos disponíveis.
Confuso? Vejamos alguns exemplos:
0x00 0x20 -> 0x20 é tratado como comando
0x00 0x22 0x01 0x07 -> 0x22, 0x01 e 0x07 são tratados como comandos
0x40 0x01 0x02 -> 0x01 e 0x02 são escritos na memória de vídeo
0x80 0x20 0x40 0x01 0x02 -> 0x20 é tratado como comando e 0x01 e 0x02 são escritos na memória de vídeo
A memória do display possui 1Kbyte, dividido em oito páginas. Cada byte corresponde a 8 pontos em uma coluna, com o bit menos significativo correspondendo à linha superior:
No meu primeiro teste, liguei o display a um Arduino. A ligação é trivial: Vcc em +5V, GND ao GND, SDA a A4 e SCL a A5 (estes dois últimos pinos são para Uno e Nano, no Mega os pinos são outros).
A iniciação do display é feita enviando vários comandos, que eu peguei do código da Adafruit. Para simplificar, enviei todos os comandos de iniciação em uma única transação I2C.
A escrita na memória também é um pouco confusa. O comando Set Column Address define uma coluna inicial e uma coluna final. O comando Set Page Address define uma página inicial e uma página final. O comando Set Memory Address Mode define como é o feito o incremento da posição atual após uma escrita. Usei o modo "horizontal" no qual a coluna começa na inicial e vai sendo incrementada até atingir a final, neste ponto volta à coluna inicial e a página é incrementada.
A minha rotina de limpeza de tela mostra isto em funcionamento, usando todas as colunas e páginas da memória.
Para completar este teste, uma rotina de escrita de caracteres. Para simplificar, usei uma célula de 8 x 8 pontos, o que gerou 8 linhas de 16 caracteres bem pequenos.
- //
- // Teste de comunicação com Display OLED
- // baseado no controlador SSD1306
- //
- #include <Wire.h>
- // Endereço I2C do display
- #define DISP_ADDR 0x3C
- // Comandos
- #define SSD1306_SETCONTRAST 0x81
- #define SSD1306_DISPLAYALLON_RESUME 0xA4
- #define SSD1306_DISPLAYALLON 0xA5
- #define SSD1306_NORMALDISPLAY 0xA6
- #define SSD1306_INVERTDISPLAY 0xA7
- #define SSD1306_DISPLAYOFF 0xAE
- #define SSD1306_DISPLAYON 0xAF
- #define SSD1306_SETDISPLAYOFFSET 0xD3
- #define SSD1306_SETCOMPINS 0xDA
- #define SSD1306_SETVCOMDETECT 0xDB
- #define SSD1306_SETDISPLAYCLOCKDIV 0xD5
- #define SSD1306_SETPRECHARGE 0xD9
- #define SSD1306_SETMULTIPLEX 0xA8
- #define SSD1306_SETLOWCOLUMN 0x00
- #define SSD1306_SETHIGHCOLUMN 0x10
- #define SSD1306_SETSTARTLINE 0x40
- #define SSD1306_MEMORYMODE 0x20
- #define SSD1306_COLUMNADDR 0x21
- #define SSD1306_PAGEADDR 0x22
- #define SSD1306_COMSCANINC 0xC0
- #define SSD1306_COMSCANDEC 0xC8
- #define SSD1306_SEGREMAP 0xA0
- #define SSD1306_CHARGEPUMP 0x8D
- #define SSD1306_EXTERNALVCC 0x1
- #define SSD1306_SWITCHCAPVCC 0x2
- // Tamanho da tela
- #define SSD1306_LCDWIDTH 128
- #define SSD1306_LCDHEIGHT 64
- // Iniciação
- void setup()
- {
- Wire.begin();
- Display_init();
- }
- // Laço principal
- void loop()
- {
- int d = 0;
- for (int l = 0; l < 8; l++)
- {
- for (int c = 0; c < 16; c++)
- {
- Display_write (l, c, d);
- if (++d == 10)
- d = 0;
- delay (500);
- }
- }
- delay (10000);
- Display_clear();
- }
- // Sequência de iniciação do display
- byte cmdInit[] =
- {
- SSD1306_DISPLAYOFF,
- SSD1306_SETDISPLAYCLOCKDIV, 0x80,
- SSD1306_SETMULTIPLEX, 0x3F,
- SSD1306_SETDISPLAYOFFSET, 0x00,
- SSD1306_SETSTARTLINE | 0x0,
- SSD1306_CHARGEPUMP, 0x14,
- SSD1306_MEMORYMODE, 0x00,
- SSD1306_SEGREMAP | 0x1,
- SSD1306_COMSCANDEC,
- SSD1306_SETCOMPINS, 0x12,
- SSD1306_SETCONTRAST, 0xCF,
- SSD1306_SETPRECHARGE, 0xF1,
- SSD1306_SETVCOMDETECT, 0x40,
- SSD1306_DISPLAYALLON_RESUME,
- SSD1306_NORMALDISPLAY,
- SSD1306_DISPLAYON
- };
- // Iniciação do display
- void Display_init()
- {
- Display_sendcmd (cmdInit, sizeof(cmdInit));
- Display_clear();
- }
- // Limpa o display
- void Display_clear()
- {
- // Define endereços iniciais e finais de colunas e páginas
- Display_sendcmd (SSD1306_COLUMNADDR);
- Display_sendcmd (0);
- Display_sendcmd (SSD1306_LCDWIDTH-1);
- Display_sendcmd (SSD1306_PAGEADDR);
- Display_sendcmd (0);
- Display_sendcmd (7);
- // Preenche a memória com zeros
- for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++)
- {
- Wire.beginTransmission(DISP_ADDR);
- Wire.write(0x40); // Co=0, DC = 1
- for (uint8_t x=0; x<16; x++)
- {
- Wire.write(0);
- }
- Wire.endTransmission();
- }
- }
- // Desenho dos números de 0 a 9
- byte gc[][7] =
- {
- {0x3E, 0x61, 0x51, 0x49, 0x45, 0x43, 0x3E} // 0
- ,{0x40, 0x40, 0x42, 0x7F, 0x40, 0x40, 0x40} // 1
- ,{0x42, 0x61, 0x51, 0x49, 0x45, 0x42, 0x40} // 2
- ,{0x22, 0x41, 0x49, 0x49, 0x49, 0x49, 0x36} // 3
- ,{0x10, 0x18, 0x14, 0x12, 0x11, 0x7F, 0x50} // 4
- ,{0x27, 0x45, 0x45, 0x45, 0x45, 0x49, 0x31} // 5
- ,{0x3C, 0x4A, 0x49, 0x49, 0x49, 0x49, 0x32} // 6
- ,{0x41, 0x21, 0x11, 0x09, 0x05, 0x03, 0x01} // 7
- ,{0x36, 0x49, 0x49, 0x49, 0x49, 0x49, 0x36} // 8
- ,{0x26, 0x49, 0x49, 0x49, 0x49, 0x29, 0x1E} // 9
- };
- // Escreve um caracter na linha l(0 a 7), coluna c(0 a 16)
- void Display_write (byte l, byte c, byte car)
- {
- byte *pc = gc[car];
- // Endereça o caracter
- Display_sendcmd (SSD1306_COLUMNADDR);
- Display_sendcmd (c*8);
- Display_sendcmd (c*8 + 7);
- Display_sendcmd (SSD1306_PAGEADDR);
- Display_sendcmd (l);
- Display_sendcmd (l);
- // Escreve
- Wire.beginTransmission(DISP_ADDR);
- Wire.write(0x40); // Co=0, DC = 1
- for (uint8_t x = 0; x < 7; x++)
- {
- Wire.write(*pc++);
- }
- Wire.endTransmission();
- }
- // Envia sequência de comandos ao display
- void Display_sendcmd (byte *cmd, int nCmds)
- {
- Wire.beginTransmission(DISP_ADDR);
- Wire.write (0); // Co= 0. DC = 0
- for (int i = 0; i < nCmds; i++)
- {
- Wire.write(cmd[i]);
- }
- Wire.endTransmission();
- }
- // Envia um byte de comando ao display
- void Display_sendcmd (byte cmd)
- {
- Wire.beginTransmission(DISP_ADDR);
- Wire.write (0); // Co= 0. DC = 0
- Wire.write(cmd);
- Wire.endTransmission();
- }
Nenhum comentário:
Postar um comentário