LCD – Um guia (quase) completo!

Os LCDs (Liquid-Crystal Display) são módulos simples de serem conectados aos microcontroladores, sendo assim  muito úteis em projetos que necessitam de uma comunicação com o usuário. Os LCDs mais comuns possuem 16 colunas por 2 linhas, normalmente indicados por 16×2 ou 1602 e podem possuir 14 ou 16 pinos, sem e com backlight  (luz de fundo) respectivamente e são equipados com um controlador compatível com o chip HD44780 da Hitachi.  E é exatamente este modelo que vamos ver a seguir.

LCD 2 linhas por 16 colunas
LCD de 2 linhas por 16 colunas

 

Disposição das Linhas e Colunas

Tomando como exemplo um display de 16×2, a disposição dos caracteres estão numa matriz com as colunas numeradas de 0 a 15 e linhas numeradas 0 e 1.

LCD 16x2 - linhas e colunas

Pinos

A numeração e descrição dos pinos:

Pino   Descrição
VSS GND (terra)
VDD +5V
VO Ajuste do contraste – utilizar com um resistor fixo ou variável (potenciômetro ou trimpot)
RS Register Select – sinaliza a instrução ou caractere que está sendo escrito (linha de controle)
R/W Read/Write – sinaliza ao microcontrolador do LCD se a operação é de escrita ou gravação (linha de controle)
Enable Ativação do LCD – se nível baixo, ignora RS e R/W (linha de controle)
D0 Linha de dado
D1 Linha de dado
D2 Linha de dado
10 D3 Linha de dado
11  D4 Linha de dado
12 D5 Linha de dado
13  D6 Linha de dado
14  D7 Linha de dado
15  LED + backlight (anodo) +5V
16  LED – backlight (catodo) GND

Nos LCDs sem backlight não temos os pinos 15 e 16.

 

Módulo I2C para LCD

Uma outra opção, bem mais interessante, é a utilizar um módulo I2C acoplado ao display LCD, como o da figura a seguir.

Este módulo conta com um trimpot para ajuste do contraste e um jumper para ligar/desligar o LED de luz de fundo (backlight). Porém, podemos ligar e desligar este LED por software.

I2C

I2C (IIC – Inter Integrated Circuit) é um barramento de comunicação desenvolvido pela Philips, hoje NXP Semiconductors, para permitir troca de dados entre os componentes que residem na mesma placa de circuito impresso.  A vantagem de usar este tipo de barramento é que precisamos de apenas dois pinos para comunicação, além do pino de alimentação (5V) e o pino terra (GND).

Este módulo em específico possui o chip PCF8574.

Vamos usar  a  biblioteca Wire (nativa no software Arduino) para facilitar o desenvolvimento com o protocolo I2C. Os dois fios de comunicação são conhecidos por SDA (Serial Data Line) e SCL (Serial Clock Line).

Nas placas Arduino Uno e compatíveis, o

SDA é o pino analógico  4 (A4) e o

SCL é o pino analógico 5 (A5).

No Arduino Mega o SDA é o pino digital 20 e o SCL é o pino digital 21.

Os módulos I2C possuem um endereço único para cada módulo. A seleção do endereço é feita pela combinação dos pads A0, A1 e A2.  No caso deste módulo, o endereço padrão é 0x27 (os três pads isolados).  Para configurar outro endereço, conforme a tabela abaixo, solde (coloque em curto) os pads desejados. Por exemplo: pad sem solda é igual a 0 (zero), pad em curto é igual a 1.

A0 A1 A2 endereço
0 0 0 0x27
1 0 0 0x26
0 1 0 0x25
1 1 0 0x24
0 0 1 0x23
1 0 1 0x22
0 1 1 0x21
1 1 1 0x20

Quer dizer que poderíamos ligar até oito módulos LCD no mesmo Arduino, combinando os pads? Sim, mas lembre-se que existem outros módulos I2C no mercado, como módulos de relógio (RTC) e cada um deverá ter o seu próprio endereço.

 

Exemplo prático – monitor de temperatura com o LM35.

Vamos mostrar dois exemplos: sem e com o módulo I2C.

Providencie:

  • 1 sensor de temperatura LM35
  • 1 potenciômetro ou trimpot de 1KΩ (caso monte com a opção sem módulo I2C)
  • 1 módulo LCD 16 x 2 *
  • 1 protoboard
  • e muitos (muitos) fios jumpers.

* é muito mais econômico, caso você não possua um módulo LCD, comprá-lo com o módulo I2C já acoplado (soldado). Se você já possui um ou mais LCDs sem este módulo talvez seja vantajoso comprar o módulo I2C separado, mas terá que soldá-lo ao módulo LCD caso queira uma conexão definitiva entre os módulos.

 Conectando o LCD no Arduino – sem módulo I2C

A biblioteca LiquidCrystal (nativa no software Arduino) utiliza apenas as linhas de dados D4 a D7, ou seja, apenas 4 bits. Para controle/ajuste do contraste utilizamos um trimpot ou um potenciômetro de 1KΩ.

O sketch deverá incluir a biblioteca LiquiCrystal através da diretiva #include e para facilitar a manutenção, os pinos associados ao LCD estarão definidos pela diretiva #define.

Obs.:  toda diretiva de pré-processamento começa com o símbolo #. As diretivas não são comandos da linguagem C ou Arduino. São instruções que antes da compilação efetiva, são tratadas (processadas) no código fonte (sketch) e posteriormente entregues ao compilador.  Ah, a sintaxe também difere dos comandos/declarações C/Arduino. Não temos o ponto e virgula (;) ao termino de uma diretiva. Por convenção, o nome das constantes da diretiva #define são escritas em maiúsculas.

A principal linha do sketch (mais adiante) é

LiquidCrystal  lcd( RS, RW, EN, D4, D5, D6, D7 );

onde lcd é a representação da classe LiquidCrystal  (pode ser qualquer nome, mas lcd nos parece mais apropriado 😉  ).  Também indicamos os pinos de controle e os pinos de dados.

Outro destaque é  a função sprintf() da linguagem C (consulte a apostila do mini curso Linguagem C), que formata dados e copia-os para um array de caracteres. Isto facilita muito o trabalho, porém da forma como esta função está implementada no Arduino, não conseguimos manipular dados do tipo float, por isso temperatura foi definido como int.

Métodos usados no exemplo:

begin( num_colunas, num_ linhas )

Inicializa o LCD e especifica o seu tamanho (linhas e colunas). Note que a ordem dos parâmetros são número de colunas por número de linhas.

clear()

“Limpa” o LCD e posiciona o cursor nas coordenadas 0,0.

setCursor( coluna, linha )

Posiciona o cursor nas coordenadas coluna e linha.

print( dados, [base] )

Imprime dados no LCD a partir do posicionamento do cursor. O parâmetro base é opcional e só se aplica quando dados for numérico (consulte a apostila do curso Linguagem Arduino), tópico Serial.print).

print() também retorna o número de bytes impressos no LCD.

Acompanhe o diagrama abaixo para montagem. São muitos fios jumpers e nossa sugestão é começar pelo módulo LCD.

Olhando o LCD de frente, da esquerda para direita, temos:

pino LCD pino Arduino
VSS GND
VDD 5V
VO pino central do potenciômetro
RS 6
RW 7
E 8
D0 GND
D1 GND
D2 GND
D3 GND
D4 2
D5 3
D6 4
D7 5
A (LED backlight anodo) 5V
K (LED backlight catodo) GND

 

LM35 – oriente-se pela figura a seguir.

ORIGINAL] LM35 LM35DZ TEMPERATURE SENSOR | Shopee Malaysia

 

pino LM35 pino Arduino
VS 5V
Vout A0 (pino analógico zero)
GND GND

Exemplo

#include <LiquidCrystal.h>
#define RS 6
#define RW 7
#define EN 8
#define D4 2
#define D5 3
#define D6 4
#define D7 5
#define TEMP A0

float leitura;
int   temperatura;
char  msg[255];

LiquidCrystal lcd(RS, RW, EN, D4, D5, D6, D7);

