Subscribe:

Ads 468x60px

e-mail: eng@marcelomaciel.com. Tecnologia do Blogger.

domingo, 18 de março de 2012

Conectando um display LCD no PIC


Um diferencial em qualquer projeto microcontrolado é utilização de um display LCD para indicação de parâmetros e informações diversas.
Um simples Display LCD 16x2 torna o projeto muito mais amigável ao usuário além de aumentar a gama de funções e operações 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 não utilizá-lo em seus projetos.

A seguir um tutorial de configuração do Display LCD 16x2 em um programa para o PIC escrito em linguagem C.



Vamos realizar a conexão de um LCD paralelo que utiliza o processador Hitachi HD44780 ou KS0066U a um microcontrolador PIC , utilizando apenas 4 vias de dados. 




Estes LCDs são muito simples, não possuem recursos como gerenciamento automático de pixels, não são coloridos (full color), não possuem iluminação ativa entre outras limitações, mas ainda são largamente utilizados na indústria. Basta ver que muitas registradoras, equipamentos hand-held, sistemas de informação de computadores servidores entre outros, ainda utilizam largamente este dispositivo.


Exemplo de equipamentos comerciais que utilizam display LCD 

Podemos fazer um paralelo à utilização 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 conexão de 4 displays de 7 seguimentos  que mostram o resultado de uma operação Fatorial. O funcionamento do circuito não é importante neste exemplo, basta observarmos a necessidade de reserva de 11 pinos do microcontrolador para mostrarmos apenas 4 caracteres apenas numéricos. Para cada dígito acrescentado, precisamos de mais um pino do microcontrolador.

Utilização de Display 7 segmentos

Ao utilizarmos um Display LCD, reservamos apenas 7 pinos de controle. Este número é o mesmo para um display 16x2 ( 32 caracteres alfa numéricos) ou um display 20x4 (80 caracteres alfa numéricos).

Utilização de Display LCD 16x2



Existem displays LCD de diversos tipos de formatos e tamanhos, todos eles são 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 16x01 - Apenas uma linha com 16 caracteres

Display 16x02 - 2 linhas com 16 caracteres cada

Display 16x04 - 4 linhas com 16 caracteres cada


Display 20x01 - Apenas uma linha com 20 caracteres

Display 20x02 - 2 linhas com 20 caracteres cada

Display 20x04 - 4 linhas com 20 caracteres cada


Basicamente, cada “célula” (Caracter) do LCD é composto de 8 pixels na horizontal e 5 pixels na vertical


Estes LCDs são conhecidos como 5x7, uma vez que a linha inferior é reservada para o cursor.


Existem também displays com 11 pixels de altura, conhecidos como 5x10.




Apresenta 14 pinos de acesso ( sem iluminação “back-light”) ou 16 pinos de acesso ( com iluminação “back-light”).

● 8 pinos de dados, 3 pinos de controle, 3 pinos de alimentação

● 2 pinos para iluminação “back-light” ( se houver)







A disposição dos pinos pode variar de modelo para modelo, veja abaixo uma outra disposição muito comum nos LCDs. Na dúvida consulte o datasheet do fabricante.


Abaixo a tabela com a função e descrição de cada pino de controle:


A alimentação padrão é de 5V ( de 4,5 à 6V) ,consumindo alguns  miliampères. 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 alimentação.


O pino (3) controla o contraste do LCD, para isso basta aplicar um sinal de 0V à +5Vcc. Podemos utilizar um potenciômetro 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 nível lógico baixo (0) os dados enviados para o LCD são tratados como comandos e os dados lidos do LCD indicam o seu estado atual (status). Quando este pino está em nível lógico alto (1), os dados são tratados como caracteres, tanto para leitura como para escrita.

Nível lógico (0): Comandos 
Nível lógico (1): Dados

O pino (5) R/W controla se a operação será de leitura (1) ou gravação (0)

O pino (6) Enable, habilita os comandos do LCD em borda de descida (de 1 para 0). É utilizado para iniciar a transferência de comandos ou caracteres entre o módulo e as linhas de dados. Quando estiver escrevendo para o display, os dados serão transmitidos apenas a partir de uma transição de high para low (H -> L) deste sinal. No entanto, para ler informações do display, as informações estarão disponíveis imediatamente após uma transição L -> H e permanecerá lá até que o sinal volte para o nível lógico baixo (0) novamente.

Os pinos (7) à (14) são 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 memória. A decisão 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 não existe nenhum pixel queimado.


Para deixá-lo operacional precisamos iniciá-lo, enviando uma sequência de comandos com a  configuração desejada. A tabela a seguir mostra quais são estes comandos.



Baseados na tabela anterior verificamos que existem diversas configurações que podem ser atribuídas ao LCD. A tabela a seguir mostra as opções disponíveis.

Lembre-se que, antes de qualquer operação com o LCD ele precisa ser inicializado utilizando estas informações da tabela acima. É importante salientar que não existe uma ordem específica para os itens de configuração especificados acima. Eles podem ser enviados em qualquer ordem, uma vez que o bit mais significativo de cada categoria indica o seu grupo de configuração. No entanto para o processo de inicialização, é importante que antes de entrarmos nos grupos acima, existe uma ordem que deve ser respeitada. Isso será visto em um capítulo específico que irá abordar a rotina de inicialização do display.



Como parte integrante do controlador que o LCD utiliza, há uma tabela de caracteres pré-programados que estão 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 são produzidos na Ásia, ele vem com um conjunto de caracteres específicos. A figura a seguir mostra a tabela existente na maioria dos LCDs. Ela é conhecida como ROM Code A00.


Existe outra tabela que também é encontrada nos LCDs, mas são mais raras. Geralmente os LCDs mais caros possuem esta opção. Novamente, é sempre muito importante ler o data sheet do seu módulo 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 português somente estarão disponíveis nesta versão de ROM. Os LCDs são fornecidos com apenas um tabela de caracteres.




Para evitar que os módulos sejam “rígidos” no que diz respeito a caracteres exibidos, todos eles possuem uma área específica para caracteres criados pelo usuário. Esta área chama-se CG RAM e é volátil, ou seja, se desligarmos o LCD ou o reiniciarmos, todas estas informações serão perdidas. Termos um capítulo específico sobre a CGRAM.

● Os endereços 0x00 à 0x07 são reservados para caracteres criados pelo usuário.

● São gravados em memória RAM (CGRAM) e se perdem ao desligar o LCD.

● Enviamos para o LCD a condição (0 ou 1) de cada pixel do caracter.

● Serão enviados 8 bytes para cada caracter.


● Para criá-lo, definimos como será nosso caracter com base em uma matriz 5x8:






● 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 serão sempre 0;

● Agora, basta escrever os bytes:



● Com o caracter pronto, basta programá-lo na CGRAM enviando a sequência de comando correta;

● Primeiro, o comando “SET CGRAM ADRESS” + o endereço inicial;

● Para o primeiro caracter disponível : 00010000;

● Em seguida, enviamos os 8 bytes que formam o caracter.

● Se continuarmos inserindo informações de bytes, o ponteiro passará para o segundo caracter e assim por diante;

● Então, para programarmos os 8 caracteres disponíveis, basta enviar o comando “SET CGRAM ADRESS”+ o endereço 0x00 e passarmos, em seguida, os 64 bytes que formarão os 8 novos caracteres;

● Maiores detalhes serão vistos nos exercícios...






Após ligarmos o LCD e o iniciarmos, o cursor irá para a primeira posição que é a 0x00 (primeira linha x primeira coluna). De acordo com que vamos imputando dados nele, o cursor irá deslocar para as posições seguintes. Este auto-incremento é uma facilidade muito interessante, pois dispensa especificar cada posição para cada caractere em separado, economizando (e muito) em linhas de códigos necessárias.

Mas, e se quisermos escrever em um ponto específico do LCD?

Neste caso, podemos especificar exatamente qual é o endereço 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 endereço na DDRAM do LCD precisaremos ter o pino RS e RW em nível lógico baixo e o bit mais significativo precisa estar obrigatoriamente em nível lógico alto. Com isso podemos endereçar até 128 posições e, ao passar o endereço, deveremos somar este bit, ou seja, precisaremos somar o valor 0x80 ao endereço desejado.

Para ilustrar, o endereçamento de um display LCD 16X02 é o seguinte:


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


Para facilitar as contas, segue abaixo o endereçamento para a maioria dos LCDs comerciais:




Independente do tamanho do LCD existem sempre 80 posições por linha que podem ser usadas. Como não existem posições 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 não haja confusões durante o endereçamento.





● Timing é uma limitação do LCD e diz respeito ao tempo de sincronização;

● Independente da velocidade do PIC, existe um tempo mínimo que o LCD precisa para processar as informações;

● Se este tempo não for respeitado, o LCD não Funcionará;

● Este tempos são apresentados no Datasheet do LCD:



● Na prática, podemos utilizar os seguintes tempos:




● O LCD baseado nos controladores HD44780 ou KS0066U possui 8 vias de dados que são enviado de forma paralela;

● Estes LCDs foram construídos para serem compatíveis com microntroladores antigos de 4 bits;

● Essa característica nos permite decidir se usaremos o LCD com 4 ou 8 vias de dados;

● Lembre-se: os dados continuarão a ter 8 bits, apenas optamos por transmiti-los usando 4 ou 8 vias

● Para iniciarmos o LCD no modo de transferência em 8 bits enviamos o comando:









O controlador HD44780 ou o KS0066U, encontrados na maioria dos módulos de LCDs alfa-numéricos existentes atualmente foram desenvolvidos para serem 100% compatíveis com microprocessadores antigos de 4 bits. No entanto esta característica é muito útil quando precisamos interfacear um microcontrolador.

Muitas vezes, durante o desenvolvimento de um projeto, precisamos ter muito cuidado com o número 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 à miniaturização, pois atualmente um grande diferencial de cada projeto está neste ponto. Cada vez mais precisamos ter equipamentos que exerçam mais funções e ocupem cada vez menos espaço. É 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 são utilizadas, e as vias D0 a D3 devem ser deixadas flutuando (sem conexão alguma) ou conectadas ao positivo da alimentação, via resistor limitador que tem seu valor entre 4K7 e 47K. Não é aconselhável aterrá-los, a não ser que o pino R/W também esteja aterrado, prevenindo que os pinos sejam configurados como saída. Se o pino for configurado erroneamente como saída e os pinos estiverem diretamente aterrados, os pinos que estiverem com valor lógico 1 poderão se queimar.

Ao energizar o LCD, ele é automaticamente posto no mode de comunicação em 8 vias. Por isso, precisamos inicializá-lo para que possa operar corretamente. Neste ponto há uma pegadinha… Como eu faço 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 não precisamos nos preocupar. Basta enviar um comando com o valor 0b001000000 (0x20) que o LCD entra no modo de 4 vias.

A partir deste momento, todas as informações que serão passadas para o LCD deverão 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 seqüência de comandos:





O LCD é composto por 6 camadas de materiais distintos que, agrupados, dão as características do display, veja:



● 1ª  Filme filtro vertical para polarizar a luz que entra



● 2ª  Carcaça de vidro com eletrodos . As formas desses eletrodos irão determinar as formas escuras que aparecem quando o LCD está ligado



● 3ª Trançado



● 4ª Carcaça de vidro com a película de eletrodo comum , com sulcos horizontais para alinhar com o filtro horizontal.



● 5ª Filme filtro horizontal para bloquear / permitir através da luz

● 6ª Superfície reflexiva para enviar luz de volta para espectador



Conhecendo como é composto o Display LCD passemos para a parte prática.

A seguir uma configuração muito básica de um circuito que integra um microcontrolador PIC 16F628A. Para a utilização com outro modelo de microcontrolador PIC basta escolher os pinos que queremos para cada função e indicá-los no programa.





● Vamos analisar o código, todo comentado, necessário para exibir uma mensagem no LCD, utilizando o PORTD de um PIC16F877a

/************************************* INICIO ************************************/

#include <16f877A.h>                                                              // microcontrolador utilizado
#fuses xt,nowdt,noprotect,put,brownout,nolvp,nocpd,nowrt // configuração dos fusíveis
#use delay(clock=4000000)
#byte porta = 0x05
#byte portb = 0x06
#byte portc = 0x07
#byte portd = 0x08
#byte porte = 0x09
#bit rs = porte.0                         // via do LCD que sinaliza recepção de dados ou comando
#bit enable = porte.1                // 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 mínimo 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 mínimo 40 microssegundos
return; // retorna
}


/******************** Função para limpar o LCD ********************/

void limpa_lcd()
{
comando_lcd(0x01);             // limpa LCD
delay_ms (2);
return;
}


/******************* Inicialização do Display de LCD ********************/

void inicializa_lcd()
{
comando_lcd(0x30);            // envia comando para inicializar display
delay_ms(4);                         // espera 4 milissegundos
comando_lcd(0x38);            // configura LCD, 8 bits, matriz de 7x5, 2 linhas
limpa_lcd();                           // limpa LCD
comando_lcd(0x0c);            // display sem cursor
comando_lcd(0x06);           // desloca cursor para a direita
return;                                    // retorna
}


/******************** Configurações do PIC ********************/

