sábado, 14 de fevereiro de 2015

Usando display de 4X20 (4 linhas por 20 caracteres) com o 16F877A - XC8

Olá, neste exemplo vou demonstrar como usar um display de LCD 4X20 com o pic 16F877A no compilador XC8, vale lembra que os arquivos com a extensão ".c" e ".h" deve estar na mesma pasta em que é criado o arquivo main(), pois assim o compilador encontra esses arquivos,  veja que no meu código principal chamei apenas os arquivos ".h" pois os outros arquivos são chamados automaticamente pelo compilador, mas caso isso não aconteça vá na aba de projetos "source files"  e clicando no mouse como botão direito escolha a opção "add existing item...", e escolha o arquivo ".c", vamos ao Código fonte :

Código fonte do arquivo "main.c":
/*                         Usando display LCD 4X20
 * File:   main.c
 * Author: Aguivone
 *
 * Created on 14 de Fevereiro de 2015, 13:28
 */

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "config_cpu.h"
#include "lcd_4X20_16F.h"
#include<delays.h>

/*
 * 
 */
void main()
{
///configuração das porta do microcontrolador///////////////////////
    TRISA = 0;//todas são saidas
    TRISB = 0;//configura portB   como saida
    TRISD = 0;//todas são saidas
    TRISC = 0;//todas são saidas
    TRISE = 0;//todas são saidas
    PORTA = 0;
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    PORTC = 0;
    PORTD = 0;
    PORTE = 0;
    ADCON0 = 0X00;//desabilita modulo ADC
    ADCON1 = 0X07;//desliga portas analogicas
    ////////////////////////////////////////////////////////////////////
    inicializar_LCD();
    GIE = 0; //desabilita GIE: Global Interrupt Enable bit
    escolhe_pos_do_cursor(1,0);//linha 1 posiçao 0
    imprime_LCD("escrevendo na linha1");
    escolhe_pos_do_cursor(2,0);//linha 2 posiçao 0
    imprime_LCD("escrevendo na linha2");
    escolhe_pos_do_cursor(3,0);//linha 3 posiçao 0
    imprime_LCD("escrevendo na linha3");
    escolhe_pos_do_cursor(4,0);//linha 4 posiçao 0
    imprime_LCD("escrevendo na linha4");
    for(;;)
    {
       // faz nada!
    }

}
Código fonte do arquivo "config_cpu.h":

/*
 * File:   config_cpu.h
 * Author: aguivone
 *
 * Created on 25 de Agosto de 2014, 16:24
 */

#ifndef CONFIG_CPU_H
#define    CONFIG_CPU_H

/*******************************configuraçôes do pic ****************************************************/
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = ON         //Codigo de proteção de firwmware




#define _XTAL_FREQ 20000000//usado para rotinas de  delays


#endif    /* CONFIG_CPU_H */

Código fonte do arquivo "lcd_4X20_16F.h":

The HTML source code is below.

/* 
 * File:   lcd_4X20_16F.h
 * Author: aguivone
 *
 * Created on 06 de fevereiro de 2015, 16:25
 */

#ifndef    LCD_4X20_16F_H
#define    LCD_4X20_16F_H

// ************************************************************************************
// Prototipos de funçoes
// ***********************************************************************************
extern void Saida_LCD(char a);
extern void comando_LCD(char a);
extern void limpar_LCD(void);
extern void escolhe_pos_do_cursor(char a, char b);
extern void inicializar_LCD(void);
extern void escreve_caracter(char a);
extern void imprime_LCD(char *a);
extern void rotaciona_LCD_direita();
extern void rotaciona_LCD_esquerda();
extern void long_to_char_LCD(unsigned long quant,int linha,int posicao);
extern void int_to_char_LCD(int quant,int linha,int posicao,int dig);

//#include "lcd_4X20_16F.c" //quando não reconhecer automaticamente usar este recurso


#endif    /*LCD_4X20_16F_H*/

Código fonte do arquivo "lcd_4X20_16F.c":

/*
 *                          FUNÇOES USADAS NO DISPLAY DE LCD
 *
 * Compilador :      MPlabXC8
 * Microcontrolador: 16F877A
 * Autor:            Aguivone
 * Versão:           1
 * Data de criação:  10/02/2015.
 */
//Biblioteca para fazer a comunicação com o display de LCD 16X2
//usando o modo 4 bits
//testada com a familia PIC16F mas pode facilmente ser usada para outras familias de pic
#include <xc.h>
#include "config_cpu.h"
#include<delays.h>

//configuracao dos pinos usado para comunicar com o display, onde os defines são os nomes dos pinos do display.

