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
1  VSS GND (terra)
2  VDD +5V
3  VO Ajuste do contraste – utilizar com um resistor fixo ou variável (potenciômetro ou trimpot)
4  RS Register Select – sinaliza a instrução ou caractere que está sendo escrito (linha de controle)
5  R/W Read/Write – sinaliza ao microcontrolador do LCD se a operação é de escrita ou gravação (linha de controle)
6  Enable Ativação do LCD – se nível baixo, ignora RS e R/W (linha de controle)
7  D0 Linha de dado
8  D1 Linha de dado
9  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


 

Deixe um comentário