Você está na página 1de 52

Conectando um display LCD no PIC

Um diferencial em qualquer projeto microcontrolado utilizao de um display LCD


para indicao de parmetros e informaes diversas.
Um simples Display LCD 162 torna o projeto muito mais amigvel ao usurio alm de
aumentar a gama de funes e operaes com um custo relativamente baixo.
O fato da simplicidade de como se configura um Display LCD para trabalhar em
conjunto com um microcontrolador PIC faz com que se pense duas vezes em no
utiliz-lo em seus projetos.
A seguir um tutorial de configurao do Display LCD 162 em um programa para o
PIC escrito em linguagem C.

Vamos realizar a conexo de um LCD paralelo que utiliza o processador Hitachi


HD44780 ou KS0066U a um microcontrolador PIC , utilizando apenas 4 vias de dados.

Estes LCDs so muito simples, no possuem recursos como gerenciamento automtico


de pixels, no so coloridos (full color), no possuem iluminao ativa entre outras
limitaes, mas ainda so largamente utilizados na indstria. Basta ver que muitas
registradoras, equipamentos hand-held, sistemas de informao de computadores
servidores entre outros, ainda utilizam largamente este dispositivo.

Exemplo de equipamentos comerciais que utilizam display LCD


Podemos fazer um paralelo utilizao de Displays de 7 seguimentos para constatar a
simplicidade do uso e economia de recursos do microcontrolador quando escolhemos
um Display LCD (Hitachi HD44780 ou KS0066U):

Display 7 seguimentos
Abaixo temos um pequeno exemplo de conexo de 4 displays de 7 seguimentos que
mostram o resultado de uma operao Fatorial. O funcionamento do circuito no
importante neste exemplo, basta observarmos a necessidade de reserva de 11 pinos do
microcontrolador para mostrarmos apenas 4 caracteres apenas numricos. Para cada
dgito acrescentado, precisamos de mais um pino do microcontrolador.

Utilizao de Display 7 segmentos

Ao utilizarmos um Display LCD, reservamos apenas 7 pinos de controle. Este nmero


o mesmo para um display 162 ( 32 caracteres alfa numricos) ou um display 204 (80
caracteres alfa numricos).

Utilizao de Display LCD 162

Existem displays LCD de diversos tipos de formatos e tamanhos, todos eles so


especificados de acordo com o numero de caracteres exibidos, divididos em linhas e
colunas. Alguns possuem back-light e as cores dos caracteres podem varias, de
modelo para modelo, em azul, mbar, verde, cinza, entre outras.

Display 1601 Apenas uma linha com 16 caracteres

Display 1602 2 linhas com 16 caracteres cada

Display 1604 4 linhas com 16 caracteres cada

Display 2001 Apenas uma linha com 20 caracteres

Display 2002 2 linhas com 20 caracteres cada

Display 2004 4 linhas com 20 caracteres cada

Basicamente, cada clula (Caracter) do LCD composto de 8 pixels na horizontal e 5


pixels na vertical

Estes LCDs so conhecidos como 57, uma vez que a linha inferior reservada para o
cursor.

Existem tambm displays com 11 pixels de altura, conhecidos como 510.

Apresenta 14 pinos de acesso ( sem iluminao back-light) ou 16 pinos de acesso


( com iluminao back-light).

8 pinos de dados, 3 pinos de controle, 3 pinos de alimentao


2 pinos para iluminao back-light ( se houver)

A disposio dos pinos pode variar de modelo para modelo, veja abaixo uma outra
disposio muito comum nos LCDs. Na dvida consulte o datasheet do fabricante.

Abaixo a tabela com a funo e descrio de cada pino de controle:

A alimentao padro de 5V ( de 4,5 6V) ,consumindo alguns miliampres. Alguns


modelos trabalham com 3V e o consumo total do LCD pode ser menor que de um nico
LED!!!
Os pinos (1) Vss(GND) e (2) Vdd (Vcc) podem ser ligados juntamente com o PIC
mesma alimentao.

O pino (3) controla o contraste do LCD, para isso basta aplicar um sinal de 0V
+5Vcc. Podemos utilizar um potencimetro entre 10k e 20k para este ajuste
conforme a figura abaixo:

O pino (4) RS o seletor de Registros. Isso quer dizer que quando este pino est em
nvel lgico baixo (0) os dados enviados para o LCD so tratados como comandos e os
dados lidos do LCD indicam o seu estado atual (status). Quando este pino est em nvel
lgico alto (1), os dados so tratados como caracteres, tanto para leitura como para
escrita.
Nvel lgico (0): Comandos
Nvel lgico (1): Dados
O pino (5) R/W controla se a operao ser de leitura (1) ou gravao (0)
O pino (6) Enable, habilita os comandos do LCD em borda de descida (de 1 para 0).
utilizado para iniciar a transferncia de comandos ou caracteres entre o mdulo e as
linhas de dados. Quando estiver escrevendo para o display, os dados sero transmitidos
apenas a partir de uma transio de high para low (H -> L) deste sinal. No entanto, para
ler informaes do display, as informaes estaro disponveis imediatamente aps uma
transio L -> H e permanecer l at que o sinal volte para o nvel lgico baixo (0)
novamente.
Os pinos (7) (14) so o barramento de dados ( 8 bits). Ele trabalha com os oito sinais
em paralelo ou ainda pode trabalhar com um barramento de 4 vias (normalmente D4 a
D7), mas os dados devem ser transmitidos em dois pacotes. Cada pacote de quatro bits
conhecido como nibble. Este um excelente recurso para minimizar o uso de pinos de
I/O do microcontrolador, mas ocupa um pouco mais de memria. A deciso de utilizar 8
vias ou 4 vias exclusiva do desenvolvedor do projeto.

Ao alimentar o LCD, a primeira linha fica toda preenchida indicando que o LCD est
alimentado corretamente e que no existe nenhum pixel queimado.

Para deix-lo operacional precisamos inici-lo, enviando uma sequncia de comandos


com a configurao desejada. A tabela a seguir mostra quais so estes comandos.

Baseados na tabela anterior verificamos que existem diversas configuraes que podem
ser atribudas ao LCD. A tabela a seguir mostra as opes disponveis.

Lembre-se que, antes de qualquer operao com o LCD ele precisa ser inicializado
utilizando estas informaes da tabela acima. importante salientar que no existe uma
ordem especfica para os itens de configurao especificados acima. Eles podem ser
enviados em qualquer ordem, uma vez que o bit mais significativo de cada categoria
indica o seu grupo de configurao. No entanto para o processo de inicializao,
importante que antes de entrarmos nos grupos acima, existe uma ordem que deve ser
respeitada. Isso ser visto em um captulo especfico que ir abordar a rotina de
inicializao do display.

Como parte integrante do controlador que o LCD utiliza, h uma tabela de caracteres
pr-programados que esto prontos para uso imediato. Com isso, todo o trabalho de
definir pixel por pixel para os caracteres foi eliminado. Mas h uma desvantagem.
Como a maioria dos LCDs so produzidos na sia, ele vem com um conjunto de
caracteres especficos. A figura a seguir mostra a tabela existente na maioria dos LCDs.
Ela conhecida como ROM Code A00.

Existe outra tabela que tambm encontrada nos LCDs, mas so mais raras. Geralmente
os LCDs mais caros possuem esta opo. Novamente, sempre muito importante ler o
data sheet do seu mdulo de LCD antes de prosseguir com o desenvolvimento do
projeto. A tabela a seguir ilustra os caracteres pertencentes ao ROM Code A02:

Como voc pode reparar, os caracteres acentuados que temos em portugus somente
estaro disponveis nesta verso de ROM. Os LCDs so fornecidos com apenas um
tabela de caracteres.

Para evitar que os mdulos sejam rgidos no que diz respeito a caracteres exibidos,
todos eles possuem uma rea especfica para caracteres criados pelo usurio. Esta
rea chama-se CG RAM e voltil, ou seja, se desligarmos o LCD ou o reiniciarmos,
todas estas informaes sero perdidas. Termos um captulo especfico sobre a
CGRAM.
Os endereos 000 007 so reservados para caracteres criados pelo usurio.
So gravados em memria RAM (CGRAM) e se perdem ao desligar o LCD.
Enviamos para o LCD a condio (0 ou 1) de cada pixel do caracter.
Sero enviados 8 bytes para cada caracter.
Para cri-lo, definimos como ser nosso caracter com base em uma matriz 58:

Para criarmos um robozinho, preenchemos a matriz da seguinte forma:

Cada linha tratada como um byte em separado. Cada ponto preenchido representa 1
e cada ponto apagado representa 0

Pelo fato de um byte ter 8 bits e cada linha apenas 5, os 3 bits mais significativos
sero sempre 0;
Agora, basta escrever os bytes:

Com o caracter pronto, basta program-lo na CGRAM enviando a sequncia de


comando correta;
Primeiro, o comando SET CGRAM ADRESS + o endereo inicial;
Para o primeiro caracter disponvel : 00010000;
Em seguida, enviamos os 8 bytes que formam o caracter.
Se continuarmos inserindo informaes de bytes, o ponteiro passar para o segundo
caracter e assim por diante;
Ento, para programarmos os 8 caracteres disponveis, basta enviar o comando SET
CGRAM ADRESS+ o endereo 000 e passarmos, em seguida, os 64 bytes que
formaro os 8 novos caracteres;
Maiores detalhes sero vistos nos exerccios

Aps ligarmos o LCD e o iniciarmos, o cursor ir para a primeira posio que a 000
(primeira linha x primeira coluna). De acordo com que vamos imputando dados nele, o
cursor ir deslocar para as posies seguintes. Este auto-incremento uma facilidade
muito interessante, pois dispensa especificar cada posio para cada caractere em
separado, economizando (e muito) em linhas de cdigos necessrias.
Mas, e se quisermos escrever em um ponto especfico do LCD?
Neste caso, podemos especificar exatamente qual o endereo que o cursor dever estar
para exibir o caractere desejado. Este dado ser passado para o LCD pelas mesmas vias
de dados que passamos um caractere, s que dessa vez ser um comando. De acordo
com a Tabela, para entrarmos com um comando para setar um endereo na DDRAM do
LCD precisaremos ter o pino RS e RW em nvel lgico baixo e o bit mais significativo

precisa estar obrigatoriamente em nvel lgico alto. Com isso podemos enderear at
128 posies e, ao passar o endereo, deveremos somar este bit, ou seja, precisaremos
somar o valor 080 ao endereo desejado.
Para ilustrar, o endereamento de um display LCD 16X02 o seguinte:

Como devemos, obrigatoriamente fazer com que o bit mais significativo do endereo
seja 1, o valor que devemos passar para o LCD obedece figura abaixo:

Para facilitar as contas, segue abaixo o endereamento para a maioria dos LCDs
comerciais:

Independente do tamanho do LCD existem sempre 80 posies por linha que podem ser
usadas. Como no existem posies suficientes no mostrador do LCD, o texto
rotacionado ou deslocado, tanto para a direita como para a esquerda. Portanto este
processo deve ser feito cuidadosamente para que no haja confuses durante o
endereamento.

Timing uma limitao do LCD e diz respeito ao tempo de sincronizao;


Independente da velocidade do PIC, existe um tempo mnimo que o LCD precisa para
processar as informaes;
Se este tempo no for respeitado, o LCD no Funcionar;
Este tempos so apresentados no Datasheet do LCD:

Na prtica, podemos utilizar os seguintes tempos:

O LCD baseado nos controladores HD44780 ou KS0066U possui 8 vias de dados que
so enviado de forma paralela;
Estes LCDs foram construdos para serem compatveis com microntroladores antigos
de 4 bits;
Essa caracterstica nos permite decidir se usaremos o LCD com 4 ou 8 vias de dados;
Lembre-se: os dados continuaro a ter 8 bits, apenas optamos por transmiti-los usando
4 ou 8 vias
Para iniciarmos o LCD no modo de transferncia em 8 bits enviamos o comando:

O controlador HD44780 ou o KS0066U, encontrados na maioria dos mdulos de LCDs


alfa-numricos existentes atualmente foram desenvolvidos para serem 100%
compatveis com microprocessadores antigos de 4 bits. No entanto esta caracterstica
muito til quando precisamos interfacear um microcontrolador.
Muitas vezes, durante o desenvolvimento de um projeto, precisamos ter muito cuidado
com o nmero de pinos de I/O utilizados, pois normalmente, este o componente mais
caro do projeto. Por isso precisamos racionar o uso destes pinos. J pensou voc
desenvolvendo um equipamento com um PIC16F628A e ter que migrar para o, por
exemplo, PIC16F873A apenas por que faltaram alguns pinos para ligar um LCD?

Outro ponto diz respeito miniaturizao, pois atualmente um grande diferencial de


cada projeto est neste ponto. Cada vez mais precisamos ter equipamentos que exeram
mais funes e ocupem cada vez menos espao. aqui que reside a grande vantagem de
se comunicar em quatro vias de dados.
Mas como eu vou mandar 1 byte (8 bits) se eu s tenho 4 vias de dados? Uma vez que o
display posto no modo de 4 vias, basta mandarmos 2 pacotes de 4 bits cada que o
display se encarrega de fazer o resto. Cada pacote de 4 bits conhecido como nibble.
Neste modo, apenas as vias de dados D4 a D7 so utilizadas, e as vias D0 a D3 devem
ser deixadas flutuando (sem conexo alguma) ou conectadas ao positivo da alimentao,
via resistor limitador que tem seu valor entre 4K7 e 47K. No aconselhvel aterr-los,
a no ser que o pino R/W tambm esteja aterrado, prevenindo que os pinos sejam
configurados como sada. Se o pino for configurado erroneamente como sada e os
pinos estiverem diretamente aterrados, os pinos que estiverem com valor lgico 1
podero se queimar.
Ao energizar o LCD, ele automaticamente posto no mode de comunicao em 8 vias.
Por isso, precisamos inicializ-lo para que possa operar corretamente. Neste ponto h
uma pegadinha Como eu fao para inicializar o LCD se ele s possui as vias D4 a D7
e ele est no modo 8 vias?
Foi pensando neste problema que os projetistas dos controladores determinaram que o
bit que ir configurar o modo 4 vias seria o D4, por isso no precisamos nos preocupar.
Basta enviar um comando com o valor 0b001000000 (020) que o LCD entra no modo
de 4 vias.
A partir deste momento, todas as informaes que sero passadas para o LCD devero
ser divididas em 2 nibbles, sendo que o nibble mais significativo deve ser enviado
primeiramente, e o nibble menos significativo dever ser enviado logo em seguida.
Para inicializarmos em 4 bits, devemos adotar a seguinte seqncia de comandos:

O LCD composto por 6 camadas de materiais distintos que, agrupados, do as


caractersticas do display, veja:

1 Filme filtro vertical para polarizar a luz que entra

2 Carcaa de vidro com eletrodos . As formas desses eletrodos iro determinar as


formas escuras que aparecem quando o LCD est ligado

3 Tranado

4 Carcaa de vidro com a pelcula de eletrodo comum , com sulcos horizontais para
alinhar com o filtro horizontal.

5 Filme filtro horizontal para bloquear / permitir atravs da luz

6 Superfcie reflexiva para enviar luz de volta para espectador

Conhecendo como composto o Display LCD passemos para a parte prtica.


A seguir uma configurao muito bsica de um circuito que integra um
microcontrolador PIC 16F628A. Para a utilizao com outro modelo de
microcontrolador PIC basta escolher os pinos que queremos para cada funo e indiclos no programa.

Vamos analisar o cdigo, todo comentado, necessrio para exibir uma mensagem no
LCD, utilizando o PORTD de um PIC16F877a
/************************************* INICIO
************************************/
#include
// microcontrolador utilizado
#fuses xt,nowdt,noprotect,put,brownout,nolvp,nocpd,nowrt // configurao dos fusveis
#use delay(clock=4000000)
#byte porta = 005
#byte portb = 006
#byte portc = 007
#byte portd = 008

#byte porte = 009


#bit rs = porte.0
comando
#bit enable = porte.1

// via do LCD que sinaliza recepo de dados ou


// enable do lcd

/************* Rotina que envia um COMANDO para o LCD **************/


