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: