Ligando Display LCD 16x2 ao PIC 16F628A - MakerHero

Ligando Display LCD 16×2 ao PIC 16F628A 29

Para ligarmos um display LCD 16×2 ao PIC, precisamos antes de mais nada saber como programar pic e entender um pouco sobre a forma como esse tipo de display trabalha. Um dos tipos mais comuns de controlador utilizados em displays LCD é o HD44780, que pode ser ligado ao microcontrolador usando-se 4 ou 8 fios, e que vai determinar a velocidade de comunicação entre eles.

Não podemos simplesmente enviar dados à um LCD sem uma sequencia lógica. Antes de utilizá-lo, é necessário o envio de comandos que preparam o display para receber caracteres. Vamos tomar como exemplo o display LCD 16×2 com controlador HD44780, disponível aqui na MakerHero :

Esse tipo de display tem 16 pinos : 3 pinos de alimentação, 3 de controle, 8 de dados e 2 para acionar o backlight (ou luz de fundo), que você pode ver em detalhes na imagem abaixo :

Os pinos de controle do display LCD 16×2 são o RS, o R/W e o E.

O pino RS tem a função de mostrar ao display que tipo de dados estamos transmitindo. Em nível baixo (0), os dados são tratados como comandos, e em nível alto (1), os dados são tratados como caracteres. O pino R/W (Read/Write) é utilizado para determinar se estamos lendo ou transmitindo dados para o display. Já o pino ENABLE é utilizado para determinar o início da transferência de dados entre o microcontrolador e o display.

Os pinos de dados D0 a D7 formam o barramento de dados. O mais comum é utilizarmos apenas 4 pinos para comunicação com o microcontrolador, mas nada impede que em aplicações mais elaboradas sejam utilizados todos os 8 pinos. Essa é uma das funções do envio de comandos que preparam o display, pois é nesse momento que determinamos se vamos usar 4 ou 8 pinos para comunicação.

No Arduino temos várias bibliotecas prontas que fazem todo esse trabalho pesado. No caso do PIC, podemos agrupar os comandos também em um arquivo texto e criar uma biblioteca própria. Esse é o método que vamos utilizar nesse post, contando com a ajuda das rotinas de comando do display disponibilizadas no blog do Marcelo Maciel.

Para controle do display LCD 16×2 vamos utilizar o microcontrolador PIC16F628A, e o programa desenvolvido para uso no software MPLab. Os detalhes para configuração e utilização do MPLab você encontra no artigo Programando PIC16F628A para piscar um led, aqui mesmo no blog.

Crie no MPLab a estrutura básica do projeto, e na mesma pasta do projeto crie um arquivo texto com o nome de lcd.h. Dentro desse arquivo, copie o seguinte código :

/***************************************************************************/
/* 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 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(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 */
/***************************************************************************/

A estrutura da pasta do projeto vai ficar mais ou menos assim :

No MPLab, coloque o código abaixo, que chama as rotinas contidas no arquivo lcd.h para inicializar o display, apagar a tela, enviar caracteres, etc. No circuito vamos usar um cristal de 16Mhz, mas você pode utilizar, por exemplo um de 20Mhz. Para isso, altere a linha com o comando #use delay(clock=16000000) no início do programa:

#include <16F628A.h>            //Define o modelo do microcontrolador

// Fusíveis de configuração do microcontrolador

#FUSES NOWDT                    //Watch Dog Timer desabilitado
#FUSES HS                       //oscilador cristal
#FUSES PUT                      //Power Up Timer
#FUSES NOPROTECT                //sem proteção para leitura da eprom
#FUSES BROWNOUT                 //Resetar quando detectar brownout
#FUSES NOMCLR                   //Reset desabilitado
#FUSES NOLVP                    //prog. baixa voltagem desabilitado
#FUSES NOCPD                    //Sem travar o chip

#use delay(clock=16000000)      //Define o cristal utilizado


//Definição de entradas e saídas
//Cada bit representa um pino físico de I/O
// 0 significa que o pino será uma saída
// 1 significa que o pino será uma entrada
#define trisa 0b00000000
#define trisb 0b00000000

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

#define lcd_db4    pin_b4       // pino de dados d4 do LCD
#define lcd_db5    pin_b5       // pino de dados d5 do LCD
#define lcd_db6    pin_b6       // pino de dados d6 do LCD
#define lcd_db7    pin_b7       // pino de dados d7 do LCD
#include <lcd.h>                //declaração da biblioteca do LCD

//Programa principal
void main(){

    inicializa_lcd();               //Inicializa o LCD

while(1){
        //limpa_lcd();            //Limpa o display do LCD
        caracter_inicio(2,6);
        printf(escreve_lcd,"                ");
        delay_ms(500);
        caracter_inicio(1,4);   //Define o caracter de inicio da escrita
        printf(escreve_lcd,"MakerHero");  //Escreve no LCD
        delay_ms(2000);
               
        caracter_inicio(1,4);
        printf(escreve_lcd,"           ");
        delay_ms(500);
        caracter_inicio(2,6);
        printf(escreve_lcd,"P I C");
        delay_ms(2000);         
        }
} //fecha void main

Grave o programa utilizando o software MicroBRN, ou outro de sua preferência, e teste o microcontrolador ligando-o ao display seguindo a tabela e circuito mostrados abaixo :

Além disso, ligue o pino 5 do PIC ao GND e o pino 14 ao 5v. O cristal deve ser ligado aos pinos 15 e 16 do PIC.

