sexta-feira, 6 de novembro de 2015

Amperímetro com com o PIC 12F675 via porta serial (rs-232) para até 20A - Usando ACS712

           Olá pessoal que me acompanha, hoje vou postar um exemplo de como usar o chip ACS712 da empresa Alegro, para medir corrente elétrica dentre as vantagens destaco o isolamento elétrico do circuito de medição com a carga a ser testada, facilidade de uso e boa resposta ao sinais avaliados, conta ainda com a vantagem de se poder medir corrente continua ou alternada.
            Este chip se baseia na medida de corrente elétrica através do campo eletromagnético gerado em torno da área de condução do sensor, é possível com auxilio de um osciloscópio ver nitidamente a forma de onda da corrente elétrica produzida por uma fonte AC em uma carga.No nosso exemplo vou usar o chip para implementar um amperímetro que consegue medir corrente de até 20A, lembrando que é possível dependendo do modelo do chip medir até 30A conforme mostrado na figura abaixo(retirado do datasheet do fabricante):
           Como ele mede corrente AC então no momento em que não há carga o sinal de saída é a metade da tensão de alimentação do circuito ou seja para 5V você terá 2,5V, como consequência o sinal varia de 2,5V até 5V nos semiciclos positivo e de 2,5 até 0V para os semiciclo negativo, logo para medir a tensão continua o sinal de saída irá depender do sentido da corrente no circuito da carga, para resolver esse problema podemos calcular a variação de unidades de medidas, assim independente se é positivo ou negativo a variação será a mesma,mas como a tensão de saída depende da alimentação então no algoritmo de leitura o circuito deve fazer um referenciamento do valor de saída do sensor quando não houver sinal(no meu caso coloquei pra fazer isso assim que ligar o circuito e então ele começa a pegar as amostras).
            Para conectar o chip ACS712 com o pic é bem simples e usa poucos componentes externos veja na imagem abaixo como ficaria o esquema elétrico da parte de interface:
          Veja que a saída serial RS-232 deve ser ligada a um chip Max232 ou similar para que possa ser ligado ao PC, o capacitor de 10nF é para estabilizar a tensão lida e evitar pequenas flutuações.
          Por fim vou mostrar como é plotado as mensagens num terminal serial do PC, veja a próxima figura, e logo em seguida é apresentado o código fonte.Este C.I. pode ser adquirido também na forma de placas montadas (no mercado livre tem aos montes).
O código fonte:


/*
 *                                    Amperímetro - via serial          
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 12F675
 * Autor: aguivone
 * Versão: 1
 * Data :  05/11/2015
 * Descrição : mede corrente usando o CI ACS712 (usa porta serial emulada)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>  //para trabalhar com string
#include <xc.h>
#define _XTAL_FREQ 20000000

/////////////////////////////////////////////////////////configurações//////////////////////////////////////////////////
// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High speed crystal/resonator on GP4/OSC2/CLKOUT and GP5/OSC1/CLKIN)
#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)

///parametros da porta serial emulada////////////////////////////////////////////////////////////////
#define  TIME_BAUD  97//9600 bps - 20MHZ
#define  BAUD_RX    95//9600 bps - 20MHZ 
#define  TX  GP2//configura o pino que será TX - não será usado aqui
#define  RX  GP1//configura o pino que será RX

int ler_adc_un()
{
        int ulcorrente;
        PIR1bits.ADIF = 0;//limpa flag
        ADCON0bits.GO = 1; //inicia conversão
        while (PIR1bits.ADIF == 0); //espera finalizar leitura
        ulcorrente = (ADRESH << 8) | ADRESL;
        return(ulcorrente);
}

/////////////////////Funçoes utilizadas pela serial/////////////////////////////////////////////////////////////
void escreve_char(char dados) {
    int contagem = 0;
    TX = 0;
    while (contagem < 8) //envia dados + stop bit
    {
        __delay_us(TIME_BAUD);
        if ((dados & 0X01) == 0X01) //testa bit menos significativo (LSB)
        {
            TX = 1;
        } else {
            TX = 0;
        }
        dados = dados >> 1; //rotaciona bit
        contagem++;
    }
    __delay_us(TIME_BAUD); //tempo do ultimo bit
    TX = 1; //volta pra nivel alto (fim de transmissão de caracter)
    __delay_us(TIME_BAUD); //stop bit
}
///é interessante observar que está rotina irá trabalhar melhor se for 
///usado na rotina de interrupção de estado no pino, aqui vou deixar na rotina principal por 
// ser um exemplo simples
char ler() {
    int contagem = 0;
    char RX_dados = 0X00;
    if (RX == 0)//tem dados pra ler
    {
        RX_dados = 0X00;
        while (contagem < 8) //recebe dados + stop bit
        {
            __delay_us(BAUD_RX); //compensa algum delay na recepção 
            if (RX == 1) {
                RX_dados = (RX_dados | 0X80); //seta bit mais significativo (MSB)
            }
            RX_dados = RX_dados >> 1;
            contagem++;
        }
        contagem = 0;
        while ((RX == 0) && (contagem < 4)) {
            contagem++; //time out para não travar,tempo de 4 bits
            __delay_us(TIME_BAUD);
        }
    }
    return (RX_dados);
}

void escreve_frase(char *frase) {
    int tam_frase = strlen(frase);
    int contagem = 0;
    while (contagem < tam_frase)           
    {
        escreve_char(frase[contagem]);
        contagem++;
    }

}

void long_to_char(long ulQuant)
{
        char cTexto[5]={'0','0','0','0','0'};
         while(ulQuant>=10000)
            {
             ulQuant=ulQuant-10000;
             cTexto[4]++;
             }
          if(cTexto[4] > '0')
          {//se igual a zero não imprime
           escreve_char(cTexto[4]);
          }
         while(ulQuant>=1000)
            {
             ulQuant=ulQuant-1000;
             cTexto[3]++;
             }
          escreve_char(cTexto[3]);
          escreve_char(',');
          while(ulQuant>=100)
            {
             ulQuant=ulQuant-100;
             cTexto[2]++;
             }
          escreve_char(cTexto[2]);          
           while(ulQuant>=10)
            {
             ulQuant=ulQuant-10;
             cTexto[1]++;
             }
          escreve_char(cTexto[1]);
           while(ulQuant>=1)
            {
             ulQuant=ulQuant-1;
             cTexto[0]++;
             }
          escreve_char(cTexto[0]);
          escreve_char('\n');//pula linha
          escreve_char('\r');//coloca no inicio do terminal
          
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
void main(void) {
    CMCON = 7;//desabilita comparadores
    ANSEL = 0X31;//usa AN0 como entrada analógica e usa o clock interno do chip(2 a 6 us)
    WPU = 0X00;//desabilita pull ups
    TMR0 = 0;
    ADCON0 = 0X80;//justificado a esquerda , ref ligado ao vdd,usando AN0
    OSCCAL = 0XFF;//configura o oscilador interno para frequência maxima(4mhz)
    OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
    PIR1 = 0X00; //desabilita interrupções não necessária
    TRISIO = 0X03;//configura gp0 e gp1 como entrada
    INTCON = 0XC0;//desabilita interrupção do timer 0 e habilita interrupção global e de periféricos
    ADCON0bits.ADON = 1;//liga modulo de conversão
    __delay_ms(1000);//debounce e tempo para preparar hardware
    int referencia = 0;
    long aux;//auxilia nas contas que necessitem de valores maiores
    int  corrente = 20; 
    while(corrente>0)//somatório de 20 amostras
    {
           referencia = ler_adc_un() + referencia;
           __delay_ms(100);
           corrente--;
    }
    referencia = referencia/20;//tira uma media para servir como referencia 
    for(;;)
    {   
           __delay_ms(500); //envia valor a cada 0,5s
           corrente = ler_adc_un();//lê o conversor 
           if((corrente >= referencia-1)&&(corrente <= referencia+1))//range de +ou- 1 unidades
           {//para filtrar erros ou pequenas variações
             long_to_char(0);
           }
           else
           {
                if(corrente >= referencia)
                {//usar a parte inferior é mais confiável
                    aux = (corrente - referencia) * 49;//dá o valor com 3 casas decimais
                    escreve_char('+');//corrente positiva
                }
                else
                {
                    aux  = (referencia - corrente) * 49;//dá o valor com 3 casas decimais
                    escreve_char('-');//corrente positiva 
                }
                    if(aux>20000)
                    {
                        escreve_frase("Perigo! valor acima da escala \n\r");
                    }
                    else
                    {
                     long_to_char(aux);
                    }                 
           }
    }//loop infinito

}


//NOTAS DE PARAMETRIZAÇÃO:

//***********************************************************************************************************
//oscilador interno do pic 12F675(testado fisicamente )//////////////////////////////////////////////////////
// configure assim o fuse do oscilador interno => #pragma config FOSC = INTRCIO
// configure assim o fuse do cristal de 4MHZ => #pragma config FOSC = XT
//
//#define  TIME_BAUD  14//19200 bps - 4MHZ - usando cristal 
//#define  TIME_BAUD  16//19200 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    12//19200 bps - 4MHZ - osc. interno - recepção não fica confiável 

//#define  TIME_BAUD  68//9600 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    58//9600 bps - 4MHZ - cristal 4MHZ - recepção funcionou bem(houve pouquíssimo erros) - no proteus esse valor não funciona
//#define  BAUD_RX    60//9600 bps - 4MHZ - recepção funcionou bem(houve pouquíssimo erros)

//#define  TIME_BAUD  175//4800 bps - 4MHZ 
//#define  BAUD_RX    165//4800 bps - 4MHZ - recepção funcionou muito bem(100% confiável fisicamente, mas no proteus apresenta falhas)


//*************************************************************************************************
// oscilador com cristal de 20MHZ (testado fisicamente funcionou ok)
// configure assim o fuse do cristal => #pragma config FOSC = HS
//#define  TIME_BAUD  45//19200 bps - 20MHZ
//#define  BAUD_RX    42//19200 bps - 20MHZ 

//#define  TIME_BAUD  97//9600 bps - 20MHZ
//#define  BAUD_RX    95//9600 bps - 20MHZ 

//#define  TIME_BAUD  202//4800 bps - 20MHZ
//#define  BAUD_RX    198//4800 bps - 20MHZ