void comando_lcd(int caracter)
{
rs = 0;
// seleciona o envio de um comando
portd = caracter;
// carrega o portd com o caracter
enable = 1 ;
// gera pulso no enable
delay_us(1);
// espera 1 microssegundos
enable = 0;
// desce o pino de enable
delay_us(40);
// espera mnimo 40 microssegundos
return; // retorna
}
/************* Rotina que envia um DADO a ser escrito no LCD *************/
void escreve_lcd(int caracter)
{
rs = 1;
// seleciona o envio de um dado
portd = caracter;
// carrega o portd com o caracter
enable = 1;
// gera pulso no enable
delay_us(1);
// espera 1 microssegundos
enable = 0;
// desce o pino de enable
delay_us(40);
// espera mnimo 40 microssegundos
return; // retorna
}
/******************** Funo para limpar o LCD ********************/
void limpa_lcd()
{
comando_lcd(001);
delay_ms (2);
return;
}

// limpa LCD

/******************* Inicializao do Display de LCD ********************/


void inicializa_lcd()
{
comando_lcd(030);
delay_ms(4);
comando_lcd(038);

// envia comando para inicializar display


// espera 4 milissegundos
// configura LCD, 8 bits, matriz de 75, 2 linhas

limpa_lcd();
comando_lcd(0x0c);
comando_lcd(006);
return;
}

// limpa LCD
// display sem cursor
// desloca cursor para a direita
// retorna

/******************** Configuraes do PIC ********************/


void main()
{
// configura microcontrolador
setup_adc_ports (no_analogs);
// configura os tris
set_tris_a(0b11011111);
set_tris_b(0b00000011);
set_tris_c(0b11111101);
set_tris_d(0b00000000);
set_tris_e(0b00000100);

// configurao da direo dos pinos de I/O

// inicializa os ports
porta=000; // limpa porta
portb=000; // limpa portb
portc=000; // limpa portc
portd=000; // limpa portd
porte=000; // limpa porte
inicializa_lcd(); // inicializa o LCD
/************************* Rotina principal **************************/
while(TRUE) // rotina principal
{
//Local onde o programa ser escrito
}
}

Para simplificar o projeto, podemos manter o pino R/W do LCD aterrado, habilitando
assim, apenas o modo de gravao que nos importa;
No poderemos ler o STATUS BIT para saber quando p LCD est pronto para o
prximo dado;

Basta apenas esperar o tempo mnimo para cada funo e executar a prxima;
Isso no uma deficincia do projeto e sim uma caracterstica da maioria dos projetos
comerciais;
Antes de comearmos a programao, precisamos informar quais os pinos de I/O
sero utilizados pelo LCD;
Isso facilitar a portabilidade do projeto para outros PICs ou LCDs
#define lcd_enable pin_e1
#define lcd_rs pin_e0
#define lcd_db4 pin_d4
#define lcd_db5 pin_d5
#define lcd_db6 pin_d6
#define lcd_db7 pin_d7

// pino enable do LCD


// pino rs do LCD
// pino de dados d4 do LCD
// pino de dados d5 do LCD
// pino de dados d6 do LCD
// pino de dados d7 do LCD

/*****************************************************************/
/* Envio de Nibble para o LCD */
/*****************************************************************/
//Esta rotina l o Nibble inferior de uma varivel e envia para o LCD.
void envia_nibble_lcd(int dado)
{
//Carrega as vias de dados (pinos) do LCD de acordo com o nibble lido
output_bit(lcd_db4, bit_test(dado,0)); //Carrega DB4 do LCD com o bit DADO
output_bit(lcd_db5, bit_test(dado,1)); //Carrega DB5 do LCD com o bit DADO
output_bit(lcd_db6, bit_test(dado,2)); //Carrega DB6 do LCD com o bit DADO
output_bit(lcd_db7, bit_test(dado,3)); //Carrega DB7 do LCD com o bit DADO
//Gera um pulso de enable
output_high(lcd_enable);
delay_us(1);
output_low(lcd_enable);
return;
}

// ENABLE = 1
// Recomendado para estabilizar o LCD
// ENABLE = 0
// Retorna ao ponto de chamada

/*****************************************************************/
/* Envio de Byte para o LCD */
/*****************************************************************/
//Esta rotina ir enviar um dado ou um comando para o LCD conforme abaixo:
// ENDEREO = 0 -> a varivel DADO ser uma instruo
// ENDEREO = 1 -> a varivel DADO ser um caractere
void envia_byte_lcd(boolean endereco, int dado)
{

output_bit(lcd_rs,endereco);
delay_us(100);
LCD
output_low(lcd_enable);
envia_nibble_lcd(dado>>4);
envia_nibble_lcd(dado & 0x0f);
do
delay_us(40);
return;
}

// Seta o bit RS para instruo ou caractere


// Aguarda 100 us para estabilizar o pino do
// Desativa a linha ENABLE
// Envia a parte ALTA do dado/comando
// Limpa a parte ALTA e envia a parte BAIXA
// dado/comando
// Aguarda 40us para estabilizar o LCD
// Retorna ao ponto de chamada da funo

/*****************************************************************/
/* Funo para limpar o LCD */
/*****************************************************************/
void limpa_lcd()
{
envia_byte_lcd(0,001);
delay_ms(2);
return;
}

// Envia instruo para limpar o LCD


// Aguarda 2ms para estabilizar o LCD
// Retorna ao ponto de chamada da funo

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
* Inicializa o LCD * /
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void inicializa_lcd()
{
output_low(lcd_db4);
output_low(lcd_db5);
output_low(lcd_db6);
output_low(lcd_db7);
output_low(lcd_rs);
output_low(lcd_enable);
delay_ms(15);
envia_nibble_lcd(003);
delay_ms(5);
envia_nibble_lcd(002);
o

// Garante que o pino DB4 esto em 0 (low)


// Garante que o pino DB5 esto em 0 (low)
// Garante que o pino DB6 esto em 0 (low)
// Garante que o pino DB7 esto em 0 (low)
// Garante que o pino RS esto em 0 (low)
// Garante que o pino ENABLE esto em 0 (low)
// Aguarda 15ms para estabilizar o LCD
// Envia comando para inicializar o display
// Aguarda 5ms para estabilizar o LCD
// CURSOR HOME Envia comando para zerar
//contador de caracteres e retornar posio

inicial
delay_ms(1);
envia_byte_lcd(0,028);
bits,
envia_byte_lcd(0,0x0c);
cursor

// Aguarda 1ms para estabilizar o LCD


// FUNCTION SET Configura o LCD para 4
// 2 linhas, fonte 5X7.
// DISPLAY CONTROL Display ligado, sem

limpa_lcd();
envia_byte_lcd(0,006);
direita
return;
}

// Limpa o LCD
// ENTRY MODE SET Desloca o cursor para a
// Retorna ao ponto de chamada da funo

Vamos fazer um exerccio prtico. O Hardware utilizado o que segue:

Abaixo, o programa correspondente:


#include
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* CONSTANTES INTERNAS *
***********************************************
* * * * * * * * * * * */

// Estas so as definies dos pinos que o LCD utiliza.


// Definem quais pinos do PIC controlaro os pinos do LCD
#define lcd_enable pin_d1
#define lcd_rs pin_d0
#define lcd_db4 pin_d4
#define lcd_db5 pin_d5
#define lcd_db6 pin_d6
#define lcd_db7 pin_d7

// pino enable do LCD


// pino rs (register select)do LCD
// (0) para comandos (1) para dados
// pino de dados d4 do LCD
// pino de dados d5 do LCD
// pino de dados d6 do LCD
// pino de dados d7 do LCD

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DEFINIO E INICIALIZAO DOS PORTS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#byte porta = 005
#byte portb = 006
#byte portc = 007
#byte portd = 008
#byte porte = 009
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* ENVIO DE NIBBLE PARA O LCD *
***********************************************
* * * * * * * * * * * */
//Esta rotina l o Nibble inferior de uma varivel e envia para o LCD.
//1byte = 8 bits = 2 Nibbles
//Sero enviados para os pinos db4, db5, db6, db7 do LCD
void envia_nibble_lcd(int dado)
{
//Carrega as vias de dados (pinos) do LCD de acordo com o nibble lido
output_bit(lcd_db4, bit_test(dado,0));
//Carrega DB4 do LCD com o bit
DADO
output_bit(lcd_db5, bit_test(dado,1));
//Carrega DB5 do LCD com o bit
DADO
output_bit(lcd_db6, bit_test(dado,2));
//Carrega DB6 do LCD com o bit
DADO
output_bit(lcd_db7, bit_test(dado,3));
//Carrega DB7 do LCD com o bit
DADO
//Gera um pulso de enable
output_high(lcd_enable);
// ENABLE = 1delay_us(1);
// Recomendado para estabilizar o
LCD
output_low(lcd_enable);
// ENABLE = 0

return;
}