A alimentação do circuito é de 5v e o potenciômetro é de 10 K, utilizado para controle do contraste do display LCD 16×2. Se necessário, coloque um resistor no pino 15 ou 16 para reduzir a intensidade da luz de fundo.

Agora você sabe como ligar seu display LCD 16×2 ao PIC.

Gostou? Deixe seu comentário logo abaixo.

Faça seu comentário

Acesse sua conta e participe

29 Comentários

  1. Parabéns pelo conteúdo! Muito bom!
    Estava tendo problemas com bibliotecas de lcd padrão que utilizam muitos ifs.
    Nestes uC que só tem 8 níveis de pilha ela se perde e os caracteres dos lcd’s ficam loucos.
    Sua biblioteca é muito simples e eficiente, resolveu meu problema!
    Muito Obrigado!

    1. Olá Mario.

      Obrigado pelo feedback.

      Att.
      Vitor Mattos.
      MakerHero.

  2. bom dia !
    pq no blog não estão aparecendo as mensagens ??
    poderiam acertar isso ??

    1. Olá Vagner!

      Imagens corrigidas, pedimos desculpas pelo transtorno.

      Abraços!
      Vinícius – Equipe MakerHero

  3. Adilson. Acompanhando seu desenvolvimento com display 16×2. Vc teria disponibilidade para um projeto com PIC 16F628A com display 8X2. Descritivo do projeto pronto. Grato – Roger

  4. Parabéns pela página,muito boa mesmo

    1. Olá Osvaldo!

      Bom saber que nosso conteúdo ajuda as pessoas! Você pode dar uma olhada no nosso fórum. Lá você pode tirar algumas dúvidas e postar seus projetos. Colaborar e aproveitar a comunidade:

      https://www.makerhero.com/forum/

      Abraços!

      André – Equipe MakerHero

  5. gostaria do programa do projeto do display pic+lcd16x2

    1. Olá Marcos!

      Dê uma olhada nesse post:
      https://www.makerhero.com/blog/display-lcd-16×2-pic-16f628a/

      Abraços!

      André – Equipe MakerHero

  6. Bom dia, o display LCD pode ser conectado a portas analógicas? de a0 a a5?

  7. Bom dia. acho muito bacana o trabalho de vocês de ensinar e ajudar quem esta aprendendo. Gostaria de saber se vocês tem e pode me enviar o diagrama da placa de desenvolvimento “acepi 18-28” ja procurei e não consigo encontrar nem mesmo na acepic. Sepuderem me ajudar eu agradeço.
    Forte abraço a tds…

  8. Olá, gostaria de saber como envio uma variável GLOBAL do tipo STRING para o LCD?.
    Atualmente uso para enviar uma variável do tipo INTEIRO para o lcd a seguinte instrução: printf(LCD_texto,”fHora: %2u:%2u:%2u”,hora,min,seg); //Escrevo
    Funciona, mas String não estou conseguindo.
    Poderia me ajudar?
    Muito obrigado!!
    Marcio Leandro

  9. Como faço para escrever uma string maior que o numero de posições do LCD, de forma que uma frase com 30 caracteres por exemplo, possa aparecer uma parte na linha de cima (16 colunas) e o restante na linha de baixo (14 colunas) ??

  10. O que faz a função bit_test()? é do proprio pic?
    Estou tentando utilizar essa biblioteca com um microcontrolador da freescale.

  11. posso utilizar o pic 16f877a ?

  12. como é possivel configurar os pinos de i/o

  13. quantos sao de i/o como os pinos sao divididos

  14. olá, eu gostaria de saber até que distância consigo ligar o display do PIC? Exemplo posso ligar o LCD há 5 metros do PIC? A ideia é fazer esta ligação através de um cabo de rede.

    1. Boa tarde Rafael,

      Não temos essa informação. Só testando mesmo.

      Abraço.

      Adilson – Equipe MakerHero

  15. Boa noite,

    Tentei fazer o projeto mas não funcionou… no display acendem todos os quadrados da linha inferior, somente isso. Tem alguma ideia do que pode ser?

    1. Ajuste o pot do Vee

  16. Esta biblioteca serve para qualquer PIC da familia 16?? mais especificamente o 16f877A?

  17. Ola. Boa tarde
    Gentileza informar se Voce grava um PIC para meu projeto. Caso positivo como fazer para enviar.

  18. Boa tarde!
    Uma dúvida, a segunda parte da programação, eu tenho que colocar logo abaixo da primeira?

    1. Boa noite Marlon,

      Não. A primeira parte você coloca em um arquivo texto, o lcd.h. A segunda parte você vai utilizar dentro do MPLab, para chamar as rotinas que foram inseridas nesse arquivo texto.

      Abraço.

      Adilson – Equipe MakerHero

  19. Meus parabéns!
    Seus posts são de grande ajuda para muita gente, eu como iniciante em PIC tenho esclarecido
    muitas dúvidas com os seus artigos.

    Que Deus te dê muita sabedoria e você continue compartilhando conosco.

    1. Valeu Gunter ! Abraço.

      Adilson – Equipe MakerHero

  20. Essa biblioteca possibilita, assim como outras, um comando para mostrar o estado ou o valor de uma variável? se sim, qual o comando?

  21. Ola amigo bom noite. será que teria como você me enviar por e-mail o arquivo deste programa. pois não consegui fazer.
    Estou precisando pois quero fazer um robo e pretendo usar o pic16f628a e um display 16×2.

    att: Mateus