quinta-feira, 17 de dezembro de 2015

Lendo tensão(0 a 55V) e temperatura(sensor interno) com o ATmega328P

Olá,
       Vamos trabalhar com medição de tensão e corrente usando o ATmega328P, como ele possui um sensor de temperatura interno a gente já mata dois coelhos com um tiro só.
        A primeira coisa que se deve fazer é configurar o hardware pra isso, é preciso estar atento a entrada Aref(referência analógica), para não ter problemas na configuração faça assim: coloque o pino Avcc ligado ao +5V,o pino Aref ligado um capacitor de 100nf que estará com o outro teminal ligado ao terra(dessa forma ele funciona como um filtro, o que reduz e muito ruídos),nunca ligue este pino ao GND pois pode inutilizar este chip para a função de leitura de tensão. Para este exemplo vou usar um cristal de 16Mhz e um circuito com o max232 para enviar os dados a um PC.
       Para fazer a leitura de tensão de 0 a 55V usei um divisor resistivo igual o da figura abaixo:
      Ou seja a razão de saída é 11:1(se entra 55V sai 5V na saída), no código deixei uma linha comentada para o caso de alguém querer usar para medir de 0 a 5V.Na simulação do proteus não deu certo colocar o divisor resistivo então optei por usar 2 potenciômetro para conseguir um resultado aproximado.Outro detalhe é que na simulação o sensor interno não é simulador então lá ele assume valor 0 o que acaba resultado em -65°C.
        A equação para medir a temperatura foi retirada deste site  http://www.embarcados.com.br/arduino-uno-sensor-de-temperatura/ .

O código fonte:

//***********************************************************************************************************
//                               Usando o conversor analógico/digital
//
// no gravador configurar os fuses assim:
//  EXTENDED 0XFF 
//  HIGH     0XD8 - neste modo funciona com qualquer valor de cristal usei um de 16Mhz  
//  LOW      0XC7
//  LOKBITS  0XFF  - sem proteção
//
//
//  versão                        : 1.1
//  descrição                   : Voltimetro e leitor de temperatura
//  Autor                      : Aguivone
//  microcontrolador           : ATmega328P
//  compilador                   : AVR-GCC / Atmel AVR Studio 6.1
//  Data do projeto            : 07/08/2014 
//  Data de atualização        : 07/08/2015 - adicionado leitura de temperatura e melhoria para ler tensão
//*************************************************************************************************************
#include <avr/io.h>
#include <string.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>// AVRJazz Mega328 SPI I/O

////////////////////////funçoes auxiliares/////////////////////////////
#define alterna_pino(x,y) x ^= _BV(y) 
#define   LED             PD2
#define  TENSAO           PC2

char caracter;
 