// Retorna ao ponto de chamada

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********
* ENVIO DE BYTE PARA O LCD *
***********************************************
* * * * * * * * * * * */
// Esta rotina ir enviar um dado ou um comando para o LCD conforme abaixo:
// ENDEREO = 0 -> a varivel DADO ser uma instruo
// ENDEREO = 1 -> a varivel DADO ser um caractere
void envia_byte_lcd(boolean endereco, int dado)
{
output_bit(lcd_rs,endereco);
// Seta o bit RS para instruo ou caractere
delay_us(100);
// Aguarda 100 us para estabilizar o pino
do LCD
output_low(lcd_enable);
// Desativa a linha ENABLE
envia_nibble_lcd(dado>>4);
// Envia a parte ALTA do dado/comando
envia_nibble_lcd(dado & 0x0f);
// Limpa a parte ALTA e envia a parte
BAIXA do
//dado/comando
delay_us(40);
// Aguarda 40us para estabilizar o LCD
return;
// Retorna ao ponto de chamada da funo
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
** * * * * * * ** * *
* ENVIO DE CARACTER PARA O LCD *
***********************************************
***********/
// Esta rotina serve apenas como uma forma mais fcil de escrever um caractere no
display.
void escreve_lcd(char c)
{
envia_byte_lcd(1,c);
}

// envia caractere para o display

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **
* * * * * * ** * *
* FUNO PARA LIMPAR O LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **
* * * * * * ** * * /
// Como esta operao pode ser muito utilizada, transformando-a em funo

// faz com que o cdigo compilado seja menor.


void limpa_lcd()
{
envia_byte_lcd(0,001);
delay_ms(2);
return;
}

// Envia instruo para limpar o LCD


// 0 envio de instruo
// 0X01 mostrar tabela
// Aguarda 2ms para estabilizar o LCD
// Retorna ao ponto de chamada da funo

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* INICIALIZA O LCD *
***********************************************
***********/
void inicializa_lcd()
{
output_low(lcd_db4);
output_low(lcd_db5);
output_low(lcd_db6);
output_low(lcd_db7);
output_low(lcd_rs);
output_low(lcd_enable);
(low)
delay_ms(15);
envia_nibble_lcd(002);
zerar o

// Garante que o pino DB4 esto em 0 (low)


// Garante que o pino DB5 esto em 0 (low)
// Garante que o pino DB6 esto em 0 (low)
// Garante que o pino DB7 esto em 0 (low)
// Garante que o pino RS esto em 0 (low)
// Garante que o pino ENABLE esto em 0
// Aguarda 15ms para estabilizar o LCD
// CURSOR HOME Envia comando para
//contador de caracteres e retornar posio

inicial (080)
delay_ms(1);
envia_byte_lcd(0,020);
tabela
envia_byte_lcd(0,0x0C);
sem cursor

// Aguarda 1ms para estabilizar o LCD


// FUNCTION SET Configura o LCD para
//4 bits, 1 linha, fonte 5X7.
// 0 envio de instruo 020 mostrar
// DISPLAY CONTROL Display ligado,
// 0 envio de instruo 0x0C mostrar

tabela
limpa_lcd();
envia_byte_lcd(0,006);
posio

// Limpa o LCD
// ENTRY MODE SET A cada caracter, incrementa uma
// 0 envio de instruo 006 mostrar

tabela
return;
}

// Retorna ao ponto de chamada da funo

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* CONFIGURAES DO PIC *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * *
************/
main()
{
// Reseta portas
porta = 0;
portb = 0;
portc = 0;
portd = 0;
porte = 0;
// configura os tris
set_tris_a(0xFF);
set_tris_b(0xFF);
set_tris_c(0xFF);
set_tris_d(0xFF);
set_tris_e(0xFF);

// configurao da direo dos pinos de I/O

// Inicializa o LCD
inicializa_lcd();
//Escreve tela
escreve_lcd(O);
escreve_lcd(L);
escreve_lcd(A);
escreve_lcd();
escreve_lcd(M);
escreve_lcd(U);
escreve_lcd(N);
escreve_lcd(D);
escreve_lcd(O);
escreve_lcd(!);
//printf(escreve_lcd,");
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* ROTINA PRINCIPAL *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * *
************/
// Como no h outra execuo, a rotina principal fica vazia
while (true)

{
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
************
* FIM DO PROGRAMA
***********************************************
* * * * * ***/
}

Normalmente a utilizao do Display LCD apenas uma das funes de uma aplicao prtica, por este motivo, podemos criar uma
biblioteca contendo todas as funes de controle do LCD em um arquivo separado do programa principal. E esta biblioteca ser
chamada por cada um dos muitos projetos futuros que sero realizados e que utilizem Displays LCD sem a necessidade de reescrev-lo
a cada vez.
Para isso criamos um arquivo no bloco de notas ou em qualquer editor de texto e salvamos com o nome de LCD.h. Este arquivo dever
ser salvo na mesma pasta onde se encontra o programa principal do seu projeto, no qual dever ser includa uma linha de comando que
inclua todas as funes desta biblioteca e as tornem disponveis no programa principal.
Exemplo de declarao da biblioteca LCD.h no programa principal:

#include
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONSTANTES INTERNAS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Estas so as definies dos pinos que o LCD utiliza.
// Definem quais pinos do PIC controlaro os pinos do LCD
#define lcd_enable pin_d1

// pino enable do LCD

#define lcd_rs pin_d0

// pino rs (register select)do LCD

#define lcd_db4 pin_d4

// pino de dados d4 do LCD

#define lcd_db5 pin_d5

// pino de dados d5 do LCD

#define lcd_db6 pin_d6

// pino de dados d6 do LCD

#define lcd_db7 pin_d7

// pino de dados d7 do LCD

// (0) para comandos (1) para dados

#INCLUDE

// Declarao da biblioteca do LCD

//Continuao do programa.

Abaixo mostrado todo o contedo do arquivo lcd.h, incluindo novas funes que foram includas a pedido dos leitores do Blog e que
sero explicadas no prprio cdigo.
Conteudo do arquivo lcd.h:

/***************************************************************************/
/* Rotinas para o LCD */
/***************************************************************************/
//Este o bloco com as rotinas necessrias para manipular o LCD
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de Nibble para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//Esta rotina l o Nibble inferior de uma varivel e envia para o LCD.
void envia_nibble_lcd(int dado)
{
//Carrega as vias de dados (pinos) do LCD de acordo com o nibble lido
output_bit(lcd_db4, bit_test(dado,0));

//Carrega DB4 do LCD com o bit DADO

output_bit(lcd_db5, bit_test(dado,1));

//Carrega DB5 do LCD com o bit DADO

output_bit(lcd_db6, bit_test(dado,2));

//Carrega DB6 do LCD com o bit DADO

output_bit(lcd_db7, bit_test(dado,3));

//Carrega DB7 do LCD com o bit DADO

//Gera um pulso de enable


output_high(lcd_enable);
delay_us(1);
output_low(lcd_enable);
return;

// ENABLE = 1
// Recomendado para estabilizar o LCD
// ENABLE = 0
// Retorna ao ponto de chamada da funo

}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de Byte para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//Esta rotina ir enviar um dado ou um comando para o LCD conforme abaixo:
// ENDEREO = 0 -> a varivel DADO ser uma instruo
// ENDEREO = 1 -> a varivel DADO ser um caractere
void envia_byte_lcd(boolean endereco, int dado)
{
output_bit(lcd_rs,endereco);

// Seta o bit RS para instruo ou caractere

delay_us(100);

// Aguarda 100 us para estabilizar o pino do LCD

output_low(lcd_enable);

// Desativa a linha ENABLE

envia_nibble_lcd(dado>>4);

// Envia a parte ALTA do dado/comando

envia_nibble_lcd(dado & 0x0f);

// Limpa a parte ALTA e envia a parte BAIXA do


// dado/comando

delay_us(40);
return;
}

// Aguarda 40us para estabilizar o LCD


// Retorna ao ponto de chamada da funo

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de caractere para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Esta rotina serve apenas como uma forma mais fcil de escrever um caractere
// no display. Ela pode ser eliminada e ao invs dela usaremos diretamente a
// funo envia_byte_lcd(1,"); ou
// envia_byte_lcd(1,<cdigo do caractere a ser mostrado no LCD>);
void escreve_lcd(char c)
// envia caractere para o display
{
envia_byte_lcd(1,c);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Funo para limpar o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Como esta operao pode ser muito utilizada, transformando-a em funo
// faz com que o cdigo compilado seja menor.
void limpa_lcd()
{
envia_byte_lcd(0,001);

// Envia instruo para limpar o LCD

delay_ms(2);

// Aguarda 2ms para estabilizar o LCD

return;

// Retorna ao ponto de chamada da funo

}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Inicializa o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void inicializa_lcd()
{
output_low(lcd_db4);

// Garante que o pino DB4 esto em 0 (low)

output_low(lcd_db5);

// Garante que o pino DB5 esto em 0 (low)

output_low(lcd_db6);

// Garante que o pino DB6 esto em 0 (low)

output_low(lcd_db7);

// Garante que o pino DB7 esto em 0 (low)output_low(lcd_rs);

que o pino RS esto em 0 (low)output_low(lcd_enable);

// Aguarda 15ms para estabilizar o LCDenvia_nibble_lcd(003);


inicializar o display
delay_ms(5);
envia_nibble_lcd(003);
delay_ms(5);
envia_nibble_lcd(003);
delay_ms(5);
envia_nibble_lcd(002);

// Aguarda 5ms para estabilizar o LCD


// Envia comando para inicializar o display
// Aguarda 5ms para estabilizar o LCD
// Envia comando para inicializar o display
// Aguarda 5ms para estabilizar o LCD
// CURSOR HOME Envia comando para zerar o

//contador de caracteres e retornar posio


// inicial (080).delay_ms(1);
envia_byte_lcd(0,028);

// Aguarda 1ms para estabilizar o LCD


// FUNCTION SET Configura o LCD para 4 bits,

// 2 linhas, fonte 5X7.


envia_byte_lcd(0,0x0c);
limpa_lcd();
envia_byte_lcd(0,006);
return;

// Garante

// Garante que o pino ENABLE esto em 0 (low)delay_ms(15);

// DISPLAY CONTROL Display ligado, sem cursor


// Limpa o LCD
// ENTRY MODE SET Desloca o cursor para a direita
// Retorna ao ponto de chamada da funo

}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Define inicio da escrita *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//Esta funo foi adicionada e serve para se definir em que posio do LCD deseja-se

// Envia comando para

//iniciar a escrita. Para isto basta chamar a Funo caracter_Inicio() indicando a


//linha e a coluna onde o cursor ser posicionado antes de se mandar escrever
void caracter_inicio(int linha, int coluna)//define a posico de inicio da frase
{
int16 posicao=0;
if(linha == 1)
{
posicao=080;

//Se setado linha 1, end incial 080

}
if(linha == 2)
{
posicao=0xc0;

//Se setado linha 2, end incial 0xc0

}
posicao=posicao+coluna;

//soma ao end inicial, o numero da coluna

posicao;

//subtrai 1 para corrigir posio

envia_byte_lcd(0,posicao);
return;
}
/***************************************************************************/
/* Final das rotinas para o LCD */
/***************************************************************************/

Agora veremos um exemplo de aplicao prtica da utilizao desta biblioteca:


#include
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONSTANTES INTERNAS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Estas so as definies dos pinos que o LCD utiliza.
// Definem quais pinos do PIC controlaro os pinos do LCD
#define lcd_enable pin_d1

// pino enable do LCD

#define lcd_rs pin_d0

// pino rs (register select)do LCD

#define lcd_db4 pin_d4

// pino de dados d4 do LCD

#define lcd_db5 pin_d5

// pino de dados d5 do LCD

#define lcd_db6 pin_d6

// pino de dados d6 do LCD

#define lcd_db7 pin_d7

// pino de dados d7 do LCD

// (0) para comandos (1) para dados

#INCLUDE

// Declarao da biblioteca do LCD

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DEFINIO E INICIALIZAO DOS PORTS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#byte porta = 005
#byte portb = 006

#byte portc = 007


#byte portd = 008
#byte porte = 009
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONFIGURAES DO PIC *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * /
main()
{
// Reseta portas
porta = 0;
portb = 0;
portc = 0;
portd = 0;
porte = 0;
// configura os tris
set_tris_a(0xFF);

// configurao da direo dos pinos de I/O

set_tris_b(0xFF);
set_tris_c(0xFF);
set_tris_d(0xFF);
set_tris_e(0xFF);
// Inicializa o LCD
inicializa_lcd();
//Limpa a tela do LCD
limpa_lcd();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ROTINA PRINCIPAL *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * /
while (true)
{
caracter_inicio(1,6);

//define o ponto de inicio da frase


//Linha 1 e coluna 6

printf(escreve_lcd,O BLOG);

caracter_inicio(2,4);

//escreve no LCD

//define o ponto de inicio da frase


//Linha 2 e coluna 4

printf(escreve_lcd,DO MARCELO);

//escreve no LCD

}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* FIM DO PROGRAMA
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ***/
}

Controlando um Display LCD (Liquid Crystal Display)

O objetivo desta aula criar uma interface Serial para controlar um display
LCD 16x2, compatvel com HD44780. Os LCDs compatveis com
processadores HD44780 trabalham com um bus de dados paralelo (interfaces
entre dispositivos externos de 4 ou 8 bits). Mas atualmente voc pode comprar
displays LCD que utilizam comunicao Serial. Isso possvel, pois algumas
empresas adaptam uma plaquinha contendo um microcontrolador PIC ou
quaisquer outros, para torn-los seriais. A maioria desses displays seriais usam
um microcontrolador PIC 16F627 ou 16F628 de 18 pinos, mais que suficiente
para interfacear um display LCD configurado para trabalhar com um Bus de
dado de 4 bits. Como estamos usando a verso free do compilador da CCS,
iremos criar nosso driver para tornar um LCD serial, um PIC 16F877 (a maioria
dos pinos no sero usados), portanto, o ideal seria usar um microcontrolador
pequeno, para tornar o circuito compacto e menos dispendioso.
As vantagens de se criar um dirver e tornar um display bus serial, so:
Usa somente um fio para fazer a comunicao com o LCD;
Facilidade em escrever dados no display LCD;
Facilidade em desenvolver circuitos microcontrolados usando LCD;
Produtividade no desenvolvimento de projetos baseados em LCD.
Figura 1 - Display LCD 16x2 com backlight

Os mdulos LCDs so compostos de memria ROM, memria RAM e dois


ou mais micro processadores. A maioria deles dispe de um espao de
memria chamada CGRAM, que permite ao usurio desenhar at 8 caracteres
personalizados (para cada caracter so necessrios 8 bytes).
H display LCD de vrios tamahos e caractersticas venda no mercado.
Nesta aula iremos usar um Mdulo LCD de 2x16 (2 linhas por 16 caracteres)
com backlight (luz de fundo), e compatvel com o processador HD44780.
Tabela 1 - Pinagem do mdulo LCD 2x16 compatvel (HD44780)

Pino
Descrio
1 VSS -Terra/GND (Alimentao 0v).
2 VDD - (Alimentao +5v ).
3 VO - Tenso para ajuste do contraste.
RS - (Register Select) Seleciona

4 Dado/Comando.

R/W - (Read/Write) Seleciona

5 leitura/escrita.

6
7
8
9
10
11
12
13
14
15
16

E - (Enable) Habilita/desabilita sinal.


D0
D1
D2
D3
D4

Barramento de dados.

D5
D6
D7
LED+ Alimentao (Anodo Backlight).
LED- Alimentao (Catodo Backlight).

A tabela acima descreve as caractersticas de cada pino do mdulo LCD.


O pino 1 (VSS) ligado ao negativo da fonte de alimentao (0v), e o
pino 2 (VDD) ao positivo (+5v). O pino 3(VO) usado para ajustar o contraste
dos caracteres; ligado a um Trim-pot de 10k ohm. O pino 4 (RS) utilizado
para avisar ao mdulo LCD se o que ser enviado posteriormente
umaInstruo (comando de controle), ou um Dado (caracter a ser impresso no
display). J o pino 5 (R/W) usado para Escrever ou L um dado no LCD.
Como nesse projeto s iremos escrever no LCD, esse pino poder ficar
aterrado (0v). O pino 6 (E) usado para habilitar ou desabilitar o LCD.
Deveremos ativ-lo (E = 1) apenas quando o display for acessado. Os
pinos 7..14(D0...D7) so usados para interfacear o LCD com
microcontroladores (no nosso caso, o PIC 16F877).
Os pinos 15 e 16 (LED+ e LED-) s estaro disponveis se o mdulo
adquirido tiver backlight (luz de fundo). Para controlar a luminosidade
acrescenta-se um Trim-pot de 100 ohm entre esses pinos. O objetivo
do backlight facilitar a leitura das informaes no escuro pelo usurio.
Tabela 2 - Informaes sobre configurao e instrues para controle do mdulo LCD.
R R/ D D D D
D D
INSTRUO
D3
D0
DESCRIO
Tempo
S W 7 6 5 4
2 1
Limpa o display e toda a
memria. Retorna o curso
Limpa Display 0 0 0 0 0 0 0 0 0 1
1,6 ms
para a primeira linha e
primeira coluna.
Retorna o curso para seu
Cursor no
lugar de origem e o display
0 0 0 0 0 0 0 0 1 x
1,6 ms
incio da tela.
volta ao estado normal de
funcionamento.
Fixa modo de 0 0 0 0 0 0 0 1 ID S Define a direo de
1,6 ms
funcionament
H movimento do cursor ou da
o
tela. ID
incrementa/decrementa o
endereo de memria.
Quando: ID=0 cursor movese p/ esquerda.
Quando: ID=1 cursor movese p/ direita.

Quando: SH=1 o texto movese com a entrada de um


novo caracter. Se SH=0, o
texto no movido.

Controle do
display

0 0 0 0

Move o cursor
0
ou texto

0 0 0 1

INSTRUO

R R/ D D D D
S W 7 6 5 4

Configurao
do LCD

0 0 1

D
L

Posiciona na
memria

1 A A A

Leitura do
Flag Busy

F C C C

Escreve na
memria

D D D D

L dado na
memria

D D D D

D=1 liga o display, D=0


desliga.
1 D C B C=1 liga o cursor, C=0
40us
desliga.
B=1 cursor piscante, se C=1.
SC=0 move o cursor.
S R
SC=1 move o texto.
x x
40us
C L
RL=1 move para a direita.
RL=0 move para a esquerda.
D D
D3
D0
DESCRIO
Tempo
2 1
Comunicao:
DL=1, se comunica com 8
bits.
DL=0, se comunica com 4
bits.
Quantidade de linha:
N F x x
40us
N=0, somente 1 linha.
N=1, de 2 ou mais linhas.
Tamanho da fonte dos
caracter:
F=0, matriz 5x7.
F=1, matriz 5x10.
A - Define o endereo de
A A A A memria para gravao ou
40us
leitura.
Leitura do contador de
endereos (C) e do flag (F).
O flag F , indica que uma
operao est sendo
C C C C
40us
executada.
Se F=0, controlador ocioso e
pronto para receber
comandos.
Grava o byte (DDDDDDDD)
no endereo de memria
D D D D
40us
apontado pelo contador de
endereos.
L o byte (DDDDDDDD) na
D D D D memria, apontado pelo
40us
contador de endereos.

Figura 2 - Procedimentos para inicializar o Mdulo LCD compatvel (HD44780 ).

Observe no fluxograma acima que aps ligar o mdulo LCD, espera-se por
mais de 30ms antes de se enviar os comandos de controle. Isso necessrio
para que a tenso no mdulo LCD estabilize.
Veja abaixo a seqncia detalhada para inicializar o mdulo LCD:
a) Ligar o LCD;
b) Aguardar 30ms;
c) Habilitar o envio de comandos (RS=0);
d) Colocar o byte comando/dados na via de dados (DB7...DB0);
e) Fazer com que o pino 6 (E) do LCD v a nvel 0 (zero);
f) Aguardar uns 20ms;
g) Fazer com que o pino 6 (E) do LCD v a nvel 1 (um);
h) Aguardar uns 20ms.
Para escrever um byte de COMANDO no display LCD, proceda conforme a
seqncia abaixo:
a) RS=0, R/W=0, E=0
b) Einvia-se o byte de comando na via
(DB7...DB0)
c) RS=0, R/W=0, E=1
d) RS=0, R/W=0, E=0

