Manual
do
Maker
.
com
O que tem de tão legal nesse display Nokia 5110? Primeiramente, o controle que você pode ter sobre cada pixel, de modo que você poderá desenhar o que desejar e com isso exibir relógios, gauges, progress bar e tudo isso junto utilizando Arduino!
Nesse tutorial de hoje, veremos a maneira mais simples de interagir com esse display pra que você possa começar agora mesmo a brincar com ele. Vamos começar pela lista de materiais:
Eu preferi fazer uma plaquinha pra evitar a protoboard e também para estrear meus resistores SMD. Tentei outras vezes e só deu errado, pela primeira vez acertei (aplausos)! Porém, estou obtendo ruidos na tela. No video eu mostro o "shot zero" com uma barra de progresso nessa placas, utilizando um Arduino Pro Mini 5V.
Os pinos do Arduino a serem utilizados são a sequência de 3 a 7 e também você precisará do GND comum. Ainda, alimento o Arduino com 5V mas o display é 3V3, por isso coloquei um regulador de tensão ( na placa que fiz - aplausos novamente, porque esse também é SMD) e um diodo no GND pra garantir que nada queime em caso de fios invertidos na alimentação. Outra opção seria uma ponte de diodos, mas estou sendo o mais simplista possível.
Esse display tem uma resolução bacana de 84x48 pixels. Isso é mais do que um ícone grande de computador, que varia entre 32x32 ou 64x64. Isso significa que você pode desenhar com boa compreensão, mas obviamente a resolução não será a mesma de um computador porque os pixels são grandes.
Esse display está disposto em um circuito quadrado de 4.5cm e usa um controlador Philips PCD8544. Isso é o que as pessoas não sabem porque normalmente se fala apenas "display de Nokia".
Tem circuitos azuis que descrevem tensão em 5V e 3V3, mas esse vermelho certamente é 3V3, operando desde 2.7V.
Seu consumo é ínfimo; inferior a 20mA, de modo que poderia seguramente ser ligado diretamente ao 3V3 de um Arduino, mas como a alimentação do Arduino Pro Mini fiz externamente, preferi colocar um regulador de tensão pra pegar alimentação pro display.
Nesse link você terá acesso ao datasheet com muitas informações interessantes, mas que provavelmente serão dispensadas por você, considerando o uso de uma biblioteca pronta.
A primeira coisa que faço quando preciso de imagens é ir ao Google Images. Lá certamente terá tudo o que você precisa pra não precisar reinventar a roda. Eu peguei essa imagem abaixo que é muito parecido com o que fiz na placa de prototipagem, com algumas pequenas resalvas.
Eu não ia usar potenciômetro, mas aí eu não poderia aproveitar o desenho acima (e com isso não sei se estou tendo mais ou menos trabalho em não redesenhar). Já no exemplo com Arduino UNO eu não alimentei o backlight do display.
Tem várias; várias "mesmo". Se você abrir a IDE do Arduino e ir em Sketch->Include Library->Library Manager e digitar "5110", verá de imediato algumas opções, mas me parece que a biblioteca da Adafruit é a mais completa porque tem funções prontas para desenho, então como queremos por o negócio pra funcionar logo, vamos direto nela, buscando-a através desse link.
Se quiser experimentar mais algumas além das já citadas, você pode pegar aqui, aqui e aqui. Não se preocupe, mal não fará, sugiro que experimente todas.
Em algumas dessas bibliotecas você pode definir pinos diferentes do padrão, caso seu projeto já esteja utilizando os atualmente descritos. Algumas estão no código principal, outras em um dos arquivos de include, é fácil achar e modificar.
É facílimo manipular cada pixel desse display. Mas precisamos de alguns conceitos básicos para isso, então primeiramente devemos definir que a matriz de um caracter é composto por 5 colunas de 8 linhas. Pense nisso como uma (inheca) planilha do excell. A segunda coisa a pensar é nos endereçamentos, que podem ser feitos de diversas maneiras, mas pra conseguir desenhar um gráfico em um espaço pequeno, vou utilizar a representação hexadecimal:
0x01 | 0x01 | 0x01 | 0x01 | 0x01 |
0x02 | 0x02 | 0x02 | 0x02 | 0x02 |
0x04 | 0x04 | 0x04 | 0x04 | 0x04 |
0x08 | 0x08 | 0x08 | 0x08 | 0x08 |
0x10 | 0x10 | 0x10 | 0x10 | 0x10 |
0x20 | 0x20 | 0x20 | 0x20 | 0x20 |
0x40 | 0x40 | 0x40 | 0x40 | 0x40 |
0x80 | 0x80 | 0x80 | 0x80 | 0x80 |
Como você pode notar, os endereços são os mesmos, o que faz todo o sentido. Mas isso precisa ser colocado em um array para dizer qual posição está sendo manipulada. Então:
static byte my_char[] = {0xff, 0x00, 0x00, 0x00, 0x00};
Isso significa que na coluna 1 foram "ligados" todos os 8 pixels. Esse resultado é obtido pela soma dos valores hexadecimais. Para fazer essa conta, você pode utilizar uma calculadora no computador com capacidade de conversão de bases ou ainda, fazer a conta em bits:
bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
decimal | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Depois é só preencher o valor desejado, somando os valores decimais dessa tabela anterior. Isto é, invés de colocar em hexadecimal, você poderia colocar em bits ou em decimal:
#binario
static byte my_char = {0b11111111,0,0,0,0};
#decimal
static byte my_char = {255,0,0,0,0}
Depois, basta utilizar o método drawBitmap da biblioteca em questão:
lcd.drawBitmap(thermometer, THERMO_WIDTH, THERMO_HEIGHT);
Acalme-se e aproveite a leitura. Até aqui estamos falando das possibilidades, do funcionamento, como é escrever uma letra do display, como os caracteres são formados; só compreenda, chegaremos em excelentes exemplos.
Essa parte pode ser interessante se você quiser dominar o display (não que esse seja o domínio, mas é o caminho). O código para escrever no display diretamente sem biblioteca é bastante curto e baseando-se no exemplo de manipulação por endereço, vamos ver o que pode ser feito.
É a representação mais fácil. Pense que você tem uma coluna de 8 bits. Ponto final. Desse modo você só precisa escrever sequencialmente de 8 em 8 bits. Se quiser escrever a letra "L", definindo que cada letra deva ter 8 bits de altura por 5 de largura, ficaria assim:
static byte my_char3[] = {0b11111111,0b10000000,0b10000000,0b10000000,0b10000000};
Talvez não lhe faça muito sentido de imediato, mas como estamos escrevendo do bit mais significativo, pense que estamos escrevendo da esquerda para a direita e de baixo para cima. Logo, o primeiro campo do array é a coluna preenchida e os demais campos são 1 bit do rodapé do caracter.
1 | ||||
1 | ||||
1 | ||||
1 | ||||
1 | ||||
1 | ||||
1 | ||||
1.... | 1 | 1 | 1 | 1 |
Tendo montado seu caracter, basta rodar um loop para escrevê-lo na tela. No código de exemplo escrevi alguma sujeira na tela para mostrar a escrita em binário, decimal e hexadecimal. Isso significa que não importa o que você usar como base, servirá perfeitamente. Em seguida está o caracter "L" utilizando apenas 1 coluna por 1 linha. Você pode fazer o que quiser, basta montar suas estruturas.
Se definirmos que o caracter deve ter 8x5 pixels, então uma função pode ajudar na escrita de letra a letra escrevendo os binários de uma maneira um pouco mais simplificada:
void writePixels(unsigned char *data){
digitalWrite(PIN_DC, HIGH); //Voce pode mandar dado ou comando para o display. Por isso, necessita informar
digitalWrite(PIN_SCE, LOW); //abre recepcao de dados
for (int i=0;i<5;i++){
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data[i]);
}
digitalWrite(PIN_SCE, HIGH); //Fim da recepcao de dados
}
O exemplo está funcional no código, como você pode ver.
Isso é um exemplo extremanente bobo, mas para introdução é perfeito para mostrar uma animação na tela. O código que disponho a seguir está devidamente comentado, inclui uma função para limpar a tela e os exemplos supracitados. Em outros artigos patrocinados pela Autocore Robóticafaremos melhores aplicações para esse display, por enquanto já é informação o suficiente para você brincar com liberdade.
Enfim, o resultado final desse tutorial está nesse código:
//Nokia 5110 sem bibliotecas
//Pinos
#define PIN_SCE 7 //display: 2
#define PIN_RESET 6 //display: 1
#define PIN_DC 5 //display: 3
#define PIN_SDIN 4 //display: 4
#define PIN_SCLK 3 //display: 5
//LCD
#define Nokia5110_X 84
#define Nokia5110_Y 48
const int CONTRAST = 175; // > 128 & < 256
void setup(void){
pinMode(PIN_SCE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_SDIN, OUTPUT);
pinMode(PIN_SCLK, OUTPUT);
digitalWrite(PIN_RESET, LOW);
digitalWrite(PIN_RESET, HIGH);
writeToNokia5110(LOW, 0x21 ); // Function set: PD=0 (On); V=0 (Horizontal); H=1 (Extended instruction set)
writeToNokia5110(LOW, 0xBA ); // Contraste
writeToNokia5110(LOW, 0x04 ); // Temperatura aqui se refere a luminosidade
writeToNokia5110(LOW, 0x14 ); // Datasheet...
writeToNokia5110(LOW, 0x20 ); // Function set: PD=0 (On); V=0 (Horizontal); H=0 (Basic instruction set)
writeToNokia5110(LOW, 0x0C ); // Display control = 10 (Normal)
clearDisplay();
}
void clearDisplay(){
//Limpa o array preenchendo com 0 todos os bits sequencialmente
for (int i=0; i<((Nokia5110_X * Nokia5110_Y)/8); i++){
writeToNokia5110(HIGH, 0x00);
}
}
void writeToNokia5110(byte dataOrCommand, byte data){
/*
Logica:
- Avisa que vai mandar dados
- Habilita a recepcao de dados
- Envia os dados (da esquerda para a direita)
- Encerra o envio
*/
digitalWrite(PIN_DC, dataOrCommand); //Voce pode mandar dado ou comando para o display. Por isso, necessita informar
digitalWrite(PIN_SCE, LOW); //abre recepcao de dados
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); //MSB eh "Most Significant Bit". O mais significante fica aa esquerda
digitalWrite(PIN_SCE, HIGH); //Fim da recepcao de dados
}
void writePixels(unsigned char *data){
digitalWrite(PIN_DC, HIGH); //Voce pode mandar dado ou comando para o display. Por isso, necessita informar
digitalWrite(PIN_SCE, LOW); //abre recepcao de dados
for (int i=0;i<5;i++){
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data[i]);
}
digitalWrite(PIN_SCE, HIGH); //Fim da recepcao de dados
}
//Barra de progresso
void progressBar(int width) {
//Referente a duas areas de caractere
static byte my_char1[] = {0xff, 0x00};
//Valor em decimal, binario e hexadecimal. 80 e 0b10100000 sao
//o mesmo valor. 0b01110101 e 0x75 sao o mesmo valor, ou seja,
//escolha a base desejada, mas eh mais facil escrever em binario
//por parecer um "bordado"
static byte my_char2[] = {80, 0b01110101,0x75,0b01010000};
//Comando: posicionamento do cursor
writeToNokia5110( 0, 0x80 | 0); // Coluna 0
writeToNokia5110( 0, 0x40 | 0); // Linha 0
//Dado: escreve os valores de cada caractere no array
writeToNokia5110(HIGH, my_char2[0]);
writeToNokia5110(HIGH, my_char2[1]);
writeToNokia5110(HIGH, my_char2[2]);
writeToNokia5110(HIGH, my_char2[3]);
writeToNokia5110(HIGH, 0b11100111);
writeToNokia5110(HIGH, 0b00000000);
writeToNokia5110(HIGH, 0b00000000);
//escrever o "L" do tutorial:
static byte my_char3[] = {0b11111111,0b10000000,0b10000000,0b10000000,0b10000000};
for (int i=0;i<5;i++){
writeToNokia5110(HIGH,my_char3[i]);
}
static byte b[] = {0b00000000,0b00000000,0b00000000,0b00000000,0b00000000};
writePixels(b);
static byte c[] = {0b10101010,0b01010101,0b10101010,0b01010101,0b10101010};
writePixels(c);
//Comando: reposicionamento do cursor
writeToNokia5110( 0, 0x80 | 0); // Coluna 0
writeToNokia5110( 0, 0x40 | 2); // Linha 2
//Array da barra de progresso
for (int i=0; i<width; i++){
if (i%2 == 0){
writeToNokia5110(HIGH, my_char1[0]);
}
else{
writeToNokia5110(HIGH, my_char1[1]);
}
}
}
void loop(void)
{
//dentro dos limites que estabelecemos, define o contraste a mostrar
for (int i = 175;i<200;i++){
//definicao da barra
int width = map(i, 175, 200, 0, Nokia5110_X);
writeToNokia5110(LOW, 0x20 ); // Function set: PD=0 (On); V=0 (Horizontal); H=0 (Basic instruction set)
writeToNokia5110(LOW, 0x0C ); // Display control = 10
progressBar(width);
delay(350);
}
progressBar(Nokia5110_X);
clearDisplay();
}
O video é só uma demonstração da prova de conceito, incluindo um teste com a biblioteca da Adafruit.
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.