#define RS RD2
#define EN RD3
//pinos de dados
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
//
//funçoes desta biblioteca
//
void Saida_LCD(char a)
{
    if(a & 1)
        {D4 = 1;}
    else
        {D4 = 0;}
        if(a & 2)
        {D5 = 1;}
    else
        {D5 = 0;}
        if(a & 4)
        {D6 = 1;}
    else
        {D6 = 0;}
        if(a & 8)
        {D7 = 1;}
    else
        {D7 = 0;}

}
void comando_LCD(char a)
{
    RS = 0;             // => RS = 0
    Saida_LCD(a);
    EN  = 1;             // => E = 1
        __delay_ms(4);       //tempo necessario para display reconhecer
        EN  = 0;             // => E = 0
}

void limpar_LCD(void)
{
    comando_LCD(0);
    comando_LCD(1);
}

void escolhe_pos_do_cursor(char a, char b)
{
    char temp,z,y;
    if(a == 1)
    {
      temp = 0x80 + b;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
    else if(a == 2)
    {
        temp = 0xC0 + b;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
        if(a == 3)
    {
      temp = 0x94 + b;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
    else if(a == 4)
    {
        temp = 0xD4 + b;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
}

void inicializar_LCD()
{
  Saida_LCD(0x00);
   __delay_ms(20);
  comando_LCD(0x03);
  __delay_ms(5);
  comando_LCD(0x03);
  __delay_ms(11);
  comando_LCD(0x03);
  /////////////////////////////////////////////////////
  comando_LCD(0x02);
  comando_LCD(0x02);
  comando_LCD(0x08);
  comando_LCD(0x00);
  comando_LCD(0x0C);
  comando_LCD(0x00);
  comando_LCD(0x06);
}

void escreve_caracter(char a)
{
   char temp,y;
   temp = a&0x0F;//pega a parte baixa do byte
   y = a&0xF0;//pega parte alta do byte
   RS = 1;             // => RS = 1
   Saida_LCD(y>>4);             //Envia a parte alta mas antes rotaciona 4 vezes
   EN = 1;
   __delay_us(40);
   EN = 0;
   Saida_LCD(temp);// envia a parte baixa
   EN = 1;
   __delay_us(40);
   EN = 0;
}

void imprime_LCD(char *a)
{
    int i;
    for(i=0;a[i]!='\0';i++)
       escreve_caracter(a[i]);
}

void rotaciona_LCD_direita()
{
    comando_LCD(0x01);
    comando_LCD(0x0C);
}

void rotaciona_LCD_esquerda()
{
    comando_LCD(0x01);
    comando_LCD(0x08);
}

void long_to_char_LCD(unsigned long quant,int linha,int posicao)//valor, posicão inicial,linha a ser usada
{
       char convert_char2='0';
       char convert_char3='0';
       char convert_char4='0';
       escolhe_pos_do_cursor(linha,posicao);//linha 1 posiçao 1
       if(quant <10)
       {
         convert_char2=' ';
         convert_char3=' ';
       }
       else
       {
           if(quant <100)
            {
              convert_char2=' ';
            }
       }

       while(quant>=100)
        {
         quant=quant-100;
         convert_char2++;
         }
       while(quant>=10)
        {
         quant=quant-10;
        convert_char3++;
         }
       while(quant>=1)
        {
           quant=quant-1;
         convert_char4++;
         }
          escolhe_pos_do_cursor(linha,posicao);//linha , posiçao
          escreve_caracter(convert_char2);
          escolhe_pos_do_cursor(linha,posicao+1);//linha , posiçao
          escreve_caracter(convert_char3);
          escolhe_pos_do_cursor(linha,posicao+2);//linha , posiçao
          escreve_caracter(convert_char4);
}
void int_to_char_LCD(int quant,int linha,int posicao,int dig)//valor, posicão inicial,linha a ser usada,numero de digitos
{
       char convert_char3='0';
       char convert_char4='0';
       escolhe_pos_do_cursor(linha,posicao);//linha 1 posiçao 1


       while(quant>=10)
        {
         quant=quant-10;
        convert_char3++;
         }
       while(quant>=1)
        {
           quant=quant-1;
           convert_char4++;
         }

          if(dig == 2)
          {
           escolhe_pos_do_cursor(linha,posicao);//linha , posiçao
           escreve_caracter(convert_char3);
           escolhe_pos_do_cursor(linha,posicao+1);//linha , posiçao
           escreve_caracter(convert_char4);
          }
          else
          {
            escolhe_pos_do_cursor(linha,posicao);//linha , posiçao
            escreve_caracter(convert_char4);
          }
}

A simulação:

segunda-feira, 29 de dezembro de 2014

Medindo temperatura com o sensor LM35(de baixíssimo custo).

            Olá hoje vou deixar um exemplo de como usar o CI LM35(TO92) com o PIC16F877A e um display de de LCD 16X2.Para isto montei uma biblioteca para o lcd funcionar em modo 4 bits, segue abaixo o exemplo,repare que no código tive que montar uma pequena correção de erro para compensar a (pequena)não linearidade do sensor:

O código fonte :

/*
 *                             Lendo temperatura com o CI LM35
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F877A
 * Autor: aguivone
 * Versão: 1
 * Data :  29 de dezembro de 2014
 */

#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 20000000    // cristal de 20 Mhz
#include <xc.h>
#include "16F_lcd.h"



/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)


////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
char caracter;
bit flag_interrupcao = 0;
///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////

void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(ADIF)
    {//se interrupção do modulo analogico!
        ADIF = 0;//limpa flag
    }
 }

///////////////////////////////////////funçoes usadas pelo conversor AD//////////////////////////////////////////
void inicializa_ADC()
{
    ADCON0 = 0X01;//Modulo ADC configurado para cristal de 20mhz(Fosc/2) e inicialmente lê a porta AN0/liga modulo de conversão
    ADCON1 = 0X82;//justificado a direita/habilita as portas de RA0 A RA4
}

unsigned long ler_adc(int iPorta_analogica)//aqui se coloca qual porta analogica se quer usar
{
        unsigned long ulTensao;  //familia 18F e 16F
        ADCON0 = ((iPorta_analogica<<3) | (ADCON0& 0XC7));
        ADCON0bits.GO = 1;//limpa conversão
        while(ADCON0bits.GO_DONE == 1); //espera finalizar leitura
        ulTensao = (ADRESH << 8) | ADRESL;
        if(ulTensao <= 1)
        {
        ulTensao = 0;   
        }
        else
        {
          ulTensao = ulTensao/2 ;
          //agora é só corrigir a não linearidade do sensor
          if(ulTensao>24)
          {
           ulTensao--;
          }
           if(ulTensao>64)
          {
            ulTensao--;
          }
          if(ulTensao>103)
          {
             ulTensao = 100;//valor maximo aceitavel
          }

        }
        return(ulTensao);
}


void tela_inicial()
{
    escolhe_pos_do_cursor(1,1);//linha 1 posiçao 1
    imprime_LCD("Usando sensor de");
    escolhe_pos_do_cursor(2,1);//linha 2 posiçao 1
    imprime_LCD("temperatura LM35");
}
void tela_principal()
{
    escolhe_pos_do_cursor(1,1);//linha 1 posiçao 1
    imprime_LCD("  Temperatura  ");
    escolhe_pos_do_cursor(2,1);//linha 2 posiçao 1
    imprime_LCD("       C       ");
}

//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void)
{
    TRISA = 0XFF;//o portA será entrada
    TRISB = 0X00;//configura portB   como saida
    TRISD = 0X00;//todas são saidas
    TRISC = 0X80;//todas são saidas exceto C7(pino RX)
    TRISE = 0X00;//todas são saidas
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    PORTC = 0;
    PORTD = 0;
    PORTE = 0;
    inicializa_ADC();
    inicializar_LCD();//biblioteca lcd
    tela_inicial();
    __delay_ms(5000);
    limpar_LCD();
    tela_principal();
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit

    for(;;)
    {
        long_to_char_LCD(ler_adc(0),2,5);//lê porta AN0, e escreve na linha2 e posição 5
        __delay_ms(1000);//espera 1 segundo para fazer nova atualização
    }//loop infinito
    

}

O código fonte da biblioteca 16F_lcd.h:

//Biblioteca para fazer a comunicação com o display de LCD 16X2
//usando o modo 4 bits
//testada com a familia PIC16F mas pode facilmente ser usada para outras familias de pic


//configuracao dos pinos usado para comunicar com o display, onde os defines são os nomes dos pinos do display.

#define RS RD2
#define EN RD3
//pinos de dados
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
//
//funçoes desta biblioteca
//
void Saida_LCD(char a)
{
    if(a & 1)
        {D4 = 1;}
    else
        {D4 = 0;}
        if(a & 2)
        {D5 = 1;}
    else
        {D5 = 0;}
        if(a & 4)
        {D6 = 1;}
    else
        {D6 = 0;}
        if(a & 8)
        {D7 = 1;}
    else
        {D7 = 0;}

}
void comando_LCD(char a)
{
    RS = 0;             // => RS = 0
    Saida_LCD(a);
    EN  = 1;             // => E = 1
        __delay_ms(4);       //tempo necessario para display reconhecer
        EN  = 0;             // => E = 0
}

limpar_LCD()
{
    comando_LCD(0);
    comando_LCD(1);
}

void escolhe_pos_do_cursor(char a, char b)
{
    char temp,z,y;
    if(a == 1)
    {
      temp = 0x80 + b - 1;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
    else if(a == 2)
    {
        temp = 0xC0 + b - 1;
        z = temp>>4;
        y = temp & 0x0F;
        comando_LCD(z);
        comando_LCD(y);
    }
}

void inicializar_LCD()
{
  Saida_LCD(0x00);
   __delay_ms(20);
  comando_LCD(0x03);
  __delay_ms(5);
  comando_LCD(0x03);
  __delay_ms(11);
  comando_LCD(0x03);
  /////////////////////////////////////////////////////
  comando_LCD(0x02);
  comando_LCD(0x02);
  comando_LCD(0x08);
  comando_LCD(0x00);
  comando_LCD(0x0C);
  comando_LCD(0x00);
  comando_LCD(0x06);
}

void escreve_caracter(char a)
{
   char temp,y;
   temp = a&0x0F;//pega a parte baixa do byte
   y = a&0xF0;//pega parte alta do byte
   RS = 1;             // => RS = 1
   Saida_LCD(y>>4);             //Envia a parte alta mas antes rotaciona 4 vezes
   EN = 1;
   __delay_us(40);
   EN = 0;
   Saida_LCD(temp);// envia a parte baixa
   EN = 1;
   __delay_us(40);
   EN = 0;
}

void imprime_LCD(char *a)
{
    int i;
    for(i=0;a[i]!='\0';i++)
       escreve_caracter(a[i]);
}

void rotaciona_LCD_direita()
{
    comando_LCD(0x01);
    comando_LCD(0x0C);
}

void rotaciona_LCD_esquerda()
{
    comando_LCD(0x01);
    comando_LCD(0x08);
}

void long_to_char_LCD(unsigned long quant,int linha,int posicao)//valor, posicão inicial,linha a ser usada
{
       char convert_char2='0';
       char convert_char3='0';
       char convert_char4='0';
       escolhe_pos_do_cursor(linha,posicao);//linha 1 posiçao 1
       if(quant <10)
       {
         convert_char2=' ';
         convert_char3=' ';
       }
       else
       {
           if(quant <100)
            {
              convert_char2=' ';
            }
       }

       while(quant>=100)
        {
         quant=quant-100;
         convert_char2++;
         }
       while(quant>=10)
        {
         quant=quant-10;
        convert_char3++;
         }
       while(quant>=1)
        {
           quant=quant-1;
         convert_char4++;
         }
          escolhe_pos_do_cursor(linha,posicao);//linha , posiçao
          escreve_caracter(convert_char2);
          escolhe_pos_do_cursor(linha,posicao+1);//linha , posiçao
          escreve_caracter(convert_char3);
          escolhe_pos_do_cursor(linha,posicao+2);//linha , posiçao
          escreve_caracter(convert_char4);
}

A simulação do código:



quinta-feira, 11 de dezembro de 2014

Acessando a memoria eeprom no compilador XC8

         Olá, eu ja havia postado aqui uma forma de fazer o acesso da eeprom interna do microcontrolador PIC, mas recentemente vi que o compilador XC8 já contempla um modo de fazer isso de forma fácil e ágil e agora vou demonstrar como :

O CÓDIGO FONTE:

/*
 *                                              voltimetro usando RS-232 no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F877A
 * Autor: aguivone
 * Versão: 1
 * Data :  04 de março de 2013
 */
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 20000000    // cristal de 20 Mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)


////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
char caracter,dados;
bit flag_interrupcao = 0;
///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////
void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(RCIF)
    {//se interrupção de recepção da serial
        //aqui não será usado essa funcão
     caracter = RCREG;
     flag_interrupcao = 1;
     RCIF = 0;//  limpa flag de interrupção de recepção
    }
 }

/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////
void inicializa_RS232(long velocidade,int modo)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    //verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(modo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SPBRG = valor;
    RCIE = 1;//habilita interrupção de recepção
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
void escreve_RS232(char valor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = valor;
    while(TXIF ==0);//espera enviar caracter
}
void imprime_RS232(const char frase[])
{
     char indice = 0;
     char tamanho = strlen(frase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve_RS232(frase[indice]);
           indice++;
       }
}

//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void)
{
    TRISB = 0X02;//configura portB  B1 (pino RX) como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    inicializa_RS232(9600,1);//modo de alta velocidade
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    imprime_RS232("acessando a eeprom com LIB XC8 \n\r");
     eeprom_write(0X01,'T');   //escreve na eeprom
     eeprom_write(0X02,'E');
     eeprom_write(0X03,'S');
     eeprom_write(0X04,'T');
     eeprom_write(0X05,'E');
     imprime_RS232("Dados da memoria : \n\r");
     escreve_RS232(EEPROM_READ(0X01)) ;//lê os dados do endereço
     escreve_RS232(EEPROM_READ(0X02)) ;//da eeprom
     escreve_RS232(EEPROM_READ(0X03)) ;
     escreve_RS232(EEPROM_READ(0X04)) ;
     escreve_RS232(EEPROM_READ(0X05)) ;
    for(;;)
    {
   
    }//loop infinito
}

A SIMULAÇÃO:




segunda-feira, 27 de outubro de 2014

Usando o watchdog do 16F877A no XC8

/*
 *                                        usando watchdog
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F877A
 * Autor: aguivone
 * Versão: 1
 * Data :  27/10/2014
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <xc.h>
#define _XTAL_FREQ 16000000//usado para rotinas de  delays

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = ON       // Watchdog Timer Enable bit (WDT habilitado)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)


//===================================================================================
//Função:    _inicializa_RS232
//Parâmetros: unsigned long ulVelocidade
//          : unsigned int uiModo
//Retorno:   não tem retorno.
//Descrição: usada para iniciar a porta serial.
//===================================================================================
void inicializa_RS232(unsigned long ulVelocidade,unsigned int uiModo)
{//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    // verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(uiModo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SPBRG = valor;
   // PIE1 = 0X20;
    RCIE = 1;//habilita interrupção de recepção
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
//===================================================================================
//Função:    _escreve_RS232
//Parâmetros: char cValor
//Retorno:   não tem retorno.
//Descrição: usada para escrever 1 caracter.
//===================================================================================
 void escreve_RS232(char cValor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = cValor;
    while(TXIF ==0);//espera enviar caracter
}
//===================================================================================
//Função:    _imprime_RS232
//Parâmetros: const char ccFrase[]
//Retorno:   não tem retorno.
//Descrição: usada para escrever uma string(vetor de caracteres).
//===================================================================================
 void imprime_RS232(const char ccFrase[])
{
     unsigned char indice = 0;
     unsigned char tamanho = strlen(ccFrase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve_RS232(ccFrase[indice]);
           indice++;
       }
}

 


//*******************************Rotina principal*********************************************/
void main(void)
{
    TRISA = 0XFE;
    TRISB = 0X00;//configura portB   como saida
    TRISD = 0X00;//todas são saidas
    TRISC = 0X80;//todas são saidas exceto C7(pino RX)
    TRISE = 0X00;//todas são saidas
    PORTA = 0X00;
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    PORTC = 0;
    PORTD = 0;
    PORTE = 0;
    inicializa_RS232(9600,1);//modo de alta velocidade
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    ADCON0 = 0X00;//desabilita modulo ADC
    ADCON1 = 0X07;//desliga portas analogicas
    OPTION_REG = 0X0F;//habilita WDT e configura ele pra 1:128
    if((STATUS & 0X10) > 0)//verifica se watchdog já resetou alguma vez
    {
      imprime_RS232("Programa watchdog iniciado \n\r");//nunca resetou
    }
    else
    {
      imprime_RS232("houve um estouro de watchdog \n\r");//já resetou
    }
    for(;;)
    {
       //faz nada!
       //para não deixar o watchdog estourar é so colocar a linha abaixo
       //no local que se deseja monitorar 
       // CLRWDT();//limpa WDT
    }
}

A simulação:


segunda-feira, 6 de outubro de 2014

Medindo tensão de 4 canais analógicos (quase)ao mesmo tempo:

       Há momentos em que precisamos ler vários canais analógicos (quase)ao mesmo tempo dai uma maneira de se fazer isso é chaveando as entradas analógicas, veja que os valores são lidos em milivolts. 

O código fonte:

/*
 *                             lendo 4 valores de tensão usando RS-232 no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F877A
 * Autor: aguivone
 * Versão: 1
 * Data :  06 de outubro de 2014
 */
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 20000000    // cristal de 20 Mhz
#include <xc.h>

/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
char caracter;
bit flag_interrupcao = 0;
///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////

void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(RCIF)
    {//se interrupção de recepção da serial
        //aqui não será usado essa funcão
     caracter = RCREG;
     flag_interrupcao = 1;
     RCIF = 0;//  limpa flag de interrupção de recepção
    }
    if(ADIF)
    {//se interrupção do modulo analogico!
        ADIF = 0;//limpa flag
    }
 }

/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////
void inicializa_RS232(long velocidade,int modo)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    //verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(modo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SPBRG = valor;
    RCIE = 1;//habilita interrupção de recepção
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
void escreve(char valor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = valor;
    while(TXIF ==0);//espera enviar caracter
}
void imprime(const char frase[])
{
     char indice = 0;
     char tamanho = strlen(frase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve(frase[indice]);
           indice++;
       }
}
void long_to_char(unsigned long quant)
{
       char convert_char1='0';
       char convert_char2='0';
       char convert_char3='0';
       char convert_char4='0';
       while(quant>=1000)
        {
         quant=quant-1000;
         convert_char1++;
         }
       while(quant>=100)
        {
         quant=quant-100;
         convert_char2++;
         }
       while(quant>=10)
        {
         quant=quant-10;
        convert_char3++;
         }
       while(quant>=1)
        {
           quant=quant-1;
         convert_char4++;
         }
          escreve(convert_char1);
          escreve(convert_char2);
          escreve(convert_char3);
          escreve(convert_char4);

}

///////////////////////////////////////funçoes usadas pelo conversor AD//////////////////////////////////////////
void inicializa_ADC()
{
    ADCON0 = 0X01;//Modulo ADC configurado para cristal de 20mhz(Fosc/2) e inicialmente lê a porta AN0/liga modulo de conversão
    ADCON1 = 0X82;//justificado a direita/habilita as portas de RA0 A RA4
}

unsigned long ler_adc_mv(int iPorta_analogica)//aqui se coloca qual porta analogica se quer usar
{
    /*
     * se for usar a familia 12F e este
     *
        unsigned long ultensao;
        ADCON0 = (int iPorta_analogica<<2) | (ADCON0& 0XF3));
        PIR1bits.ADIF = 0;//limpa flag
        ADCON0bits.ADON = 1;//liga modulo de conversão
        ADCON0bits.GO = 1; //inicia conversão
        while (PIR1bits.ADIF == 0); //espera finalizar leitura
        ultensao = (ADRESH << 8) | ADRESL;
        ADCON0bits.ADON = 0;//desliga modulo de conversão
        return(ultensao);
    */
        unsigned long ulTensao;  //familia 18F e 16F
        ADCON0 = ((iPorta_analogica<<3) | (ADCON0& 0XC7));
        ADCON0bits.GO = 1;//limpa conversão
        while(ADCON0bits.GO_DONE == 1); //espera finalizar leitura
        ulTensao = (ADRESH << 8) | ADRESL;
        ulTensao =((ulTensao * 5000)/1023); //Vref está em milliVolts
        return(ulTensao);
}



//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void)
{
    TRISA = 0XFF;//o portA será entrada
    TRISB = 0X00;//configura portB   como saida
    TRISD = 0X00;//todas são saidas
    TRISC = 0X80;//todas são saidas exceto C7(pino RX)
    TRISE = 0X00;//todas são saidas
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    PORTC = 0;
    PORTD = 0;
    PORTE = 0;
    inicializa_RS232(9600,1);//modo de alta velocidade
    inicializa_ADC();
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    imprime("Voltimetro multiplo usando a serial! \n\r");
    imprime("AN0 - AN1 - AN2 - AN3  \n\r");
    for(;;)
    {
        long_to_char(ler_adc_mv(0));//lê porta AN0
        escreve('-');//separador de leituras
        long_to_char(ler_adc_mv(1));//lê porta AN1
        escreve('-');//separador de leituras
        long_to_char(ler_adc_mv(2));//lê porta AN2
        escreve('-');//separador de leituras
        long_to_char(ler_adc_mv(3));//lê porta AN3
        escreve('\r');//retorna pro inicio da linha
        escreve('\n');//pula linha
        __delay_ms(1000);//espera 1 segundo
    }//loop infinito

}

A simulação :


Curta a fan page do blog e tenha acesso as últimas postagem assim que publicadas!

segunda-feira, 29 de setembro de 2014

Como usar o timer e o comparador de tensão no PIC 12F675

Simples exemplo de interrupções no PIC12F675 a seguir o código fonte:

/*
 *       Exemplo de uso do timer0 e comparador analogico
 * 
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 12F675
 * Autor: aguivone
 * Versão: 1
 * Data :  29/09/2014
 */
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 4000000

#define PISCA GP2
#define LED GP5
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
// CONFIG
#pragma config FOSC = INTRCIO
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF     // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

long largura_pulso=0;
long quantida_de_passos=0;
long tempo_pisca=0;

void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(T0IF)
    {   //então: temos que 250khz / 25 incrementos = 10khz
        //então o valor do tmr0 será 256-25=231        
        tempo_pisca++;
        if(tempo_pisca > 10000)//aprox 1 segundo
        {
            PISCA = ~PISCA;
            tempo_pisca=0;
        } 
        T0IF = 0;//  limpa flag de interrupção do timer0
        TMR0 = 231;//zera timer zero
    }
    if(CMIF)
    { //tensão esta acima do esperado
         LED = COUT;
         CMIF =0;//limpa flag de interrupção
    }
 }