c) Habilitar o envio de comandos (RS=0);


d) Colocar o byte de comando na via de dados (DB7...DB0);
e) Fazer com que o pino 6 (E) do LCD v a nvel 0 (zero);
f) Aguardar uns 40us;

g) Fazer com que o pino 6 (E) do LCD v a nvel 1 (um);


h) Aguardar uns 40us;
Para enviar qualquer comando para o LCD repetir os passos: d), e), f), g) e h).

Para escrever um byte de DADO no display LCD, proceda conforme a


seqncia abaixo:
a) RS=1, R/W=0, E=0
b) Einvia-se o byte de dados na via
(DB7...DB0)
c) RS=1, R/W=0, E=1
d) RS=1, R/W=0, E=0

c) Habilitar o envio de comandos (RS=1);


d) Colocar o byte de dados na via de dados (DB7...DB0);
e) Fazer com que o pino 6 (E) do LCD v a nvel 0 (zero);
f) Aguardar uns 40us;
g) Fazer com que o pino 6 (E) do LCD v a nvel 1 (um);
h) Aguardar uns 40us.
Para enviar qualquer dado para o LCD repetir os passos: d), e), f), g) e h).
Fonte 1 - Driver para controle de LCD usando o PIC16F877
//-------------------------------------------------------------------------------------------------//Curso USB/Serial
//Programa: DriverLCD.c
//Driver para controle de LCD no modo 4 bits.
//Configurado para o PIC16F877 - 4Mhz.
//Com pequenas mudanas possvel usar PIC16F627 ou 16F628.
//LCD Padro: Microprocessador HD44780.
//www.rogercom.com
//rogercom@rogercom.com
//Antonio Rogrio Messias
//Data: 20/11/2005
//--------------------------------------------------------------------------//Pinos do PIC16F877 para interfacear o LCD
//--------------------------------------------------------------------------#define LCD_PIN_RS
pin_d7 //Seleciona Registrador.
#define LCD_PIN_ENABLE pin_d6 //Habilita LCD.
#define LCD_PIN_RW
pin_d5 //Nvel 0 = Escrita no LCD.
//--------------------------------------------------------------//Bus de 4 bits
#define LCD_DADO_7
pin_b7 // --> pino 14 LCD.
#define LCD_DADO_6
pin_b6 // --> pino 13 LCD.
#define LCD_DADO_5
pin_b5 // --> pino 12 LCD.
#define LCD_DADO_4
pin_b4 // --> pino 11 LCD.
//---------------------------------------------------------------------------