void setup() {
  lcd.begin(2, 16);
  lcd.setCursor(2, 0);
  lcd.print("* EADuino *");
  delay(3000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Temperatura");
}

void loop() {
  leitura = analogRead( TEMP );
  temperatura = (5 * leitura * 100) / 1024;
  sprintf( msg, "%2d C", temperatura );
  lcd.setCursor(0, 1);
  lcd.print(msg);
  delay(5000);
}

 

Conectando o LCD no Arduino – com módulo I2C

Primeiro, verifique se você tem instalado a biblioteca LiquidCrystal_I2C. Vá até o gerenciador de bibliotecas (atalho CTRL-SHIFT-i), digite liquidcrystal. Optamos por instalar esta mostrada na figura.  Caso não encontre, uma boa fonte é o site  Arduino Library List (link no final deste artigo), porém para este módulo faça o download aqui https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c

 

 

Oriente-se pela imagem abaixo.

 

pino I2C pino Arduino
GND GND
VCC 5V
SDA A4
SCL A5

 

Note como o circuito fica muito (mas muito) mais simples. Além da economia de pinos Arduino.

 

 

No sketch a seguir instanciamos a classe

LiquidCrystal_I2C  lcd( 0x27, 16, 2 ); 

e informamos o endereço do módulo,  quantidade de colunas e linhas. Na função setup() iniciamos o módulo e na função loop() mantemos o backlight aceso por 10 segundos e em seguida desligamos o backlight por 20 segundos.

Métodos usados no exemplo:

init( )

Inicializa o LCD.

clear( ) – idêntico ao exemplo anterior

setCursor( ) – idêntico ao exemplo anterior

print( ) – idêntico ao exemplo anterior

noBacklight( )

desliga luz de fundo

backlight( )

liga luz de fundo

setBacklight( HIGH | LOW )

liga/desliga luz de fundo

Exemplo

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define TEMP A0

float leitura;
int temperatura;
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();
}

void loop() {
  leitura = analogRead( TEMP );
  temperatura = (5 * leitura * 100) / 1024;
  lcd.clear();
  lcd.backlight(); // ou setBacklight( HIGH );
  lcd.setCursor(0, 0);
  lcd.print("Temperatura");
  lcd.setCursor(10, 1);
  lcd.print(temperatura);
  lcd.print("C");
  delay(10000);
  lcd.noBacklight(); // ou setBacklight( LOW );
  delay(20000);
}

 

Criando novos caracteres (custom chars)

Com o método createChar( )  é possível criar e exibir caracteres personalizados no LCD. Isso é especialmente útil se você deseja exibir um caractere que não faz parte do conjunto de caracteres ASCII padrão.

Em módulos LCDs baseados no controlador HD44780 temos dois tipos de memórias: CGROM e CGRAM (memórias do gerador de caracteres).

  • O CGROM gera todos os padrões de caracteres de 5 x 8 pontos.
  • O CGRAM pode gerar até 8 padrões de caracteres definidos pelo usuário (personalizados).

Como sugestão utilize uma folha de papel quadriculado, separe os quadrados em grupos de 8 por 5 e preencha os quadrados que deseja mostrar ou, melhor ainda, acesse este site https://maxpromer.github.io/LCD-Character-Creator/.  Você pode escolher se é um LCD com ou sem I2C e ainda gerar os valores em binário ou hexadecimal. Depois é só colar o código  😎  .

Métodos usados no exemplo:

createChar( posiçao, mapa de bits )

armazena na  memória CGRAM, na posição de 0 a 7, o mapa de bits do caractere customizado.

write( posição )

exibe no display o caractere customizado na posição de memória de 0 a 7.

Carregue e execute o sketch a seguir.

 Exemplo

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

// caracteres personalizados - os vetores abaixo foram populados com numeros binarios
byte Heart[] = {
  B00000,
  B01010,
  B11111,
  B11111,
  B01110,
  B00100,
  B00000,
  B00000
};

byte Bell[] = {
  B00100,
  B01110,
  B01110,
  B01110,
  B11111,
  B00000,
  B00100,
  B00000
};

byte Alien[] = {
  B11111,
  B10101,
  B11111,
  B11111,
  B01110,
  B01010,
  B11011,
  B00000
};

byte Check[] = {
  B00000,
  B00001,
  B00011,
  B10110,
  B11100,
  B01000,
  B00000,
  B00000
};

byte Speaker[] = {
  B00001,
  B00011,
  B01111,
  B01111,
  B01111,
  B00011,
  B00001,
  B00000
};

byte Sound[] = {
  B00001,
  B00011,
  B00101,
  B01001,
  B01001,
  B01011,
  B11011,
  B11000
};

byte Skull[] = {
  B00000,
  B01110,
  B10101,
  B11011,
  B01110,
  B01110,
  B00000,
  B00000
};

byte Lock[] = {
  B01110,
  B10001,
  B10001,
  B11111,
  B11011,
  B11011,
  B11111,
  B00000
};

