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