void RS232_inicializa(unsigned int BAUD)
{

   unsigned int velocidade = F_CPU/16/BAUD-1;
   //veja que na simulação do proteus dá erros nas velocidade diferentes de 9600 mas na pratica funciona.
   UCSR0A = 0X20;
/* habilita receiver and transmitter buffers */
  UBRR0H = (unsigned char)(velocidade>>8);
  UBRR0L = (unsigned char)velocidade;
  UCSR0B = 0X98;//deve se habilitar somente a interrupção de recepção para não travar o microcontrolador
  /* 8 bits , 1 stop bit, assincrono , sem paridade,modo nornal */
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
void escreve_caracter(unsigned char carac)
{  
            _delay_loop_1(1);//tempo para estabilizar tensão            
            while ( !( UCSR0A & (1<<UDRE0)) ); //espera transmitir para enviar um novo
            UDR0 = carac;    
            while ( !( UCSR0A & (1<<UDRE0)) ); //espera transmitir desabilita a transmissão
            _delay_loop_1(2);//tempo para garantir o envio
}
void impri_serial(const char frase[80])
{
      unsigned int indice=0;
      unsigned int tamanho = strlen(frase);    
       while(indice<=tamanho)///veja que o programa pode travar se aqui não tiver as duas aspas
       {
            escreve_caracter(frase[indice]);
            indice++;
       }

}
//////////////////////////interrupções requerida para usar a recepção serial///////////////////////////////////
ISR(USART_RX_vect)
{
 caracter = (char)UDR0;
 escreve_caracter(caracter);
}
///long para char
void long_to_char(unsigned int quant, int inicio)
{
        char texto[6];
        texto[1] = '0';
        texto[2] = '0';
        texto[3] = ',';
        texto[4] = '0';
        texto[5] = '0';
        if(quant>9999)//valor maior que 100V
        {
        texto[1] = 'F';
        texto[2] = 'A';
        texto[3] = 'L';
        texto[4] = 'H';
        texto[5] = 'A';
        }
        else
        {
              while(quant>=1000)
                {
                 quant=quant-1000;
                 texto[1]++;
                 }
              while(quant>=100)
                {
                 quant=quant-100;
                 texto[2]++;
                 }
               while(quant>=10)
                {
                 quant=quant-10;
                 texto[4]++;
                 }
               while(quant>=1)
                {
                 quant=quant-1;
                 texto[5]++;
                 }
         }
         int cont = inicio;
         while(cont<=5)
         {
             escreve_caracter(texto[cont]);
             cont++;
         }  
}

////////////rotina de leitura de tensão /////////////////////////////////
void ler_adc() {
    ADCSRA |= _BV(ADSC);//inicia conversão analogico/digital
    while ( (ADCSRA & _BV(ADSC)) );//espera terminar leitura
    float valor = ADC;
    //valor = ((ADC*50)/102);//mede de 0 a 5V
    valor = ((ADC*50)/102)*11;//mede de 0 a 55V  - colocando um resitor de 10K e outro de 1K(entre o pino de entrada e gnd) 
    long_to_char(valor,1);
    return;
}
void ler_temperatura() {
    ADCSRA |= _BV(ADSC);//inicia conversão analogico/digital
    while ( (ADCSRA & _BV(ADSC)) );//espera terminar leitura
    float valor = ADC;
    // a formula é  temperatura = (ADC - 324,31)/1,22 , mas vou arrendondar para 324
    if(valor < 324)
    {
        //temperatura negativa
        valor = (324.31-valor)/1.22;;
        escreve_caracter('-');
    }
    else
    {    //temperatura positiva
        valor = (valor-324.31)/1.22;;
        escreve_caracter('+');
    }    
    long_to_char(valor,4);
    return;
}
void inicializa_adc(int valor)
{
    if(valor == 0)///Modo ler tensão
    {
        ADMUX    = 0X42;// vai usar o pino PC2 como entrada, justificado a direita e com capacitor externo no pino de referencia(estabiliza leitura)
        ADCSRA = 0XA7;//habilita o conversor adc clock dividido por 128(para 16MHZ - pois o range deve ser de 50-200khz) e trigger
        ADCSRB = 0X41;//habilita o comparador e o trigger usará a saida dele
        DIDR0 = 0XFB;////desabilita entrada analógica(reduz consumo), deixa apenas PC2 como analógica
    }
    if(valor == 1)///Modo ler temperatura
    {
        //deve ser alterado somente esses dois registradores pois 
        ADMUX    = 0XC8;// sensor de temperatura, justificado a direita e capacitor externo no pino de referencia
        //ADCSRA = 0XA7;//habilita o trigger e o conversor adc clock dividido por 128(para 16MHZ - pois o range deve ser de 50-200khz) 
        ADCSRB = 0X41;//habilita o comparador e o trigger usará a saida dele
        //DIDR0 = 0XFF;////desabilita entrada analógica(reduz consumo)
    }

 } 

////////////////////função principal/////////////////////////////////////
int main(void)
 {
  
  //////////////////inicializa port D /////////////////////////////////////////////////////////////////////
    DDRD = 0XFE;  //inicializa portD,4 a 7 entrada
    DDRC = 0x18;//configura port C como entradas exceto C3 e C4
    PORTD = 0x00;//desabilita pull ups
    DDRB = 0x2F;//configura port B
    PORTB = 0x02;//inicia com o pino de reset e desliga wiznet em nivel alto
    RS232_inicializa(9600);
    inicializa_adc(0);
    sei();//habilita interrupções
    impri_serial("Lendo tensão e temperatura ... \n\r");
///////////////////////////////////////////////////////
 for(;;)
    {  
            impri_serial("Tensão = ");
            ler_adc();
            inicializa_adc(1);//ler temp
            _delay_ms(50);//estabilizar tensão
            impri_serial("  Temp = ");
            ler_temperatura();
            impri_serial(". \n\r ");
            inicializa_adc(0);//volta a ler tensão 
            //não sei por que em alguns terminais seriais o valor não é impresso,
            //mas na simulação e em um aplicativo que fiz mostra normalmente     
            _delay_ms(500);
            alterna_pino(PORTD,LED);//led
   }//chave do laço infinito (for)
}//chave de main

A Simulação:
Saída no terminal serial:

link para baixar o terminal serial: https://drive.google.com/file/d/0Bx9JeN0dmzXxNFhxMVFLQ2NPWHc/view?usp=sharing

Nenhum comentário :

Postar um comentário

olá,digite aqui seu comentário!