Introdução
Módulos de display LCD de caracteres alfanuméricos são interfaces de comunicação visual muito úteis e atraentes. Eles se encontram em quase todos os aparelhos domésticos, eletroeletrônicos, automóveis, instrumentos de medição etc. São dispositivos que possuem interfaces elétricas padronizadas e recursos internos gráficos e de software que permitem facilmente a permuta por outros de outros fabricantes, sem que seja necessário alterar o programa de aplicação. Por ser altamente padronizado seu custo é baixo. É um recurso antigo, deve ter uns vinte anos de idade ou mais, mas continua atual, com suas inúmeras formas, cores, tamanhos e preços. A tecnologia predominante continua sendo o LCD (Liquid Crystal Display), porém já se pode encontrar alguns baseados em LEDs orgânicos (OLED).
O módulo de display LCD representa um avanço tecnológico enorme se comparado com os primeiros displays a LED de 7, 14 ou 16 segmentos. Leia mais sobre os displays a LED no artigo técnico Displays de LED de 7 segmentos [1].
Especificações
Os módulos LCD são especificados principalmente por sua capacidade gráfica de comunicação, ou seja, o número de caracteres por linha e o número de linhas. Alguns valores típicos para essas especificações são:
Número de caracteres: 8, 12, 16, 20, 24 e 40
Número de linhas: 1, 2 e 4
Algumas configurações típicas de um módulo de display LCD