//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
void main(void) {
    CMCON = 2;//habilita comparador para modo isolado
    ANSEL = 0X03;//usa AN0 e AN1 como entrada analogica
    WPU = 0X00;//desabilita pull ups
    TMR0 = 0;
    OSCCAL = 0XFF;//configura o oscilador interno para frequencia maxima(4mhz)
    OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
    INTCON = 0XE0;//habilita interrupção do timer 0 e interrupção global e de perifericos
    CMIE = 1;//habilita interrupção do comparador
    TRISIO = 0X03;//configura gp0 e gp1 como entrada
    //logo a frequencia de interrupção é 250khz
    for(;;)
    {}//loop infinito

}


   O circuito fica o tempo todo comparando 2 tensões assim que o nível de tensão na entrada 1(GP0) fica igual ou maior que a entrada 2(GP1) a saída do comparador muda de estado, a simulação no proteus ficou assim:




    Para quem quiser se manter informado sobre as postagem aqui no blog basta curtir a fan page no facebook: https://www.facebook.com/pages/Microcontroladores-C/576463872371829

quinta-feira, 25 de setembro de 2014

Timer0 com PIC12F675


Neste exemplo vou demonstrar como utilizar e configurar o timer0 do PIC 12F675 no compilador MPlab XC8.

A simulação no proteus:




O Código fonte:

/*
 *                                              Exemplo de uso do timer0 
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 12F675
 * Autor: aguivone
 * Versão: 1
 * Data :  25/09/2014
 */
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 4000000

#define LED GP1
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-Up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF         // Data Code Protection bit (Data memory code protection is enabled)

long tempo_pisca=0;

void interrupt interrupcoes(void)//vetor de interrupção
 {
        LED = ~LED;
        //então: temos que 250khz / 250 incrementos = 1khz
        //então o valor do tmr0 será 256-250=6
        T0IF = 0;//  limpa flag de interrupção do timer0
        TMR0 = 6;//zera timer zero
 }


//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
void main(void) {
    TRISIO = 0X01;//configura gp0 como entrada
    CMCON = 7;//desabilita comparadores
    ANSEL = 0;//habilita as portas para funcionar de forma digital
    WPU = 0X00;//desabilita pull ups
    TMR0 = 0;
    OSCCAL = 0XFF;//configura o oscilador interno para frequencia maxima(4mhz)
    OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
    INTCON = 0XE0;//habilita interrupção do timer 0 e interrupção global e de perifericos
    //logo a frequencia de interrupção é 250khz
    for(;;)
    {
     //faz nada
    }//loop infinito

}

sexta-feira, 29 de agosto de 2014