void setup() {
  lcd.init();
  lcd.backlight();

  // cria os novos caracteres
  lcd.createChar(0, Heart);
  lcd.createChar(1, Bell);
  lcd.createChar(2, Alien);
  lcd.createChar(3, Check);
  lcd.createChar(4, Speaker);
  lcd.createChar(5, Sound);
  lcd.createChar(6, Skull);
  lcd.createChar(7, Lock);
  lcd.clear();
}

void loop() {
  byte coluna = 0;
  for ( byte indice = 0; indice <= 7; indice++ ) {
    lcd.setCursor(coluna, 1);
    lcd.write( indice );
    coluna += 2; //incrementa a variavel de dois em dois
  }
}

Um outro exemplo com os vetores populados com números hexadecimais. Preferimos com números binários, pois praticamente você transcreve o que desenhou para o valor binário.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

// caracteres personalizados - os vetores abaixo foram populados com numeros hexadecimais
byte sino[]     = {0x4, 0xe, 0xe, 0xe, 0x1f, 0x0, 0x4};
byte nota[]     = {0x2, 0x3, 0x2, 0xe, 0x1e, 0xc, 0x0};
byte relogio[]  = {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0};
byte coracao[]  = {0x0, 0xa, 0x1f, 0x1f, 0xe, 0x4, 0x0};
byte pato[]     = {0x0, 0xc, 0x1d, 0xf, 0xf, 0x6, 0x0};
byte checklist[]= {0x0, 0x1, 0x3, 0x16, 0x1c, 0x8, 0x0};
byte cruz[]     = {0x0, 0x1b, 0xe, 0x4, 0xe, 0x1b, 0x0};
byte enter[]    = {0x1, 0x1, 0x5, 0x9, 0x1f, 0x8, 0x4};

void setup() {
  lcd.init();
  lcd.backlight();

  // cria os novos caracteres
  lcd.createChar(0, sino);
  lcd.createChar(1, nota);
  lcd.createChar(2, relogio);
  lcd.createChar(3, coracao);
  lcd.createChar(4, pato);
  lcd.createChar(5, checklist);
  lcd.createChar(6, cruz);
  lcd.createChar(7, enter);

  lcd.clear();
}

void loop() {
  byte coluna = 0;
  for ( byte indice = 0; indice <= 7; indice++ ) {
    lcd.setCursor(coluna, 1);
    lcd.write( indice );
    coluna += 2; //incrementa a variavel de dois em dois
  }
}

 

Outros Métodos

home()

posiciona o cursor no topo/esquerda do display – não limpa a tela.

blink()
noBlink()

exibe/esconde cursor em formato de bloco na próxima coluna disponível.

cursor()
noCursor()

exibe/esconde cursor em formato sublinhado (underscore) na próxima coluna disponível.

display()
noDisplay()

liga/desliga o display, mas não apaga o conteúdo em memória.

scrollDisplayLeft()
scrollDisplayRight()

desloca toda a tela do display uma posição a esquerda/direta.

Exemplo

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);


void setup() {
  lcd.init();
  lcd.backlight();
}

void loop() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("EADuino");
  lcd.setCursor(0, 1);
  lcd.print("Bem-vindo(a)!");
  delay(5000);

  lcd.clear();
  lcd.print("cursor ");
  lcd.cursor();
  delay(5000);
  lcd.home();
  lcd.print("sem cursor "); 
  lcd.noCursor();
  delay(5000);

  lcd.clear();
  lcd.print("cursor - bloco");
  lcd.blink();
  delay(5000);
  lcd.print("sem cursor    "); 
  lcd.noBlink();
  delay(5000);

  lcd.clear(); 
  lcd.print("EADuino!"); 
  delay(2000);
  lcd.noDisplay();
  delay(2000);
  lcd.display();
  delay(5000);

  lcd.clear();
  int bloco, tamanho, quantidade_blocos, caracteres_restantes;

  tamanho = lcd.print("EADuino - Cursos de Arduino Online!");
  delay(3000);
  quantidade_blocos = tamanho / 16;
  caracteres_restantes = tamanho % 16;

  for (bloco = 1; bloco <= quantidade_blocos - 1; bloco++) {
    for (byte scroll = 0; scroll < 16; scroll++) {
      lcd.scrollDisplayLeft();
      delay(200);
    }
    delay(3000);
  }

  if ( caracteres_restantes > 0 ) {
    for (byte scroll = 0; scroll <= caracteres_restantes; scroll++) {
      lcd.scrollDisplayLeft();
      delay(200);
    }
    delay(3000);
  }
}

Links úteis