#define LCD_MOVE_CURSOR_ESQ 0x10 //Move cursor para esquerda.


#define LCD_MOVE_CURSOR_DIR
0x14 //Move cursor para direita.
#define LCD_MOVE_DISPLAY_ESQ 0x18 //Move o texto para a esquerda.
#define LCD_MOVE_DISPLAY_DIR
0x1C //Move o texto para a direita.
#define LCD_MODO_8_BITS
0x38 //8 bit de dados, 2 linhas (fonte
5x7).
#define LCD_MODO_4_BITS
0x28 //4 bit de dados, 2 linhas (fonte
5x7).
#define LCD_CONTROLE_DISPLAY 0x0E //00001110
#define LCD_FIXA_MODO
0x06 //Fixa modo de funcionamento
do display.
#define LCD_DISPLAY_ON
0x0C //Liga display.
#define LCD_DISPLAY_OFF
0x08 //Desliga Display sem apagar os
dados na memria.
#define LCD_SET_DD_RAM
0x80 //Linha 1 posio 1.
#define LCD_LINHA_1
0x80 //DDRAM nicio da primeira
linha.
#define LCD_LINHA_2
0xC0 //DDRAM nicio da segunda
linha.
#define LCD_LIMPA_DISPLAY
0x01 //Limpa o display e apaga a
memria.
#define LCD_CURSOR_HOME
0x02 //Pe curso no incio da tela.
#define LCD_CURSOR_OFF
0x0C //desliga cursor.
#define LCD_CURSOR_PISCANTE
0x0D //cursor barra piscante.
#define LCD_CURSOR_LINHA
0x0E //linha fixa.
#define LCD_CURSOR_LINHA_BARRA 0x0F //linha fixa e barra piscante.
//--------------------------------------------------------------------------//Funes para controle do LCD
//--------------------------------------------------------------------------void LCD_Modo_Comando(void);
void LCD_Modo_Dado(void);
void LCD_Escreve_Modo_4_bits(char ByteValor);
void LCD_EnviaChar(char carac);
void LCD_EnviaString(char *Str);
void LCD_Gotoxy(int colunaX, int LinhaY);
void LCD_StringCentro(char *Texto, int LinhaY);
void LCD_LimpaDisplay(void);
void LCD_CursorHome(void);
void LCD_TrocaCursor(int Tipo);
void LCD_PiscaString(int ColunaX, int LinhaY, char *String, int Vezes);
void LCD_Cria_Car_CGRAM(int Endereco,unsigned char codigo[]);
void LCD_Configura(void);
//--------------------------------------------------------------------------//Faz o LCD aceitar Comandos.
//--------------------------------------------------------------------------void LCD_Modo_Comando(void)
{
output_low( LCD_PIN_RS ); //RS em 0 - COMANDO.
delay_us(50);
}
//--------------------------------------------------------------------------//Faz o LCD aceitar Dados.
//--------------------------------------------------------------------------void LCD_Modo_Dado(void)

{
output_high( LCD_PIN_RS ); //RS em 1 - DADO.
delay_us(50);
}
//--------------------------------------------------------------------------//Escreve 8 bits de dados no modo 4 bits no LCD.
//--------------------------------------------------------------------------void LCD_Escreve_Modo_4_bits(int ByteValor)
{
//===> 20Mhz usar delay de 20us.
//===> 4Mhz usar delay de 2us.
//Envia o nibble mais significante (MSB) para o LCD.
output_bit(LCD_DADO_7, bit_test(ByteValor, 7) );
output_bit(LCD_DADO_6, bit_test(ByteValor, 6) );
output_bit(LCD_DADO_5, bit_test(ByteValor, 5) );
output_bit(LCD_DADO_4, bit_test(ByteValor, 4) );
delay_us(2);
output_high( LCD_PIN_ENABLE );
delay_us(2);
output_low( LCD_PIN_ENABLE ); //Clock.
delay_us(2);
//Envia o nibble menos significante (LSB) para o LCD.
output_bit(LCD_DADO_7, bit_test(ByteValor, 3) );
output_bit(LCD_DADO_6, bit_test(ByteValor, 2) );
output_bit(LCD_DADO_5, bit_test(ByteValor, 1) );
output_bit(LCD_DADO_4, bit_test(ByteValor, 0) );
delay_us(2);
output_high( LCD_PIN_ENABLE );
delay_us(2);
output_low( LCD_PIN_ENABLE ); //Clock.
}
//--------------------------------------------------------------------------//Envia um caracter para o LCD.
//--------------------------------------------------------------------------void LCD_EnviaChar(char carac)
{
LCD_Escreve_Modo_4_bits(carac);
}
//--------------------------------------------------------------------------//Envia uma string de caracteres para o LCD.
//--------------------------------------------------------------------------void LCD_EnviaString(char *Str) //Envia um texto para o LCD.
{
LCD_Modo_Dado();
while(*Str) //Loop enquanto no for 'encontrado '\0' (final da string).
LCD_EnviaChar(*Str++); //Envia caracter a caracter para o display LCD.
}
//--------------------------------------------------------------------------//Posisiona o cursor na na coluna X linha do LCD.
//--------------------------------------------------------------------------void LCD_Gotoxy(int colunaX, int LinhaY) //Posiciona na coluna e linha do
display de LCD.
{

LCD_Modo_Comando();
if(LinhaY < 2) //se for a linha 1.
LCD_EnviaChar(LCD_LINHA_1+colunaX); //Endereo de memria da 1a.
linha do LCD (0x80).
else //se no 2a linha.
LCD_EnviaChar(LCD_LINHA_2+colunaX); //Endereo de memria da 2a.
linha do LCD (0xC0).
LCD_Modo_Dado(); //Volta ao modo Dado.
}
//--------------------------------------------------------------------------//Envia uma string no centro da linha do LCD.
//--------------------------------------------------------------------------void LCD_StringCentro(char *Texto, int LinhaY)
{
int Metade;
Metade = strlen(Texto) / 2; //Acha a metade do texto.
LCD_Gotoxy(8-Metade, LinhaY); //Centraliza. O 8 o centro da tela do
display (16 Div. 2).
LCD_EnviaString(Texto);
}
//--------------------------------------------------------------------------//Limpa o LCD.
//--------------------------------------------------------------------------void LCD_LimpaDisplay(void) //Limpa o display e a memria do LCD
posicionando na primeira linha.
{
LCD_Modo_Comando(); //Pe no modo Comando.
LCD_EnviaChar(LCD_LIMPA_DISPLAY); //Limpa o display.
LCD_Modo_Dado(); //Volta ao modo Dado.
}
//------------------------------------------------------------------------------//Pe o curso na posio superior esquerda do display.
//------------------------------------------------------------------------------void LCD_CursorHome(void) //Limpa o display e a memria do LCD
posicionando na primeira linha.
{
LCD_Modo_Comando(); //Pe no modo Comando.
LCD_EnviaChar(LCD_CURSOR_HOME); //Pe o curso na posio superior
esquerda do display.
LCD_Modo_Dado(); //Volta ao modo Dado.
}
//--------------------------------------------------------------------------//Muda o tipo de cursor do LCD.
//--------------------------------------------------------------------------void LCD_TrocaCursor(int Tipo) //Muda o tipo de cursor do display do LCD.
{
LCD_Modo_Comando(); //Pe no modo Comando.
LCD_EnviaChar(Tipo); //Muda o tipo de cursor.
LCD_Modo_Dado(); //Volta ao modo Dado.
}
//-------------------------------------------------------------------------------------------//Pisca uma string na linha/coluna do LCD, definindo um intervalo.
//-------------------------------------------------------------------------------------------void LCD_PiscaString(int ColunaX, int LinhaY, char *String, int Vezes)
{
char Apaga[16]; //para armazenar 16 espaos.
int Conta=0;
int TamaString;
TamaString = strlen(String); //acha o tamanho do texto.

memset(Apaga,' ',TamaString); //preenche com espaos a varivel 'Apaga'.


Apaga[TamaString] = '\0'; //finaliza a string.
while( Conta < Vezes ) //enquanto nenhuma tecla for pressionada.
{
LCD_Gotoxy(ColunaX, LinhaY); //posiciona texto.
LCD_EnviaString(String); //escreve texto.
delay_ms(200); //aguarda.
LCD_Gotoxy(ColunaX, LinhaY); //posiciona espaos.
LCD_EnviaString(Apaga); //apaga texto.
delay_ms(200); //aguarda.
Conta++;
}
}
//---------------------------------------------------------------------------------------------------------------//Cria at 8 caracteres personalizados na CGRAM.
//Endereo para cada caracter: 0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78
//Aps a criao, os novos caracteres podem ser acessados respectivamente
//atravs dos seguintes caracteres ASC: 0, 1, 2, 3, 4, 5, 6 e 7.
//Para cada caracter so necessrios passa 8 bytes atravs da matriz codigo[].
//---------------------------------------------------------------------------------------------------------------void LCD_Cria_Car_CGRAM(int Endereco, unsigned char codigo[] )
{
int i;
LCD_Modo_Comando();
LCD_EnviaChar(Endereco); //Endereo para criar o caracter.
LCD_Modo_Dado();
for(i=0; i<8; i++)
LCD_EnviaChar(codigo[i]); //Desenha na CGRAM o novo caracter.
}
//--------------------------------------------------------------------------//Configura o LCD.
//--------------------------------------------------------------------------void LCD_Configura(void)
{
delay_ms(30); //Aguarda a tenso no LCD estabilizar.
LCD_Modo_Comando();
LCD_EnviaChar(0x33);
delay_ms(10);
LCD_EnviaChar(0x32);
delay_ms(10);
LCD_EnviaChar(LCD_MODO_4_BITS);
delay_ms(10);
LCD_EnviaChar(LCD_FIXA_MODO);
delay_ms(10);
LCD_EnviaChar(LCD_DISPLAY_ON);
delay_ms(10);
LCD_EnviaChar(LCD_LIMPA_DISPLAY);
delay_ms(10);
LCD_Modo_Dado();
}

O driver acima foi escrito para simplificar a programao com LCDs, deve
ser usado quando formos criar um novo programa fonte. Use a diretiva #include
<DriverLCD.c> para incluir o driver ao seu novo programa fonte.