Usando display de LCD no modo 4 bits com o XC8


Aqui vou demonstrar como usar um display de 2 linhas 16 caracteres usando o Mplab XC8, para colocar a frase na linha 1 basta inserir o carácter "\n" ena linha dois o carácter "\r" antes da mensagem desejada.Uma vantagem desta configuração é a possibilidade de escolher qualquer port e pino do microcontrolador por meio do"#define" dos bits, livrando a necessidade de ter um port especifico pra isso.


O Código fonte:


/*
 *                          USANDO DISPLAY DE LCD NO MODO 4BITS
 *
 * Compilador :      MPlabXC8
 * Microcontrolador: 18F13K22
 * Autor:            Aguivone
 * Versão:           1
 * Data de criação:  29 de agosto de 2014.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <xc.h>
#define _XTAL_FREQ 16000000//usado para rotinas de  delays
/*******************************configuraçôes do pic ****************************************************/

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config PLLEN = OFF      // 4 X PLL Enable bit (PLL is under software control)
#pragma config PCLKEN = ON      // Primary Clock Enable bit (Primary clock enabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRTEN = OFF     // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 19        // Brown Out Reset Voltage bits (VBOR set to 1.9 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up bit (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RA3 input pin enabled; MCLR disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled)
#pragma config BBSIZ = OFF      // Boot Block Size Select bit (512W boot block size)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block not protected from table reads executed in other blocks)