Outras especificações importantes que devemos considerar são as dimensões físicas do módulo, a tensão de alimentação, a disposição física dos pontos de conexão externa, a posição desses pontos de conexão com relação ao display, dos lados esquerdo ou direito, em cima ou embaixo, o tipo de interface eletrônica, paralela (predominante) ou serial, backlight (luz de fundo) e finalmente o controlador do display.
Quando se consulta o manual de um módulo de display LCD, em geral se obtém apenas uma página contendo as dimensões e especificações elétricas. Isso pode parecer pouco mas é mais do que o suficiente. Observe na Figura 3 as especificações do módulo retratado na Figura 1.
Interface de conexão externa
A interface de conexão com um microcontrolador também é padronizada. Ela tem de 14 a 16 pinos, dependendo se o módulo tem ou não backlight para permitir a leitura do display em ambientes escuros. Na Tabela 1 se pode observar a descrição dos pinos.
Tabela 1: Descrição dos pinos da interface de conexão
Repare que o pino 3 foi reservado para ajustar o contraste do display. Isso permite que você ajuste esse contraste para a melhor visibilidade do texto. Na Figura 4 se pode observar um circuito simples para essa função.
O contraste muitas vezes varia em função da temperatura ambiente. Para isso pode-se utilizar o circuito ilustrado na Figura 5, por exemplo, que compensa essa variação.
Se for utilizado o backlight é necessário providenciar as conexões e dimensionar corretamente o resistor para limitar a corrente nos LEDs. Nas especificações do módulo WH2001B (Figura 6), se pode observar que a corrente típica dos LEDs de backlight (IF) é de 60 mA e a máxima de 75 mA com tensões diretas (VF) correspondentes respectivamente a 3,5 e 3,6V.
ATENÇÃO: Nunca ligue a fonte de alimentação diretamente nos terminais de backlight. Isto provocará o aquecimento excessivo do display e a queima prematura do módulo.
A Figura 7 ilustra um circuito típico para ativar o backlight. Utilizando os dados da Figura 6, podemos calcular o resistor de limitação de corrente.
Rlimit = (5 – 3,5)V / 60mA = 25 Ohms (Pode-se utilizar os valores comerciais de 22 ou 27 Ohms 5%)
Quando for projetada a conexão do módulo com um microprocessador, é sempre bom lembrar que o operação do módulo LCD é relativamente lenta, se comparada com a de um microcontrolador. Na Figura 8 se pode observar os diagramas de tempos de leitura e escrita da interface com o microcontrolador. É muito importante que se estude esses diagramas e que sejam respeitados os tempos especificados nesses diagramas.
Figura 8: Diagramas de tempo típicos para a leitura e a escrita dos módulos LCD
Uma boa maneira de se verificar se o módulo está pronto para um novo acesso é testar o busy–flag (bit de sinalização de ocupado). Para acessar esse flag, deve-se acionar os bits de controle RS = 0 e R/W = 1 (Read) e o flag poderá ser lido em DB7. Deve-se esperar o término das operações internas enquanto DB7 estiver em 1. Os detalhes você pode ler no manual do controlador.
Controladores de display LCD
No intuito de manter a padronização e compatibilidade entre os módulos de display de diversos fabricantes, adotou-se no início que o componente da Samsung – KS0066 [2] seria o padrão para esse tipo de módulo. É muito comum encontrarmos especificações de controladores como compatíveis com o KS0066. Outros controladores compatíveis: Samsung S6A0069 [3], Sitronix – ST 7066 [4] e Hitachi – HD44780 [5]. O HD44780 atualmente é o mais utilizado.
Quando se compra um módulo de display LCD é interessante se saber qual é o controlador que é utilizado nesse módulo. Os recursos oferecidos pelos controladores variam um pouco além dos recursos básicos comuns para todos. Alguns recursos comuns são a interface com um microcontrolador configurável para 4 ou 8 bits, memória ROM interna com os gráficos correspondentes a cada caractere disponível, opções de Fonts para os caracteres, entre outros. Na Figura 9 se pode observar como é formado graficamente o caractere “A”, numa matriz de pontos (pixels), onde os 1s indicam pixels acesos e os 0s, os pixels apagados.
A seguir serão mostrados os comandos ou instruções que podem ser utilizados nos módulos. O código dessas instruções também é padronizado. Serve para a maioria dos módulos. Veja a Tabela 2.
Tabela 2: Instruções de um módulo LCD
Um outro aspecto importante na utilização do módulo é respeitar a sequência de inicialização, após a energização do módulo. As especificações do controlador fixam o seguinte fluxo (Figura 10).
Figura 10: Sequência de inicialização para operação em 8 bits
Observe que na inicialização existem situações em que não é possível testar o busy-flag, porém é necessário esperar no mínimo o tempo especificado para que o controlador esteja pronto para receber mais um comando.
Depois e inicializado, o módulo pode receber caracteres para serem apresentados no display. Para isso, basta transferi-los de forma adequada para o endereço interno ao controlador correspondente ao da DDRAM (Display Data RAM). O endereço inicial da primeira linha é 0x00, o da segunda é 0x40 etc. Os caracteres deverão estar codificados em ASCII. Simples, não?
Exemplo
A seguir serão apresentados alguns trechos de código desenvolvidos no MPLAB e o compilador C30 da Microchip, para um dsPIC32. Os dados de 8 bits para o display LCD foram conectados ao Port D e os demais sinais em outro Port. O módulo do display utilizado é um módulo no padrão 16 x 2. A seguir se pode observar a definição de diversas constantes e parâmetros para o módulo LCD.
Observação: Como o código está escrito em linguagem C, é muito fácil adaptá-lo para qualquer outro microcontrolador.
//*************** DISPLAY LCD ***********************
#define DADO_DISPLAY PORTD
#define MASCARA_DO_DISPLAY 0x00ff
#define RESET_LCD _LATE4 //_RE4
#define RD_WR_LCD _LATE5 //_RE5
#define ENABLE_LCD _LATE6 //_RE6
#define BUSY_FLAG _RD7
#define COMANDO_LCD 0
#define DADOS_LCD 1
// Define os comandos e configuração do DISPLAY LCD
#define CLR_DISPLAY 0b00000001
#define RETURN_HOME 0b00000010
//***
#define ENTRY_SET_MODE 0b00000100 // A ser composto com os parametros abaixo
// Direção do cursor
#define INCREMENT 0b00000010
#define DECREMENT 0b00000000
// Deslocamento do Display
#define EN_SHIFT 0b00000001
#define NO_SHIFT 0b00000000
//***
#define DISPLAY_ON_CONTROL 0b00001000 // A ser composto com os parametros abaixo
// Liga ou desliga o display
#define DISPLAY_ON 0b00000100
#define DISPLAY_OFF 0b00000000
// Liga ou desliga o cursor
#define CURSOR_ON 0b00000010
#define CURSOR_OFF 0b00000000
// Pisca o cursor
#define CURSOR_BLINK_ON 0b00000001
#define CURSOR_BLINK_OFF 0b00000000
//***
#define CURSOR_DISPLAY_SHIFT 0b00010000 // A ser composto com os parametros abaixo
// Bits de configuração do deslocamento
#define CURSOR_LEFT_AC_DECR 0b00000000
#define CURSOR_RIGHT_AC_INCR 0b00000100
#define SHIFT_DISPLAY_LEFT 0b00001000
#define SHIFT_DISPLAY_RIGHT 0b00001100
//***
#define FUNCTION_SET 0b00100000 // A ser composto com os parametros abaixo
// Tamanho do bus de dados
#define BUS_8_BITS 0b00010000
#define BUS_4_BITS 0b00000000
// Numero de linhas do Display
#define DISPLAY_2_LINHAS 0b00001000
#define DISPLAY_1_LINHA 0b00000000
// Tamanho do fonte
#define FONTE_5x11 0b00000100
#define FONTE_5x8 0b00000000
//***
#define SET_CGRAM_ADDRESS 0b01000000
#define SET_DRAM_ADDRESS 0b10000000
#define END_INICIAL_LINHA_1 0x00
#define END_INICIAL_LINHA_2 0x40
// Saudacao
int chSaudacaoDeAbertura[16] = {' ', ' ', 'E', 'M', 'B', 'A', 'R', 'C', 'A','D','O','S',' ',' ', 0x0};
int chSaudacaoDeAbertura_II[16] = {' ', 'D', 'i', 's', 'p', 'l', 'a', 'y',' ',' ','L','C','D',' ',' ', 0x0};
char fchFlagApresentouSaudacao = 0;
char fchFlagDisplayLimpo = 0;
//***************************************************
A seguir, se pode observar as rotinas que acessam o módulo propriamente dito. Uma rotina que escreve um caractere e outra que escreve uma frase. Observe que na rotina que escreve um caractere é utilizado um temporizador (T4) para realizar as temporizações necessárias.
Rotina Escreve no Display LCD
void Escreve_no_Display_LCD(char chDado, char chModo)
{
//********************************************************************************************
//
// Rotina que escreve um caracter para o Display LCD
//
// Entradas: Caracter a ser transmitido
// Modo de transmissao: COMANDO - 0
// DADOS - 1
// Saidas: Nenhuma
//
//*******************
char chfTerminou = 0;
char chContadorDeTimeOut = 0;
char chfFalhaDeBusy = 0;
// Inicializa os sinais de controle
RESET_LCD = chModo;
RD_WR_LCD = 0b0;
ENABLE_LCD = 0b0;
// Inicializa temporizacao
PR4 = ESPERA_MEIO_CICLO;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
// Transfere o dado
DADO_DISPLAY = chDado & MASCARA_DO_DISPLAY;
ENABLE_LCD = 0b1;
// Inicializa temporicacao
PR4 = ESPERA_MEIO_CICLO;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
while (chfTerminou == 0)
{
Aguarda sinal de OK */
ENABLE_LCD = 0b0;
// Inicializa temporicacao
PR4 = ESPERA_MEIO_CICLO;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
RD_WR_LCD = 0b1;
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario// Espera o tempo m¡nimo
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
ENABLE_LCD = 0b1;
// Inicializa temporicacao
PR4 = ESPERA_MEIO_CICLO;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario// Espera o tempo m¡nimo
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
if (BUSY_FLAG == 1)
{
// Incrementa o contador de time-out
chContadorDeTimeOut++;
if (chContadorDeTimeOut == CONTAGEM_TIME_OUT_LCD)
{
// Sinaliza que terminou a espera
chfTerminou =1;
chfFalhaDeBusy = 1;
}
}
else
{
// Sinaliza que terminou a espera
chfTerminou =1;
}
}
// Testa se houve falha
if (chfFalhaDeBusy == 1)
{
}
// Termina o ultimo ciclo
ENABLE_LCD = 0b0; // Inicializa temporicacao
_TON_T4 = 0b0; // Desiga Timer
_T4IE = 0b0; // Desabilita a interrupcao
_T4IF = 0b0; // Zera o flag de interrupcao
TMR4 = 0x0000;
PR4 = ESPERA_MEIO_CICLO;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
RD_WR_LCD = 0b0;
RESET_LCD = 0b0;
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
// Espera o display executar a atualizacao
_TON_T4 = 0b0; // Desiga Timer
_T4IE = 0b0; // Desabilita a interrupcao
_T4IF = 0b0; // Zera o flag de interrupcao
TMR4 = 0x0000;
PR4 = ESPERA45us;
// Atencao!!!! a variavel abaixo deve ser inicializada antes de habilitada a interrupcao!!!
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
}
Rotina Escreve uma frase
void Escreve_Frase_no_Display_LCD(int *pchDado, int nEndereco)
{
//************************************************************************************
//
// Rotina que escreve um "string" ASCII no display LCD
//
// Entradas: Ponteiro para "string", terminado com 0, máximo 17 caracteres incluindo o 0
// Endereco onde posicionar o primeiro caractere
//
// Saidas: Nenhuma
//
//****
// Acerta o endereco
Escreve_no_Display_LCD((SET_DRAM_ADDRESS | nEndereco), COMANDO_LCD);
// Transfere a frase
while (*pchDado != 0)
{
// Escreve a saudacao
Escreve_no_Display_LCD(*pchDado, DADOS_LCD);
pchDado++;
}
}
O próximo trecho é inserido no programa principal na parte de inicializações.
//=== Inicializa PORT D ===
//
PORTD = 0x0000; // Inicializa PORTD
TRISD = 0xCF00; // Inicializa a configuração de E/S (1 = E/ 0 = S)
ODCD = 0x00FF; // Inicializa as saídas de coletor aberto
//
//=== Inicializa DISPLAY LCD ===
//
// Aguarda periodo minimo antes de inicializar - 30ms
char chAuxiliar = 3;
while (chAuxiliar-- > 0)
{
PR4 = ESPERA10ms;
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
}
// Define o funcionamento do display
Escreve_no_Display_LCD((FUNCTION_SET | BUS_8_BITS | DISPLAY_2_LINHAS | FONTE_5x11), COMANDO_LCD);
// Liga o Display
Escreve_no_Display_LCD((DISPLAY_ON_CONTROL | DISPLAY_ON | CURSOR_OFF | CURSOR_BLINK_OFF), COMANDO_LCD);
// Limpa o Display
Escreve_no_Display_LCD((CLR_DISPLAY), COMANDO_LCD);
// Espera limpar o display
PR4 = ESPERA1550us;
nTerminoDeTemporizacaoDisplay = 0; // Inicializa flag de termino
_T4IE = 0b1; // Habilita a interrupcao
_TON_T4 = 0b1; // Liga Timer
// Espera o tempo necessario
while (nTerminoDeTemporizacaoDisplay == 0)
{
}
// Escreve na primeira linha
Escreve_Frase_no_Display_LCD(&chSaudacaoDeAbertura[0], END_INICIAL_LINHA_1 );
// Escreve restante na segunda linha
Escreve_Frase_no_Display_LCD(&chSaudacaoDeAbertura_II[0], END_INICIAL_LINHA_2 );
fchFlagApresentouSaudacao = 1;
Se você quiser se aprofundar um pouco mais nesse assunto, sugiro que você leia o artigo técnico Display LCD [7] de Ilton L. Barbacena e Claudio Afonso Fleury.














Bom dia Henrique. você sabe me dizer se o controlador de um display 128×64 Sitronix St7565 é compatível com esses módulos de arduino como o St7920, se há algum site para consulta? lista? Obrigado
Amigo,
Pode tirar uma duvida.
o display lcd2004 é compativel com display gdm2004?
Parabéns, esclarecedor o artigo
Muito bom! Obrigado pela aula.
Parabéns! Pela Materia o Sr poderia fazer uma matéria de tipos de comunicação para si temas embarcados.
Caro Henrique, boa noite ! Gostei muito do seu tutorial. Me remeteu aos tempos em que tínhamos que programar “na unha” a operação desses displays.
Uma dúvida: Qual editor de códigos é utilizado para colocar esses códigos na página do blog, de tal forma que fiquem com as linhas com contrastes diferentes e numeradas ?
Um abraço !
Olá Bernardo,
Para postar código-fonte utilizamos esse plugin:
https://wordpress.org/plugins/crayon-syntax-highlighter/
Obrigado Diego !!!
Bons tempos, Bernardo.
Abraço