DownLoad do projeto PIC_LCD para gravar o PIC16F877.


Fonte 2 - Programa LCD_Serial.c para gravar o PIC16F877
//Curso USB/Serial.
//Programa: LCD_Serial.c
//Controle de LCD no modo 4 bits atravs de uma interface Serial.
//Configurao: 9600,8,N,1.
//www.rogercom.com
//rogercom@rogercom.com
//Antonio Rogrio Messias
//----------------------------------------------------------------------------------------------#include <16F877.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#use delay(clock=4000000) //4Mhz.
#fuses XT, PUT, NOWDT, NOBROWNOUT, NOLVP
#use rs232(BAUD=9600, parity=N, BITS=8, XMIT=pin_c6, RCV=pin_c7)
#include <DriverLCD.c> //Funes para acesar o LCD no modo 4 bits.
//------------------------------------------------------------------------------------------------------------------------------//Interrupo de recebimento de caracter pela Serial.
//Esta funo a responsvel em receber os comandos via serial e envi-los
para o LCD.
//-------------------------------------------------------------------------------------------------------------------------------#int_rda
void Serial_Recebe_Car()
{
static char ch;
static int FlagComando=false;
ch = getc(); //Pega o caracter da Serial.
if( ch == 0xFE )
//Habilita Modo Comando (0xFE).
{
FlagComando = true;
}else if( FlagComando == true) {
//Escreve no Modo Comando.
FlagComando = false;
LCD_Modo_Comando(); //Pe LCD no modo Comando.
LCD_EnviaChar(ch); //Escreve um Comando no LCD.
LCD_Modo_Dado(); //Pe LCD no modo Dado.
}else{ //Escreve no Modo Dados.
LCD_Modo_Dado(); //Pe LCD no modo Dado.
LCD_EnviaChar(ch); //Escreve um caracter no LCD.
}
}
//---------------------------------------------------------------------------

//Pisca LED1 indicando que o PIC est em execuo.


//--------------------------------------------------------------------------#int_timer0
void MeuTimer()
{
static boolean led;
static int conta;
set_timer0(131-get_timer0());
conta++;
if(conta == 10)
{
conta = 0;
led = !led;
output_bit(pin_d1, led);
}
}
//--------------------------------------------------------------------------//Programa Principal.
void main(void)
{
set_timer0(131);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_64);
enable_interrupts(int_rda); //Habilita interrupo de recebimento.
enable_interrupts(int_timer0);
enable_interrupts(GLOBAL); //Habilita registrador de interrupo.
set_tris_d(0b00000000); //Controle do LCD.
set_tris_b(0b00000000); //Controle do LCD.
output_b(0b00000000); //Pinos desligados.
output_d(0b00000000); //Pinos desligados.
LCD_Configura(); //Inicializa LCD (funo disponvel no DriverLCD.c.)
LCD_CursorHome(); //Inicializa LCD (funo disponvel no DriverLCD.c.)
while( true ); //Loop.
}

O programa acima funciona da seguinte forma: quando um byte recebido


pela funo Serial_Recebe_Car() atravs da serial (UART do PIC), o byte
enviado imediatamente para o mdulo LCD atravs dos pinos
(D7, D6, D5 e D4) e escrito na tela do display. Para enviar uma instruo
(comando) para o LCD necessrio primeiramente enviar o byte 0xFE seguido
do comando propriamente dito (LCD_LIMPA_DISPLAY, LCD_CURSOR_HOME,
LCD_LINHA_1, etc). Quando o PIC recebe o valor 0xFE ele trata o prximo byte
como um Comando. Aps a execuo do comando, ele coloca
automaticamente o LCD no modo Dado. A partir desse ponto, qualquer byte
recebido, diferente de 0xFE ser imediatamente escrito na tela do display LCD.
As funes de interrupo do timer0 podem ser eliminadas do programa,
pois seu objetivo no mesmo simplesmente fazer o LED1 do circuito da Figura
3 piscar, para mostrar ao desenvolvedor que o programa gravado no PIC est
funcionando. Na fase inicial de testes do circuito bom manter esta funo,

pois um recuso a mais para eliminao de erros. Se for remover esta funo,
elimine tambm o LED1 do circuito,, pois este no ter mais utilidade.
Figura 3 - Circuito que torna um display comum, em um display com interface Serial

No circuito acima a transmisso de dados entre o PIC e o LCD feita de


forma paralela (em 4 bits - D7, D6, D5 e D4). Como o display ir trabalhar com
bus de 4 bits, os pinos D0, D1, D2e D3 no sero usados. O PIC controla o
LCD atravs dos pinos 28, 29 e 30. No nosso projeto o programa (DriverLCD.c)
no usa o pino R/W, sendo assim, ele pode ser desconectado do PIC e
aterrado (0v).
A funo do trim-pot P1 (10k) de ajustar o contraste dos caracteres. J
o trim-pot P2 (100R) tem a funo de ajustar a luz de fundo do LCD (backlight),
se o mesmo tiver este recurso; caso contrrio desconsidere este componente
no circuito.

O LED1 e o boto RESET so opcionais e podem ser eliminados do


circuito sem interferir no funcionamento do mesmo.
Figura 4 - Tela do programa controle de LCD

Download do programa fonte e


EXE:
Delphi

C++

O programa acima um exemplo de como controlar o LCD serial do


circuito da Figura 3 conectado ao PC atravs do FT232BM.
Nesse programa so exploradas vrias funes para controle do LCD
como: movimento do cursor, movimento de textos na tela, centralizar e
posicionar textos, limpar, ligar e desligar o display ou o cursor, entre outras.

Figura 5 - Tela do programa para desenhar caracteres na memria CGRAM do LCD

Download do programa fonte e


EXE:
Delphi

C++

O programa acima tambm foi criado para ser usado com o circuito
da Figura 3. Seu objetivo criar novos caracteres para ser gravado na
memria CGRAM.
Ao clicar com o mouse na rea de desenho do caractere, os valores
correspondentes a cada linha do caracter so gerados e atribudos
matriz char linha[8]. Esse cdigo pode ser copiado e usado como modelo no
seu programa fonte, para gravar um novo caracter na CGRAM. Veja no
programa, que tambm possvel mudar o nome da matiz, inverter os pixels do
caracter, limpar o LCD, apagar a rea de desenho e o contedo da CGRAM no
LCD.
Um recurso bastante interessante neste programa a opo "Desenha em
tempo real". Se esta opo for marcada os pixels plotados na rea de desenho
sero automaticamente plotados tambm na tela do display LCD.
Esse programa s desenha em um nico endereo da memria CGRAM.
Este endereo o 40(em hexadecimal) referente a posio do caracter de
cdigo ASCII (0).

Exemplo de como gravar novos caracteres na CGRAM:

Veja no exemplo abaixo como gravar um novo caracter na memria


CGRAM usando as funes do driver Driver_LCD.c:
char linha[8];
linha[0] = 0x01;
linha[1] = 0x02;
linha[2] = 0x0E;
linha[3] = 0x01;
linha[4] = 0x0F;
linha[5] = 0x11;
linha[6] = 0x0F;
linha[7] = 0x00;

//Armazena o cdigo para criar o caracter ''.

LCD_Cria_Car_CGRAM( 0x40, linha ); //Escreve no primeiro endereo da memria CGRAM


associado ao cdigo ASCII (0).
LCD_EnviaChar( 0 ); //Mostra na tela do LCD o caracter '' gravado com a funo acima.

Para imprimir na tela do LCD o novo caracter desenhado, use o cdigo


ASCII (0). Este cdigo est associado ao endereo 0x40, ou seja, ao caracter
'' criado no exemplo acima.
Veja abaixo uma tabela com os 8 endereos vlidos para se criar caracteres na
memria CGRAM.
Tabela 3 - Endereos para se criar caracteres na CGRAM
Cdigo ASCII
Endereo
associado
0x40
0
0x48
1
0x50
2
0x58
3
0x60
4
0x68
5
0x70
6
0x78
7

Cada caracter desenhado ocupa 8 bytes da memria CGRAM. Sendo


assim, para desenhar um caracter associado ao ASCII (0) os endereos
ocupados so: 0x40, 0x41, 0x42, 0x43,0x44, 0x45, 0x46 e 0x47. J o caracter
ASCII (1) comea a partir do endereo 0x48, conforme mostrado na tabela
acima.