//************************************************************************************/
//VARIAVEIS GLOBAIS
//************************************************************************************/
char gcCaracter;
bit  gbFlag_interrupcao = 0;


//o pino R/W é conectado ao terra pois não é feita nenhuma leitura no display
//configuração:
//
// pino da porta selecionada / pino display
//
// C4 = pino de D4 do display
// C5 = pino de D5 do display
// C6 = pino de D6 do display
// C7 = pino de D7 do display
//
// C1 = pino de RS do display
// C2 = pino de EN do display
//
/*************************** configuração dos pinos **************************************************/
 #define BIT_0  LATCbits.LATC4
 #define BIT_1  LATCbits.LATC5
 #define BIT_2  LATCbits.LATC6
 #define BIT_3  LATCbits.LATC7

 #define RS     LATCbits.LATC0
 #define EN     LATCbits.LATC1

#define _XTAL_FREQ 16000000//usado para rotinas de  delays


unsigned char valor_bit(char cDa,char cBit)
{
    unsigned char retorna = 0;
    if((cDa & cBit)>= 1)
    {
     retorna = 1;
    }
    return(retorna);
}

void porta_lcd(char cDado)//byte a ser enviado
{
       //envia os bytes mais altos
       BIT_0 = valor_bit(cDado,0X10);
       BIT_1 = valor_bit(cDado,0X20);
       BIT_2 = valor_bit(cDado,0X40);
       BIT_3 = valor_bit(cDado,0X80);
       EN = 1;
       __delay_ms(10);
       EN = 0;
       __delay_ms(15);
       //envia os bytes mais baixos
       RS = 0; //habilita comandos
       BIT_0 = valor_bit(cDado,0X01);
       BIT_1 = valor_bit(cDado,0X02);
       BIT_2 = valor_bit(cDado,0X04);
       BIT_3 = valor_bit(cDado,0X08);
       EN = 1;
       __delay_ms(10);
       EN = 0;
       __delay_ms(15);
}
void escreve_LCD(char cDado,int iVlr)//byte a ser enviado ,escreve caracter(0) ou comando(1)
{
    if(iVlr == 0)
       {
         RS = 0; //habilita comandos
       }
       else
       {
        RS = 1; //habilita dados
       }
       //envia os bytes mais altos
       BIT_0 = valor_bit(cDado,0X10);
       BIT_1 = valor_bit(cDado,0X20);
       BIT_2 = valor_bit(cDado,0X40);
       BIT_3 = valor_bit(cDado,0X80);
       EN = 1;
       __delay_us(30);//tempo estabilizar o pino
       EN = 0;
       __delay_us(30);//tempo estabilizar o pino
       //envia os bytes mais baixos
       BIT_0 = valor_bit(cDado,0X01);
       BIT_1 = valor_bit(cDado,0X02);
       BIT_2 = valor_bit(cDado,0X04);
       BIT_3 = valor_bit(cDado,0X08);
       EN = 1;
       __delay_us(30);//tempo estabilizar o pino
       EN = 0;
       __delay_us(30);
}