void main()
{

// configura microcontrolador
setup_adc_ports (no_analogs);

// configura os tris
set_tris_a(0b11011111);       // configuração da direção dos pinos de I/O
set_tris_b(0b00000011);
set_tris_c(0b11111101);
set_tris_d(0b00000000);
set_tris_e(0b00000100);


// inicializa os ports
porta=0x00; // limpa porta
portb=0x00; // limpa portb
portc=0x00; // limpa portc
portd=0x00; // limpa portd
porte=0x00; // 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 gravação que nos importa;

● Não poderemos ler o STATUS BIT para saber quando p LCD está pronto para o próximo dado;

● Basta apenas esperar o tempo mínimo para cada função e executar a próxima;

● Isso não é uma deficiência do projeto e sim uma característica da maioria dos projetos comerciais;

● Antes de começarmos a programação, precisamos informar quais os pinos de I/O serão utilizados pelo LCD;

● Isso facilitará a portabilidade do projeto para outros PICs ou LCDs


#define lcd_enable pin_e1                   // pino enable do LCD
#define lcd_rs pin_e0                           // pino rs 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


/*****************************************************************/
/* Envio de "Nibble" para o LCD */
/*****************************************************************/

//Esta rotina lê o "Nibble" inferior de uma variável 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<0>
output_bit(lcd_db5, bit_test(dado,1));     //Carrega DB5 do LCD com o bit DADO<1>
output_bit(lcd_db6, bit_test(dado,2));     //Carrega DB6 do LCD com o bit DADO<2>
output_bit(lcd_db7, bit_test(dado,3));     //Carrega DB7 do LCD com o bit DADO<3>

//Gera um pulso de enable
output_high(lcd_enable);                          // ENABLE = 1
delay_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:
// ENDEREÇO = 0 -> a variável DADO será uma instrução
// ENDEREÇO = 1 -> a variável DADO será um caractere

void envia_byte_lcd(boolean endereco, int dado)
{
output_bit(lcd_rs,endereco);                    // Seta o bit RS para instrução 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 função
}


/*****************************************************************/
/* Função para limpar o LCD */
/*****************************************************************/

void limpa_lcd()
{
envia_byte_lcd(0,0x01);                          // Envia instrução para limpar o LCD
delay_ms(2);                                            // Aguarda 2ms para estabilizar o LCD
return;                                                        // Retorna ao ponto de chamada da função
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
* Inicializa o LCD * /
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void inicializa_lcd()
{
output_low(lcd_db4);                             // Garante que o pino DB4 estão em 0 (low)
output_low(lcd_db5);                             // Garante que o pino DB5 estão em 0 (low)
output_low(lcd_db6);                             // Garante que o pino DB6 estão em 0 (low)
output_low(lcd_db7);                             // Garante que o pino DB7 estão em 0 (low)
output_low(lcd_rs);                                // Garante que o pino RS estão em 0 (low)
output_low(lcd_enable);                       // Garante que o pino ENABLE estão em 0 (low)
delay_ms(15);                                       // Aguarda 15ms para estabilizar o LCD

envia_nibble_lcd(0x03);                       // Envia comando para inicializar o display
delay_ms(5);                                         // Aguarda 5ms para estabilizar o LCD
envia_nibble_lcd(0x02);                      // CURSOR HOME - Envia comando para zerar o
                                                                //contador de caracteres e retornar à posição inicial
delay_ms(1);                                         // Aguarda 1ms para estabilizar o LCD
envia_byte_lcd(0,0x28);                      // FUNCTION SET - Configura o LCD para 4 bits,
                                                               // 2 linhas, fonte 5X7.
envia_byte_lcd(0,0x0c);                      // DISPLAY CONTROL - Display ligado, sem cursor
limpa_lcd();                                           // Limpa o LCD
envia_byte_lcd(0,0x06);                     // ENTRY MODE SET - Desloca o cursor para a direita
return;                                                   // Retorna ao ponto de chamada da função
}










Vamos fazer um exercício prático. O Hardware utilizado é o que segue:



Abaixo, o programa correspondente:

#include <16f877a.h>
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONSTANTES INTERNAS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

// Estas são as definições dos pinos que o LCD utiliza.
// Definem quais pinos do PIC controlarão os pinos do LCD

#define lcd_enable pin_d1                    // pino enable do LCD
#define lcd_rs pin_d0                            // pino rs (register select)do LCD
                                                                  // (0) para comandos (1) para dados
#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

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DEFINIÇÃO E INICIALIZAÇÃO DOS PORTS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#byte porta = 0x05
#byte portb = 0x06
#byte portc = 0x07
#byte portd = 0x08
#byte porte = 0x09


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ENVIO DE NIBBLE PARA O LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

//Esta rotina lê o "Nibble" inferior de uma variável e envia para o LCD.
//1byte = 8 bits = 2 Nibbles
//Serão 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<0>
output_bit(lcd_db5, bit_test(dado,1));                //Carrega DB5 do LCD com o bit DADO<1>
output_bit(lcd_db6, bit_test(dado,2));                //Carrega DB6 do LCD com o bit DADO<2>
output_bit(lcd_db7, bit_test(dado,3));                //Carrega DB7 do LCD com o bit DADO<3>
                                                                                //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:
// ENDEREÇO = 0 -> a variável DADO será uma instrução
// ENDEREÇO = 1 -> a variável DADO será um caractere

void envia_byte_lcd(boolean endereco, int dado)
{
output_bit(lcd_rs,endereco);                          // Seta o bit RS para instrução 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 função
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * ** * *
* ENVIO DE CARACTER PARA O LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /

// Esta rotina serve apenas como uma forma mais fácil de escrever um caractere no display.

void escreve_lcd(char c)                               // envia caractere para o display
{
envia_byte_lcd(1,c);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * ** * *
* FUNÇÃO PARA LIMPAR O LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * ** * * /

// Como esta operação pode ser muito utilizada, transformando-a em função
// faz com que o código compilado seja menor.

void limpa_lcd()
{
envia_byte_lcd(0,0x01);                                 // Envia instrução para limpar o LCD
                                                                          // 0 - envio de instrução
                                                                          // 0X01 - mostrar tabela
delay_ms(2);                                                   // Aguarda 2ms para estabilizar o LCD
return;                                                               // Retorna ao ponto de chamada da função
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* INICIALIZA O LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /

void inicializa_lcd()
{
output_low(lcd_db4);                                 // Garante que o pino DB4 estão em 0 (low)
output_low(lcd_db5);                                 // Garante que o pino DB5 estão em 0 (low)
output_low(lcd_db6);                                 // Garante que o pino DB6 estão em 0 (low)
output_low(lcd_db7);                                 // Garante que o pino DB7 estão em 0 (low)
output_low(lcd_rs);                                     // Garante que o pino RS estão em 0 (low)
output_low(lcd_enable);                            // Garante que o pino ENABLE estão em 0 (low)
delay_ms(15);                                            // Aguarda 15ms para estabilizar o LCD
envia_nibble_lcd(0x02);                           // CURSOR HOME - Envia comando para zerar o 
                                                             //contador de caracteres e retornar à posição inicial (0x80)
delay_ms(1);                                               // Aguarda 1ms para estabilizar o LCD
envia_byte_lcd(0,0x20);                            // FUNCTION SET - Configura o LCD para 
                                                                     //4 bits, 1 linha, fonte 5X7.
                                                                     // 0 - envio de instrução 0x20 - mostrar tabela
envia_byte_lcd(0,0x0C);                           // DISPLAY CONTROL - Display ligado, sem cursor
                                                                     // 0 - envio de instrução 0x0C - mostrar tabela
limpa_lcd();                                                 // Limpa o LCD
envia_byte_lcd(0,0x06);        // ENTRY MODE SET - A cada caracter, incrementa uma posição
                                                                     // 0 - envio de instrução 0x06 - mostrar tabela
return;                                                          // Retorna ao ponto de chamada da função
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONFIGURAÇÕES DO PIC *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * /

main()
{

// Reseta portas
porta = 0;
portb = 0;
portc = 0;
portd = 0;
porte = 0;

// configura os tris
set_tris_a(0xFF);                                 // configuração da direção 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();


//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 não há outra execução, a rotina principal fica vazia

while (true)
{
}

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




Normalmente a utilização do Display LCD é apenas uma das funções de uma aplicação prática, por este motivo, podemos criar uma biblioteca contendo todas as funções de controle do LCD em um arquivo separado do programa principal. E esta biblioteca será chamada por cada um dos muitos projetos futuros que serão 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 incluída uma linha de comando que inclua todas as funções desta biblioteca e as tornem disponíveis no programa principal.

Exemplo de declaração da biblioteca LCD.h no programa principal:


#include <16f877a.h>
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONSTANTES INTERNAS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

// Estas são as definições dos pinos que o LCD utiliza.
// Definem quais pinos do PIC controlarão os pinos do LCD

#define lcd_enable pin_d1                    // pino enable do LCD
#define lcd_rs pin_d0                            // pino rs (register select)do LCD
                                                                  // (0) para comandos (1) para dados
#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

#INCLUDE <LCD.H>                             // Declaração da biblioteca do LCD

//Continuação do programa....





Abaixo é mostrado todo o conteúdo do arquivo "lcd.h", incluindo novas funções que foram incluídas a pedido dos leitores do Blog e que serão explicadas no próprio código.

Conteudo do arquivo "lcd.h":


/***************************************************************************/
/* Rotinas para o LCD */
/***************************************************************************/

//Este é o bloco com as rotinas necessárias para manipular o LCD

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de "Nibble" para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

//Esta rotina lê o "Nibble" inferior de uma variável 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<0>
output_bit(lcd_db5, bit_test(dado,1));      //Carrega DB5 do LCD com o bit DADO<1>
output_bit(lcd_db6, bit_test(dado,2));      //Carrega DB6 do LCD com o bit DADO<2>
output_bit(lcd_db7, bit_test(dado,3));      //Carrega DB7 do LCD com o bit DADO<3>

//Gera um pulso de enable
output_high(lcd_enable);                           // ENABLE = 1
delay_us(1);                                                // Recomendado para estabilizar o LCD
output_low(lcd_enable);                            // ENABLE = 0
return;                                                          // Retorna ao ponto de chamada da função
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de Byte para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

//Esta rotina irá enviar um dado ou um comando para o LCD conforme abaixo:
// ENDEREÇO = 0 -> a variável DADO será uma instrução
// ENDEREÇO = 1 -> a variável DADO será um caractere

void envia_byte_lcd(boolean endereco, int dado)
{
output_bit(lcd_rs,endereco);                    // Seta o bit RS para instrução 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 função
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Envio de caractere para o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Esta rotina serve apenas como uma forma mais fácil de escrever um caractere
// no display. Ela pode ser eliminada e ao invés dela usaremos diretamente a
// função envia_byte_lcd(1,"<caractere a ser mostrado no LCD>"); ou
// envia_byte_lcd(1,<código do caractere a ser mostrado no LCD>);

void escreve_lcd(char c)
// envia caractere para o display
{
envia_byte_lcd(1,c);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Função para limpar o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Como esta operação pode ser muito utilizada, transformando-a em função
// faz com que o código compilado seja menor.

void limpa_lcd()
{
envia_byte_lcd(0,0x01);                          // Envia instrução para limpar o LCD
delay_ms(2);                                             // Aguarda 2ms para estabilizar o LCD
return;                                                         // Retorna ao ponto de chamada da função
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Inicializa o LCD *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void inicializa_lcd()
{
output_low(lcd_db4);                                 // Garante que o pino DB4 estão em 0 (low)
output_low(lcd_db5);                                 // Garante que o pino DB5 estão em 0 (low)
output_low(lcd_db6);                                 // Garante que o pino DB6 estão em 0 (low)
output_low(lcd_db7);                                 // Garante que o pino DB7 estão em 0 (low)output_low(lcd_rs);                                    // Garante que o pino RS estão em 0 (low)output_low(lcd_enable);                            // Garante que o pino ENABLE estão em 0 (low)delay_ms(15);                                            // Aguarda 15ms para estabilizar o LCDenvia_nibble_lcd(0x03);                           // Envia comando para inicializar o display
delay_ms(5);                                              // Aguarda 5ms para estabilizar o LCD
envia_nibble_lcd(0x03);                           // Envia comando para inicializar o display
delay_ms(5);                                              // Aguarda 5ms para estabilizar o LCD
envia_nibble_lcd(0x03);                           // Envia comando para inicializar o display
delay_ms(5);                                              // Aguarda 5ms para estabilizar o LCD
envia_nibble_lcd(0x02);                           // CURSOR HOME - Envia comando para zerar o
                                                                    //contador de caracteres e retornar à posição
                                                                    // inicial (0x80).
delay_ms(1);                                             // Aguarda 1ms para estabilizar o LCD
envia_byte_lcd(0,0x28);                          // FUNCTION SET - Configura o LCD para 4 bits,
                                                                    // 2 linhas, fonte 5X7.
envia_byte_lcd(0,0x0c);                          // DISPLAY CONTROL - Display ligado, sem cursor
limpa_lcd();                                               // Limpa o LCD
envia_byte_lcd(0,0x06);                          // ENTRY MODE SET - Desloca o cursor para a direita
return;                                                        // Retorna ao ponto de chamada da função
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Define inicio da escrita *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//Esta função foi adicionada e serve para se definir em que posição do LCD deseja-se
//iniciar a escrita. Para isto basta chamar a Função "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 posicão de inicio da frase
{
int16 posicao=0;
   if(linha == 1)
   {
      posicao=0x80;                                    //Se setado linha 1, end incial 0x80
   }
   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 posição

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





Agora veremos um exemplo de aplicação prática da utilização desta biblioteca:


#include <16f877a.h>
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP,NOCPD,NOWRT
#use delay(clock=4000000)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONSTANTES INTERNAS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

// Estas são as definições dos pinos que o LCD utiliza.
// Definem quais pinos do PIC controlarão os pinos do LCD

#define lcd_enable pin_d1                    // pino enable do LCD
#define lcd_rs pin_d0                            // pino rs (register select)do LCD
                                                                  // (0) para comandos (1) para dados
#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

#INCLUDE <LCD.H>                             // Declaração da biblioteca do LCD


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DEFINIÇÃO E INICIALIZAÇÃO DOS PORTS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#byte porta = 0x05
#byte portb = 0x06
#byte portc = 0x07
#byte portd = 0x08
#byte porte = 0x09


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CONFIGURAÇÕES DO PIC *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * /

main()
{

// Reseta portas
porta = 0;
portb = 0;
portc = 0;
portd = 0;
porte = 0;

// configura os tris
set_tris_a(0xFF);                                 // configuração da direção 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");                 //escreve no LCD


caracter_inicio(2,4);                                  //define o ponto de inicio da frase
                                                                     //Linha 2 e coluna 4
printf(escreve_lcd,"DO MARCELO");     //escreve no LCD



}

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



41 comentários:

  1. oi marcelo!
    tenho pic 16f 877a e um display 20x4,vc sabe como encrever
    no display uma letra ocupando as 4 linhas??

    ResponderExcluir
  2. Olá Marcelo beleza??
    Eu tenho um kit que contem um display de lcd
    mas não sei configura-lo vc pode me ajudar??
    O meu kit usa o port (b).
    LCD 16x2
    RB0==E
    RB1==RS
    RB2==RW
    RB4==D4
    RB5==D5
    RB6==D6
    RB7==D7
    Tem algum jeito mais facil;acabei de comprar um
    livro de pic e estou tentando aprender sozinho.

    Valeu!!

    Fabio neri

    ResponderExcluir
  3. Basta modificar as seguintes linhas:

    #define lcd_enable pin_b0
    #define lcd_rs pin_b1
    #define lcd_db4 pin_b4
    #define lcd_db5 pin_b5
    #define lcd_db6 pin_b6
    #define lcd_db7 pin_b7

    este código não utiliza o pino RW que serve para determinar se o LCD será lido (1) ou escrito (o).
    Neste caso basta deixa este pino sempre em nível lógico zero.

    output_low(pin_b2);

    Quanto ao livro, sugiro que siga ele por completos, fazendo todos os exercícios e exemplo, assim vc vai aprender bem mais rápido. Depois pegue outros livros de outros autores e faça o mesmo. Vc vai ver seu progresso acontecer.

    ResponderExcluir
  4. ola Marcelo

    Tenho um pic 18f4520 e nao consigo escrever em modo 4 bits, esse codigo publicado funciona com esse controlador
    Gustavo Tristao

    ResponderExcluir
  5. boa tarde,
    se for possivel me auxiliar, estou com seguinte problema nao consigo fazer display funcionar em um PIC18F4620. Fiz uma simulaçao no proteus e nao funciona, porem alternando o processador para um 16f877a tudo normal.
    o codigo do display padrao.

    ResponderExcluir
  6. Boa tarde Marcelo, passei esse tutorial para uma galera aqui da UFSM e foi o maior sucesso. Alguns me disseram que os comandos \f, \n e \b não funcionavam e realmente esses comandos não estavam na biblioteca que vc compartilhou, e nem eram necessários uma vez que vc n os utilizou no exemplo.
    Enfim, o comando \f consegui fazer funcionar, adicionando uma parte da biblioteca de LCD do PCW, mas utilizando um display de 2 linha, n consigo escrever na segunda (\n). Caso n for pedir demais, poderia resolver este "problema" para nós e me mandar por email ou publicar por aqui?
    Um grande abraço, Juliano.

    ResponderExcluir
  7. Boa noite Juliano, fico muito feliz que este tutorial esteja ajudando a todos vocês. Depois de postar este tutorial, fiz uma modificação no código acrescentando um função para definir a linha e a coluna que se deseja iniciar a escrita. Hoje não vou conseguir escrevê-la aqui, mas prometo acrescentá-la no tutorial o mais rápido possível. Deixe seu email aqui ou entre nestes próximos dias que verás a solução para este problema. Realmente este comandos q citou naum funcionam pois esta biblioteca foi criada de forma independente da biblioteca do PCW e sem esta fosse tomada como base.

    Mais uma vez, obrigado por frequentar o Blog. Fico feliz q o objetivo de compartilhar conhecimento esteja sendo alcançado.

    Att.

    Marcelo Maciel.

    ResponderExcluir
  8. Boa tarde Marcelo,
    amigo, se puder faça a postagem da versão que vc mencionou ao colega Juliano, referente a trabalhar com lcd com linha e coluna....sua postagem foi excelente.
    obrigado
    abrço!

    ResponderExcluir
  9. Olá amigos, já atualizei o post com a inclusão da função de seleção de linha e coluna...

    Obrigado pelas visitas!

    ResponderExcluir
  10. Olá Gustavo Martins, este código funciona com a grande maioria dos Microcontroladores PIC.

    ResponderExcluir
  11. boa noite.
    gostaria de fazer uma pergunta.
    trabalho com eletronica, porem, esta parte de microcontoladores e tal , nao tenho muita experiencia no quesito programacao.
    tenho um equipamento que utiliza display16x2 e o original apresentou problema. substituí o mesmo por um novo ,porem tive uma surpresa. ele mostra informacoes no display,nas posicoes corretas, porem , os caracteres nao sao os que deveriam ser. o que devo fazer? como ja dito, em programacao sou leigo, porem com ajuda gostaria de tentar resolver esse problema. agradeco desde ja.

    ResponderExcluir
  12. Pode ser que a tabela de caracteres do Display que vc está utilizando seja diferente da tabela utilizado pelo LCD original. Leia o tópico "Tabela de Caracteres" deste artigo e me diga se os caracteres diferentes correspondem as duas tabelas. Se sim, vc deverá procurar um LCD com a mesma tabela de caracteres. Verifique o Datasheet do modelo original na Internet.

    ResponderExcluir
  13. Marcelo,obrigado pela resposta.
    consegui resolver o problema. comparando as tabelas, descobri que os mesmos, apesar de serem de diferentes fabricantes, eram totalmente compativeis. como o aparelho que estava com defeito ja havia passado por outras maos, descobri que os fios das entradas db4 e db5 estavam invertidos. feita a correcao, tudo funcionou perfeitamente.
    se nao for um incomodo, gostaria que vc me orientasse por onde começo, um livro, um programa, pois como te disse, em programacao de microcontroladores meus conhecimentos sao pouquissimos.
    obrigado e parabens pela sua humilde atitude de compartilhar seus conhecimentos.

    ResponderExcluir
  14. Olá Eder, obrigado pelas palavras e por visitar o Blog e retornar com a resposta para seu problema.
    O mundo dos microcontroladores ainda é muito mistificado, mas não é nada de mais. Basta apenas um pouco de dedicação, assim como tudo na vida, para que se consiga já fazer os primeiros programas.
    Quanto a por onde começar, indico os livros:

    -Desbravando o Pic - Ampliado e Atualizado para Pic 16f628a

    http://www.litec.com.br/produto/819-desbravando-o-pic--ampliado-e-atualizado-para-pic-16f628a/

    -Microcontroladores Pic: Programação em C

    http://www.litec.com.br/produto/422-microcontroladores-pic-programacao-em-c/

    Microcontroladores Pic18 - Aprenda e Programe em Linguagem C

    http://www.litec.com.br/produto/5226-microcontroladores-pic18--aprenda-e-programe-em-linguagem-c/

    Estes são os livros com os quais eu mais me familiarizei no inicio, sugiro que pegue cada um deles e faça TODOS os exercício do ínicio ao fim, mesmo q no início naum esteja entendendo bem. Faça isso com estes livros e procure mais livros na internet sobre PIC e faça o mesmo. Vc pode comprá-los ou encontrá-los na intenet pra baixar. Eu prefiro comprar pq além de ajudar o autor, servem sempre como consultas futuras. Mas normalmente eu baixo o livro e se for bom eu compro o impresso.

    Boa sorte nos estudos e volte pra dizer como vaia a evolução!

    Abraço!

    ResponderExcluir
  15. Marcelo quero fazer uma bomba sonora que tenha contagem faça barulho se erramos o desarme ou emita um som que foi desarmada e que tenha sensor que se movimentar e nao desarma emita um som tbm vc me ajuda??? melhor uma um pic20

    ResponderExcluir
    Respostas
    1. Olá Ramon, obrigado pela visita ao blog. Não entendi muito bem o que deseja fazer. Descreva um pouco melhor o seu projeto e qual a finalidade dele.

      Qual o seu nível de conhecimento em programação e PIC?

      Não entendi o que vc quiz dizer com PIC20.

      Excluir
  16. Olá Marcelo.. parabéns pelo tutorial.. ficou muito bem detalhado.. A minha dúvida é: como eu ligo este circuito a um computador? de modo que eu possa interagir com o LCD através de algum programa que eu desenvolva..

    Grato,

    ResponderExcluir
    Respostas
    1. Olá, seja bem vindo ao blog.
      Este circuito foi desenvolvido para funcionar de forma autonoma à um Computador, por exemplo. Esta configuração do LCD serve apenas como uma parte do programa inserido no microcontrolador, serve para como acessório para visualização do que o programa principal desejar.
      Para comunicação com um computador é preciso criar uma interface com o microcontrolador, que pode ser serial, USB ou Ethernet, dependendo do modelo do PIC escolhido. Aí sim, depois de configurado esta interface, o Microcontrolador poderá receber informações de um programa do computador e apresentar o texto no LCD.

      Excluir
  17. Parabéns Marcelo ! era o que eu precisva, porém estou escrevendo em Assembly, mas me viro aqui !
    kkk

    ResponderExcluir
  18. não consigo fazer o display gerar carácter
    pic 16f628a
    como faço?
    C+

    e possivel aproveitar o codigo de um pic para outro tipo 16f877 para 16f628

    obrigado ! Alex S.

    ResponderExcluir
    Respostas
    1. Olá Alex, obrigado pela visita e pelo comentário!

      Sim é possivel, basta apenas adequar as configurações do modelo do PIC que são diferentes para cada modelo. Mas o programa em si pode sim ser transportado para outro modelo de PIC. Claro que alguns modelos possuem recursos não encontrados em outros e se o programa necessitar destes recursos não será possivel migrar o programa, neste caso será preciso encontrar outro modelo com características compatíveis.

      Contudo este programa pode ser utilizado pela grande maioria , senão todos! Principalmente porque pode utilizar a biblioteca LCD.H em qualquer programa.

      Diga-me qual a sua experiência com microntroladores e linguagem C pra ver se posso te ajudar.

      Excluir
  19. Marcelo Maciel

    Ufa ......estou começando agora, + ja faço alguma coisa , todo dia entro em seu blog

    + é difícil to tentando fazer frases no pic 16f628a , comprei ate um kit de desenvolvimento + veio com 40pinos estou tentando adaptar para o 18 pinos ,,,,,,,,,,,quero mesmo e fazer um codigo para o 16f628 ,,,,, no proteus fiz com o 16f877 ..........vç pode fazer um codigo para o 16f628 estudo ele TODO DIA ............ TAMBEM COMPREI UM PROGRAMADOR .....USO O MULTIPROG E O COMPILADOR ...PCWHD;;;;;;;;HELP

    ResponderExcluir
    Respostas
    1. Olá Alex, acabei de postar a utilização do LCD no PIC16F628a. Acesse o link:

      http://www.marcelomaciel.com/2013/03/lcd-no-pic16f628a.html

      Excluir
  20. Olá, tenho pic 18F4550 e estou usando MPLAB X

    Sabe algum tutorial de como usar neste pic LCD 16x2?

    ResponderExcluir
    Respostas
    1. Olá, estou programando para escrever um tutorial com este assunto. Cadastre-se no blog para receber o artigo assim q for escrito.

      Excluir
  21. Boa noite Marcelo, blz. To aprendendo bastante com os tópicos aqui, valeu mesmo.
    Tenho um display 16x4 e estou programando um pic 16F877A para mostrar palavras na 1ª e 2ª linhas.As palavras andam para a esquerda sem parar, terminando no lado esquerdo do display e começando no direito.Meu programa tá muito grande, pq tenho que repetir os comandos para cada coluna do display.Existe algum comando que eu faça esse deslocamento direto?

    Exemplo do programa em C:

    lcd_putc("\f GRUPO DOUBLE ");
    lcd_putc("\r TORRICELLI ");
    delay_ms(200);


    lcd_putc("\f GRUPO DOUBLE ");
    lcd_putc("\r TORRICELLI ");
    delay_ms(200);


    lcd_putc("\f GRUPO DOUBLE ");
    lcd_putc("\r TORRICELLI ");
    delay_ms(200);


    lcd_putc("\fGRUPO DOUBLE ");
    lcd_putc("\r TORRICELLI ");
    delay_ms(200);


    lcd_putc("\fRUPO DOUBLE G");
    lcd_putc("\r TORRICELLI ");
    delay_ms(200);

    Valeu..

    ResponderExcluir
    Respostas
    1. Olá Dutra,
      vejo que está utilizando a biblioteca LCD original do CCS. Não gosto muito dela pq acho um pouco limitada, por isso utilizo esta biblioteca LCD descrita neste artigo. Com ela bastaria fazer um laço "for" ou "if" e incrementar ou decrementar a instrução de caracter de inicio da escrita no LCD.

      Excluir
  22. Boa noite Marcelo!

    Acabei de conhecer seu blog e adorei o material. Parabéns pela iniciativa e dedicação em ajudar os outros. Já programei em PIC algumas vezes, mas nada de mais. Porém parei já faz um tempinho e estou retornando agora (tô enferrujado). Tenho alguns projetos em mente. Provavelmente pedirei sua ajuda em alguns momentos. Tudo bem?! Obrigado! Abraços! Tiago Piasi

    ResponderExcluir
    Respostas
    1. Olá Tiago,
      Obrigado pela visita e pelo comentário. A ideia do blog é compartilhar conhecimento, fico feliz em poder ajudar.

      Excluir
    2. Oi Marcelo, tem como você fazer um exemplo e postar aqui, estou tentado fazer mas não estou conseguindo.

      Boa noite.

      Excluir
  23. Boa noite! Tenho dado uma olhada no seu blog e realmente ele esta bacana. Parabéns! Estou fazendo um projeto para meu TCC no curso de engª elétrica e estou compilando com PCWHD e simulando com o Proteus. Eu tenho varios arquivos includes do pic com rotinas de LCD. Ja alterei os tempos nas rotinas mas não ha oque faça funcionar o display no Proteus. VC ja usou o proteus para simular alguma rotina? Tem alguma idéia do que possa estar acontecendo? A minha idéia era montar o circuito somente quando o projeto estivesse rodando limpo. Por isto queria saber s evc pode me ajudar com alguma sugestão. Meu pic é 16F877A. Grato!

    ResponderExcluir
    Respostas
    1. Olá,
      Sempre utilizo o Proteus para testar meus circuitos, e utilizar o LCD no Proteus é muito simples. Verifique se está utilizando a biblioteca correta do LCD e se os terminais estão estão ligados corretamente. Se tudo estiver certo não tem erro. Outros programas do PIC rodam corretamente? Pode ser que a frequência do oscilador esteja configurada de forma errada no PIC, isso também costuma gerar problemas na simulação do PIC no Proteus. Está utilizando a biblioteca original do PCWHD? Verifique se está utilizando os terminais corretos indicados na biblioteca LCD e se estes terminais estão configurados corretamente na instrução Set_tris.

      Excluir
  24. Olá Marcelo, estou fazendo um projeto de tcc que será uma caixa de correio aperfeiçoado por alguns componentes eletrônicos,através de um sensor Capacitivo dentro da caixa que ao detectar a correspondência acionará o transmissor fazendo com que envie um sinal para o seu receptor que irá mostrar no display de LCD o número de correspondência dentro da caixa.O pic que estou utilizando é o 16F628A,só que estou com duvidas ao efetuar a rotina em linguagem C.Gostaria de saber se essa rotina que você utilizou no tutorial seria a mesma para meu projeto. Aguardo sua resposta, Grata.

    ResponderExcluir
  25. Olá Maria, obrigado pela visita!

    Existem infinitas maneiras de se estruturar um programa em C para o PIC, mas esta maneira descrita no artigo pode ser utilizada para o seu projeto sim. Existe a possibilidade de que precise realizar alguns ajustes, principalmente na configuração dos TRIS e de uma rotina de interrupção que provavelmente o sensor das cartas utilizará. Não sei qual a sua experiência em programação, mas geralmente utilizo um programa padrão para escrever todos os outros, realizando ajustes quando necessário.

    ResponderExcluir
  26. Olá Marcelo, tudo bem?
    Preciso de sua ajuda. Estou tentando montar um dimmer com o pic 16F877A mas não consigo desenvolver o código em C. Minha idéia é ligar um potenciometro em sua entrada analógica para comandar um TRIAC em sua saída. Tenho um modelo já montado em protoboard com detector de passagem por zero e tudo. Por favor, você poderia me ajudar? Tiago Piasi.

    ResponderExcluir
    Respostas
    1. Veja se este artigo pode te ajudar:
      http://www.marcelomaciel.com/2012/04/pwm-para-motor-dc-30a-com-pic-16f877a.html

      Fiz um PWM com um IGBT, mas o principio com o TRIAC é semelhante.

      Excluir
  27. olá consigo programa-lo com qualquer tipo de display LCD? como por exemplo esse http://produto.mercadolivre.com.br/MLB-501184568-modulo-lcd-display-para-arduino-avr-mcu-arm-pic-_JM#questionText obg desde jah!

    ResponderExcluir
    Respostas
    1. Qualquer tipo não, somente os que utilizam este mesmo controlador. Este LCD do mercadolivre que indicou é compatível e pode ser utilizado.

      Excluir
  28. olá marcelo, boa noite blz,,ve se vc pode me ajudar,,eu tenho uma fonte regulável de tensão de 0a 24v com potenciometro claro,,mais eu nao tenho um diplay,,tem com adapatar um pic e um diplay para visualizar a tensão.

    ResponderExcluir
    Respostas
    1. É claro que pode fazer com o PIC e o LCD, contudo, como o PIC aceita tensão máxima de 5V, terá construir um circuito divisor de tensão para baixar a tensão máxima de 24V para 5V. Um opção mais prática é vc comprar um display LCD para fontes que é bem barato e prático. Não sei de onde vc é, mas em São Paulo encontrará facilmente na Região da rua Santa Efigênia.

      Excluir