void inicializa_display(void)
{
       EN = 0;
       __delay_ms(15);//tempo para estabilizar a tensão e preparar o display
       porta_lcd(0X02);//nesse momento o display ainda está em modo 8bits solicita modo de 4 bits
       porta_lcd(0X28);//configura para usar modo 4bits,manda usar 2 linhas e matriza de caracter de 5X7.
       porta_lcd(0X01);//limpa display
       porta_lcd(0X06);//deslocamento do cursor para direita ,sem deslocamento de caracter automaticamente.
       porta_lcd(0X0C);//liga display ,cursor desligado e

}
void impri_lcd(const unsigned char *ucFrase)//  [17])
{
      unsigned int indice=0;
      unsigned int tamanho = strlen(ucFrase);
      while(indice<tamanho)
       {
            if(ucFrase[indice]=='\n')//linha 1
            {
              escreve_LCD(0X80,0);
                        }
            if(ucFrase[indice]=='\r')//linha2
            {
                          escreve_LCD(0XC0,0);
            }
            if((ucFrase[indice]!='\r') && (ucFrase[indice]!='\n') && (indice<18))//dados
                        {
                          escreve_LCD(ucFrase[indice],1);
            }
       indice++;
       }
}

//*******************************Rotina principal*********************************************/

void main(void)
{
    TRISC = 0x00;    
    inicializa_display();
    impri_lcd("\n__Teste de lcd  ");
    impri_lcd("\rTamanho max.16__");
for(;;)
    {}//loop infinito
}

A simulação: