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 

       



segunda-feira, 26 de outubro de 2015

Voltímetro com Smartphone Android(usando o pic 12F675).

         Olá,esse artigo é a junção de 2 posts anteriores , bluetooth e emulando uma porta serial, a proposta aqui é juntar conversor analógico + comunicação serial +bluetooth o que resulta num voltímetro sem contato físico usando apenas a comunicação do módulo ZS-40(HC-05).
          Ao ler o modulo analógico o qual varia sua conversão de 0 a 1024, sendo então enviado este valor ao aparelho ou equipamento que irá receber("no meu caso usei um celular pra isso"), o calculo da tensão fica por conta do smatphone pois assim caso mude o range de leitura, o que pode ser feito colocando um divisor resistivo(basta fazer uma busca sobre o assunto no google), basta configurar isso no aparelho.Por exemplo se eu quiser que o pic faça a leitura de 0 a 30V, devo configurar no software do celular para valor máximo de 30 assim ele irá calcular em cima deste valor.
         O aplicativo para android está disponível no link : https://drive.google.com/open?id=0Bx9JeN0dmzXxcUszcEh4LTR1T3M .
         O vídeo de demostração do funcionamento é este :

         
O Esquemático:



O Código fonte:


/*
 *                                    voltimetro DC - via serial          
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 12F675
 * Autor: aguivone
 * Versão: 1
 * Data :  15/09/2015
 * Revisado:  29/10/2015
 */
#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
#define  RX  GP1//configura o pino que será RX


int tensao=0;


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

/////////////////////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(unsigned long ulQuant)
{
        char cTexto[4]={'0','0','0','0'};
         while(ulQuant>=1000)
            {
             ulQuant=ulQuant-1000;
             cTexto[3]++;
             }
          escreve_char(cTexto[3]);
          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]);
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
void main(void) {
    CMCON = 7;//desabilita comparadores
    ANSEL = 0X31;//usa AN0 como entrada analogica 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 frequencia maxima(4mhz)
    OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
    PIR1 = 0X00; //desabilita interrupções não necessaria
    TRISIO = 0X03;//configura gp0 e gp1 como entrada
    INTCON = 0XC0;//desabilita interrupção do timer 0 e habilitainterrupção global e de perifericos
    ADCON0bits.ADON = 1;//liga modulo de conversão
    __delay_ms(300);//debounce e tempo para preparar hardware
    //logo a frequencia de interrupção é 250khz
    for(;;)
    {   __delay_ms(300);        
        tensao = ler_adc_un();       
        long_to_char(tensao);
        
    }//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 confiavel 

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

//#define  TIME_BAUD  175//4800 bps - 4MHZ 
//#define  BAUD_RX    165//4800 bps - 4MHZ - recepção funcionou muito bem(100% confiavel 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 

sexta-feira, 28 de agosto de 2015

Emulando uma porta serial no PIC12F675

 Olá,
            As vezes precisamos usar um microcontrolador como o nosso "pequeno notável" PIC12F675, pois tem somente 8 pinos ficando ideal para aplicações em que a simplicidade e redução de tamanho são significativo,mas apesar de contar com um conversor analógico digital não tem uma porta serial para entregar uma informação, qual a solução? Isso mesmo criar uma porta serial virtual ou seja emular uma porta serial.Esse algoritmo pode ser usado em outros microcontroladores para ser ter mais portas seriais(sem a necessidades de comprar um microcontrolador dedicado).
            A primeira coisa que deve ser feita é escolher o cristal a ser utilizado pois a velocidade de comunicação depende disto,neste exemplo vou colocar pra 3 tipos de configurações:usando o oscilador interno,usando um cristal externo de 4MHZ e usando um cristal de 20MHZ.
           Para ajustar os tempos de bits tive que definir 2 variáveis de controle ; "TIME_BAUD" ,usado pela transmissão e "BAUD_RX" usado pela recepção(o motivo desta variável é compensar atrasos de leitura do pino),as velocidades testadas foram 4800bps,9600bps e 19200bps, os tempos de cada bit são 208us,104us e 52us respectivamente.
           Para o cristal de 4MHZ ou o oscilador interno(que é de 4MHZ mas é do tipo RC) o melhor desempenho foi na faixa de 4800bps, enquanto o de 20MHZ funcionou bem nas três velocidades, pois os tempos de execução de rotina se tornam pouco expressivo nos tempos de envio e recepção.Os resultados foram colocados no final do código para ajudar a orientar no processo de uso do algoritmo.
           Neste exemplo o ao ser ligado o circuito ele envia uma mensagem para que o usuário digite algo no terminal serial e então repete o carácter, conta ainda com um botão que ao ser pressionado enviar o carácter "A", veja abaixo como ficou o código fonte e a simulação:

O código-fonte:

/*
 *                                               Emulando(cria uma porta virtual) uma porta serial
 *
 * Compilador : MPlabXC8 V.3.0
 * Data: 28/08/2015.
 * Microcontrolador: 12F675.
 * Autor: aguivone.
 * Versão: 1.
 * 
 * Emulado para 8 bits de dados/sem paridade/1bit de stop bit
 * Emulado para 1 bit de start e stop 
 * sem paridade
 * 
 * Antes de usar leia os parametros no final do codigo.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//para trabalhar com string
#include <xc.h>
#define  _XTAL_FREQ  20000000//usado pela rotina de tempo do pic
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = HS  // 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 = ON         // Data Code Protection bit (Data memory code protection is enabled)

#define  BOT  GP2//configura o pino que será RX
///parametros da porta serial emulada////////////////////////////////////////////////////////////////
#define  TIME_BAUD  45//19200 bps - 20MHZ
#define  BAUD_RX    42//19200 bps - 20MHZ
#define  TX  GP0//configura o pino que será TX
#define  RX  GP1//configura o pino que será RX
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////Funçoes utilizadas/////////////////////////////////////////////////////////////
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++;
    }

}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void) {
    TRISIO = 0X06; //configura gp1 e gp2 como entrada
    CMCON = 7; //desabilita comparadores
    ANSEL = 0; //habilita as portas de I/O
    char teste;
    TX = 1; //prepara pino de TX
    escreve_frase("testando comunicação serial emulada!");
    escreve_frase("\n\r digite algo?");
    for (;;) {

        if (BOT == 1) {//quando o botão é pressionado escreve 1 caracter
            escreve_char('A');
            __delay_ms(100);
        }
        teste = ler(); //verifica se tem dados pra ser lido 
        if (teste != 0X00)//tem dados se for diferente de 0X00(este caracter pode ser alterado)
        {
            escreve_frase("\n\r");
            escreve_frase("Caracter digitado = ");
            escreve_char(teste); //escreve o caracter digitado

        }

    }

}

//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 confiavel 

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

//#define  TIME_BAUD  175//4800 bps - 4MHZ 
//#define  BAUD_RX    165//4800 bps - 4MHZ - recepção funcionou muito bem(100% confiavel 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 

A simulação:


     

domingo, 9 de agosto de 2015

Bluetooth com PIC 16F648A

Olá!
       Hoje vou mostrar como comunicar com um celular Android via Bluetooth, para isto vamos usar um modulo especifico para isso e que já contém toda a pilha e o protocolo bluetooth junto(o que facilita o trabalho).
       O modulo bluetooth funciona com uma interface RS232 com o microcontrolador, funciona como se fosse uma porta serial sem fio(pois no protocolo Bluetooth existe o suporte a esse tipo de dispositivo, pois poderíamos conectar como um dispositivo de áudio, vídeo, telefone ou quaisquer outra forma que é suportada pelo protocolo),para configurar velocidade da porta serial e outros detalhes do modulo é recomendável ler o datasheet, para conseguir entrar no modo de configuração(modo AT) basta ligar-ló com o botão da placa pressionado e o pino "EN" alimentado com +5V,após entrar no modo AT(o led passa a piscar a cada 1 segundo) é possível mudar varias configurações dele.Por padrão  a velocidade de comunicação é de 38400, 1 stopbit,sem paridade.
       Neste exemplo vou demonstrar como ligar e desligar um led além de testar um botão, tudo que é enviado ao microcontrolador é retornado pela porta serial, para isto fiz um programa que roda no android para testar essa funcionalidade do modulo bluetooth, este programa foi feito no "App Inventor 2"(clique aqui para pegar um print da tela de componentes,da tela de algoritmo e o aplicativo) que é um software que roda on line desenvolvido pela faculdade de Massachusetts, pretendo colocar mais uma aba no site para mostrar como está ficando o algoritmo, para instalar basta baixar o arquivo com extensão "apk"(bluetooth.apk), pode ser que dependendo do celular o aplicativo fique com os botões e textos diferentes devido a compatibilidade.
       Na figura a seguir tem um esquemático de como ficou a ligação do modulo ao microcontrolador 16F648A:

O código fonte do microcontrolador 16F648A:

/*
 *                                              Usando a interface bluetooth no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F648A
 * Autor: aguivone
 * Versão: 1
 * Data :  05 de agosto de 2015
 */
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 16000000    // cristal de 20 Mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/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      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

////////////////////////////////////////////////////Defines////////////////////////////////////////////////////////////////
#define LIGA_MODULO               PORTBbits.RB4
#define HAB_CFG_MOD               PORTBbits.RB5  //esse pino não será usado neste exemplo 
#define LED                       PORTBbits.RB6  
#define BOTAO                     PORTBbits.RB7  
////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
char caracter,dados;
bit flag_interrupcao = 0;

/////////////////////////////////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++;
       }
}

///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////
void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(RCIF)
    {//se interrupção de recepção da serial
     caracter = RCREG;
     int erro = 0;
     if(caracter == 'A')
     {//liga led
         LED = 1;
         escreve_RS232(caracter);//indica que ligou
     }
     if(caracter == 'B')
     {
         escreve_RS232(caracter);//indica que desligou
         LED = 0;
     }
     if((caracter != 'A')&&(caracter != 'B'))
     {
        escreve_RS232(caracter);//retorna o que leu
     }     
     flag_interrupcao = 1;
     RCIF = 0;//  limpa flag de interrupção de recepção
    }
 }
void espera_resposta()
{
        int tempo=0;
        while((caracter == 'Z')&&(tempo < 10))
        {//espera resposta ou 1s pra saber quando enviar o proximo dado
          __delay_ms(100);//tempo para entra no modo AT
          tempo++;
        }
        __delay_ms(100);//tempo para garantir todo envio;
}

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

void main(void)
{
    TRISB = 0X82;//configura portB,  B1(pino RX) e B7 como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas  
    inicializa_RS232(38400,1);//Quando vai comunicar pelo modo "AT" precisa de estar neste valor de comunicação
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    LIGA_MODULO = 0; //esse pino não será usado neste exemplo   
    for(;;)
    {
        if(BOTAO == 1)
        {
           imprime_RS232("Botão pressionado!");
           __delay_ms(500);//debouce
        }
    }//loop infinito
}

A Demonstração:




colocar o video aqui 

links para baixar os arquivos,clique aqui!


terça-feira, 12 de maio de 2015

Voltímetro RMS - para medir tensão alternada(AC) via RS-232.


           Este exemplo mostra como medir a tensão RMS senoidal(a mesma da rede elétrica), para isto o circuito deve ler o valor de pico e calcular o valor RMS, neste exemplo eu calculo o valor de pico a partir da tensão RMS, mas pode ser fazer o contrario também, basta lembra que  Vrms = VP/(1,41...) ou seja valor de pico dividido pela raiz quadrada de 2.
           Um dos maiores problemas é ler a tensão 220V ou 110V diretamente com o PIC dai tem se as opções de recorrer a um chip dedicado a isso(que custa caro e é difícil de encontrar) ou usar um transformador(mas dai a corrente de magnetização e as próprias características elétrica do mesmo não são padrão, o que gerar certo retrabalho), então a forma mais simples foi usar divisores resistivos,mas dai teria que montar outros circuitos externos para manter o sinal em níveis desejado pegando os pulsos negativos e positivos, dai dentro das possibilidades de simplificar o projeto montei o seguinte circuito:

            Caso não tenha experiência com eletrônica não monte o circuito físico("não me responsabilizo por eventuais choques ou danos materiais"), monte a seguinte tela de simulação no programa proteus:

            Veja que na simulação o circuito marcando exatamente a mesma tensão que o voltímetro AC. Como o circuito foi montado em uma protoboard pode ser que dê uma pequena variação na tensão medida no circuito física de vcs(mas mesmo assim estará próximo do valor real), se for conferir a tensão com um multímetro verá que se estiver na escala 200V será bem próximo ou igual se tiver na escala de 1000V dará uma diferença maior pois o próprio multímetro tem uma variação de -+5V (nesta escala), um detalhe interessante que reparei aqui nos teste de bancada foi que dependendo do lado que eu coloco o pino dá diferença no valor(ou seja a posição Fase e Neutro deve ser respeitada), coloquei um transformador para avaliar a situação e vi que os picos positivos da senoide eram maiores e talvez isso afeta o circuito de alguma forma, talvez pela própria fonte de alimentação do circuito, mas o fato é que mesmo assim o circuito funcionou de forma satisfatoria.
          Os resistores de 100K são para limitar a corrente e promover uma queda de tensão (isso evita que os diodos entrem em curto na sua região de tensão reversa), a tensão medida é o valor da queda de tensão no resistor de 3,3K(R3),após a retificação do sinal pelos diodos(1N4007) existe um segundo divisor de tensão que irá deixar passar uma pequena corrente suficiente para o microcontrolador ler.
         Com este circuito nas mãos o próximo passo era ler diferentes tensões alternadas e montar uma equação que convertesse as unidades lidas pelo modulo ADC do PIC para o valor RMS.Um detalhe importante deste circuito é a captura do maior valor lido durante o período de uma senoide(adotei 25ms pois assim abrange uma faixa maior e pode operar tanto em 50HZ quanto em 60HZ).
        No código você pode usar ele com o simulador ou com o circuito real(físico),basta  comentar uma das linhas :
        circuito_real(); // para usar o circuito fisico
        simulador(); //para usar o simulador
    Isso se deve pelo fato dos circuitos serem diferentes.

Segue abaixo o código fonte:

/*
 *                             voltímetro AC usando RS-232 no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F877A
 * Autor: aguivone
 * Versão: 1
 * Data :  12 de março de 2015
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <delays.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///////////////////////////////////////////////////////////
#define MAXIMO  20 //tamanho máximo de amostra deve ser numero par(e maior que 4) para funcionar
// saiba que na verdade isso faz com que se tenha um impar(pois começa em 0)
char caracter;
bit flag_interrupcao = 0;
    
float variavel_aux=0;
unsigned long tensao = 0;

void delay_ms(long val)
   {
       while(val>0)
       {
           __delay_ms(1);//resolve bug do compilador
           val--;
       }
   }
///////////////////////////////////////////////////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(long quant)
{
       char convert_char6='0';
       char convert_char7='0';
       char convert_char8='0';
       char convert_char9='0';
       while(quant>=1000)
        {
         quant=quant-1000;
         convert_char6++;
         }
       while(quant>=100)
        {
         quant=quant-100;
         convert_char7++;
         }
       while(quant>=10)
        {
         quant=quant-10;
        convert_char8++;
         }
       while(quant>=1)
        {
           quant=quant-1;
         convert_char9++;
         }
          escreve(convert_char6);
          escreve(convert_char7);
          escreve(convert_char8);
          escreve(',');
          escreve(convert_char9);

}

///////////////////////////////////////funçoes usadas pelo conversor AD//////////////////////////////////////////
void inicializa_AD()
{
    ADCON0 = 0X81;//freq. div. por 32(20mhz o que leva a 1.6us cada aquisição) ;habilita modulo de conversão'
    ADCON1 = 0X84;//leitura do valor justificado a direita,apenas RA0,RA1 e RA3 são portas analogicas.
    ADIE = 1;//não habilita interrupção de conversão analogica
    ADIF = 0;
}

// ===================================================================================
// Função:    _ler_adc_un
// Parâmetros: não tem.
// Retorno   : unsigned long ultensao.
// Descrição : retorna o valor lido em unidades de medidas.
// ===================================================================================

unsigned long ler_adc_un(void)
{
    ADCON0bits.GO = 1; //inicia conversão
    while (ADCON0bits.GO_DONE == 1); //espera finalizar leitura
    return((ADRESH << 8) | ADRESL);
}
// ===================================================================================
// Função:    _Valor_pico
// Parâmetros: não tem.
// Retorno   : unsigned long tensao.
// Descrição : retorna o valor lido em unidades de medidas.
// ===================================================================================
unsigned long Valor_pico()//não colocar mais que 60 amostras
{
    unsigned long tensao_temporaria; 
    unsigned int conta=0;
    unsigned long tensao_maxima = 0;
    //tempo de 1 senoide completa em 50hz - mas funciona tambem em 60hz
    //caso queira apenas 60hz use o valor 166
        while(conta < 2500)//tempo de amostra de 25ms(assim fica compativel com 50hz))
        {
            tensao_temporaria = ler_adc_un();
            if(tensao_temporaria > tensao_maxima)//pega sempre o maior valor valor de pico
            {
               tensao_maxima =  tensao_temporaria;
            }         
            __delay_us(10); 
            conta++;  
        }  
    return (tensao_maxima);
}
// ===================================================================================
// Função:    _simulador
// Parâmetros: não tem.
// Retorno   : não tem.
// Descrição : faz calculo e joga na interface serial . //usado somente na simulação do proteus
// ===================================================================================
void simulador()
{
    if(tensao != 0) //só trata se tiver lido alguma tensão
        {
            imprime("\n\r valor pico - valor RMS");
            imprime("\n\r ");
            variavel_aux = (489*tensao)/10000; //para ter mais precisão - valor de pico 
            long_to_char((unsigned long)variavel_aux);
            imprime(" - ");
            variavel_aux = (347*tensao)/10000; //para ter mais precisão - valor RMS
            long_to_char((unsigned long)variavel_aux);
        }
}
// ===================================================================================
// Função:    _circuito_real
// Parâmetros: não tem.
// Retorno   : não tem.
// Descrição : faz calculo e joga na interface serial . //usado para ler tensão de 0 a 350V(teorico pois só foi testado até 230V)
// ===================================================================================
void circuito_real()
{
    if(tensao != 0) //só trata se tiver lido alguma tensão
        {
            imprime("\n\r valor RMS - valor pico");
            imprime("\n\r ");
            variavel_aux = (((100*tensao) + 258)/278)*10; //para ter mais precisão - valor de pico 
            long_to_char((unsigned long)variavel_aux);
            imprime(" - ");
            variavel_aux = (variavel_aux*141)/100; //para ter mais precisão - valor RMS
            long_to_char((unsigned long)variavel_aux);
        }
}
void main(void)
{
    TRISC = 0X80;//configura portC  C7 (pino RX) como entrada
    PORTC = 0;  // limpar as portas que estão configuradas como saidas
    TRISA = 0XFF;//configura portA como entrada
    inicializa_RS232(9600,1);//modo de alta velocidade
    inicializa_AD();
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    imprime("Voltimetro RMS usando a serial!");
    for(;;)
    {   
        tensao = Valor_pico();
        tensao = tensao + Valor_pico();
        tensao = tensao + Valor_pico();
        tensao = (tensao + Valor_pico())/4;//pega 4 amostras e tira uma media logo será 100ms
         //comente a linha que não for usar
       // circuito_real();
        simulador();
        delay_ms(500);//espera 0.5 segundo
    }//loop infinito

}









quinta-feira, 23 de abril de 2015

Controle remoto - parte 3 - a implementação do projeto.


       Olá, continuando o projeto do decodificador de controle remoto que pode ser usado para acionar relês (neste projeto será acionado 4 leds), com pequenas alterações é possível acionar mais relês ou cargas, mantive a parte de comunicação via serial para o caso de vocês quererem visualizar o código recebido num pc, outro detalhe foi a adição de 4 chaves de toque para fazer a gravação dos códigos além de 5 leds sendo 4 para os código do controle e um para indicar que está recebendo dados do controle remoto, o código fonte segue abaixo:

/*
 *                 Decodificando controle remoto MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F648A
 * Autor: aguivone
 * Versão: 1
 * Data :  23 de abril de 2015
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#define _XTAL_FREQ 4000000    // oscilador interno de 4 Mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#define LED_STATUS RB0
#define SINAL PORTAbits.RA0
#define CH1 PORTBbits.RB4
#define CH2 PORTBbits.RB5
#define CH3 PORTBbits.RB6
#define CH4 PORTBbits.RB7


#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/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      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = ON       // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
long  anterior = 0, atual = 0,largura_pulso = 0;
int   primeiro_pacote = 0,valor_pulso=0;
long  timeout = 0;
long  bit_0=0,bit_1=0,start_stop=0,range_bit=150;
char  comando;
long  byte_cod_baixo = 0;
long  byte_cod_alto = 0;
long  CH1_byte_cod_baixo = 1;
long  CH1_byte_cod_alto = 1;
long  CH2_byte_cod_baixo = 1;
long  CH2_byte_cod_alto = 1;
long  CH3_byte_cod_baixo = 1;
long  CH3_byte_cod_alto = 1;
long  CH4_byte_cod_baixo = 1;
long  CH4_byte_cod_alto = 1;
int   taman_byte =0;
int   stopbit_reconhecido = 0;
///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////
void interrupt CCP1(void)
{
 if(CCP1IF) //verifica se é interrupção de cpp
    {// cada incremtento do timer será a cada 1us ,pois estou usando o oscilador interno de 4mhz
        atual = CCPR1;
        CCP1IF = 0; //limpa interrupção de captura de sinal
                if (anterior > atual)
                {
                    largura_pulso = (0XFFFF - anterior) + atual;
                }
                else
                {
                    largura_pulso = atual - anterior;
                }
       anterior = atual;
   }
 }
/////////////////////////////////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 = 0;//não vou receber nenhum dado do pc
    //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 inicializa_CCP() {
    //habilita captura de pulso na borda de subida (prescaler sem divisão)
    CCP1CON = 0X05;//modo de captura sem divisão (borda de subida)
    CCP1IF = 0; //limpa flag de interrupção de captura
    CCP1IE = 1;//habilita modo de captura de pulsos*/
}

void inicializa_timer1() {
    TMR1IF = 0; //limpa flag de interrupção de timer
   // TMR1IE = 1; //habilita interrupção de estouro do timer
    TMR1IE = 0; //desabilita interrupção de estouro do timer
    T1CON = 0x81; //habilita leitura do timer 1 e o timer 1,sem prescaler
}
void limpa_pacote()
{
   comando =' ';
   byte_cod_baixo = 0;
   byte_cod_alto=0;
   taman_byte =0;
}
void rotina_tratamento(long byte_alto,long byte_baixo)
{
    int reconhecer = 1;
   if(CH1 == 0)
    {//grava codigo do botão 1
       CH1_byte_cod_alto = byte_alto;
       CH1_byte_cod_baixo = byte_baixo;
       imprime("\n\r CH1_gravado!");
       reconhecer = 0;
    }
   if(CH2 == 0)
    {//grava codigo do botão 2
       CH2_byte_cod_alto = byte_alto;
       CH2_byte_cod_baixo = byte_baixo;
       imprime("\n\r CH2_gravado!");
      reconhecer = 0;
    }
   if(CH3 == 0)
    {//grava codigo do botão 3
       CH3_byte_cod_alto = byte_alto;
       CH3_byte_cod_baixo = byte_baixo;
       imprime("\n\r CH3_gravado!");
      reconhecer = 0;
    }
   if(CH4 == 0)
    {//grava codigo do botão 4
       CH4_byte_cod_alto = byte_alto;
       CH4_byte_cod_baixo = byte_baixo;
       imprime("\n\r CH4_gravado!");
      reconhecer = 0;
    }
    if(reconhecer == 1)//indica que é pra reconhecer o codigo
    {
        TRISB = 0X08;//configura os pinos dos botoes como saidas
        PORTB = 0XFF;//desliga saidas
        if((byte_alto == CH1_byte_cod_alto )&&(byte_baixo == CH1_byte_cod_baixo))
        {
           imprime("\n\r CH1_ok");
           CH1 = 0;//liga pino
        }
        if((byte_alto == CH2_byte_cod_alto )&&(byte_baixo == CH2_byte_cod_baixo))
        {
           imprime("\n\r CH2_ok");
           CH2 = 0;//liga pino
        }
        if((byte_alto == CH3_byte_cod_alto )&&(byte_baixo == CH3_byte_cod_baixo))
        {
           imprime("\n\r CH3_ok");
           CH3 = 0;//liga pino
        }
       if((byte_alto == CH4_byte_cod_alto )&&(byte_baixo == CH4_byte_cod_baixo))
        {
           imprime("\n\r CH4_ok");
           CH4 =0;//liga pino
        }
        __delay_ms(500);//espera 0,5s
        PORTB = 0XF0;//desliga saidas
        TRISB = 0XF8;//os pinos voltam a ser entradas
    }
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void) {
    TRISA = 0XE7;//todos são entrada exceto as saidas do comparador RA3 RA4
    TRISB = 0XF8;//configura portB  B3 (ccp1) e de rb4 a rb7 como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    CMCON = 0X06;//habilita somente um comparador interno
    inicializa_CCP();
    inicializa_timer1();
    inicializa_RS232(9600,1);//modo de alta velocidade
    imprime("controle remoto2");
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    limpa_pacote();
    for(;;)
    {
          if(largura_pulso > 0)//leu pulsos
                     {
                      valor_pulso = largura_pulso;
                      if(primeiro_pacote == 0)
                          {
                           primeiro_pacote = 1;//descarta pois virá com lixo
                           limpa_pacote();
                          }
                      else
                      {
                      if(primeiro_pacote == 1)
                        {//tenta localizar o protocolo
                          primeiro_pacote = 2;
                             escreve('\n');//pula para proxima linha
                             escreve('\r');//coloca no inicio da linha
                           if((valor_pulso > 2900)&&(valor_pulso < 3200))
                            {//sony
                               escreve('G');
                               comando ='G';
                               bit_0 = 1200;
                               bit_1 = 1800;
                               start_stop=3000;
                            }
                           if(valor_pulso < 2900)
                            {//philips
                               escreve('P');
                               comando ='P';
                               bit_0 = 1800;
                               bit_1 = 2700;
                               start_stop=0;
                               if(valor_pulso > (bit_0 + range_bit))
                              {//bit 1
                                escreve('1');
                                byte_cod_baixo = 1;
                                taman_byte =1;
                              }
                              else
                              {//bit0
                                 escreve('0');
                                 taman_byte =1;
                              }
                            }
                            if((valor_pulso > 8000)&&(valor_pulso < 10000))
                            {//sansung
                               escreve('S');
                               comando ='S';
                               bit_0 = 1100;
                               bit_1 = 2250;
                               start_stop=9000;
                            }
                            if(valor_pulso > 10000)
                            {//lg
                               escreve('L');
                               comando ='L';
                               bit_0 = 1100;
                               bit_1 = 2250;
                               start_stop=13500;
                            }
                         LED_STATUS = 1;
                        }
                      else
                      {
                         if((valor_pulso > (start_stop - range_bit))&&(valor_pulso < (start_stop + range_bit)))
                              {//pulso de fim de pacote
                                escreve('F');//pra indicar que recebeu um bit de inicio ou fim de pacote 
                                if((comando == 'S')||(comando == 'L')||(comando == 'P')||(comando == 'G'))
                                  {//verifica se o pacote é valido para os protocolos, pode ser que outras marcas coicidam e funcione
                                    rotina_tratamento(byte_cod_alto,byte_cod_baixo);
                                  }
                                stopbit_reconhecido = 1;
                                // no caso do protocolo ca sony aqui pode se fazer a logica e separa os 3 codigos que são
                                //os mesmos
                              }
                         
                      if(valor_pulso < (bit_1 + range_bit))
                      {
                          if(valor_pulso > (bit_0 + range_bit))
                              {//bit 1
                                escreve('1');
                                if(taman_byte<32)
                                {
                                  long valor=1;
                                  byte_cod_baixo = byte_cod_baixo + (valor<<taman_byte);
                                }
                                else
                                {
                                  long valor=1,temp_byte;
                                  temp_byte = taman_byte-32;
                                  byte_cod_alto = byte_cod_alto + (valor<<temp_byte);
                                }
                                taman_byte++;
                              }
                              else
                              {//bit0
                                 escreve('0');
                                 taman_byte++;
                              }

                      }
                      }
                      }
                    largura_pulso=0;
                        timeout = 3000;//prepara contagem
                     }//fim do leu pulsos
     if(timeout>0)
     {
     timeout--;
     }
     if(timeout==1)
     {// tempo de estouro por esperar o pacote
          escreve('T');//timeout
          if(stopbit_reconhecido == 0)
          {
              if((comando == 'S')||(comando == 'L')||(comando == 'P')||(comando == 'G'))
              {//verifica se o pacote é valido para os protocolos, pode ser que outras marcas coicidam e funcione
                rotina_tratamento(byte_cod_alto,byte_cod_baixo);
              }
          }
          else
          {
                 stopbit_reconhecido = 0;
          }
          LED_STATUS = 0;          
      comando =' ';//limpa comando
      taman_byte = 0;
      primeiro_pacote = 0;

     }
    }//loop infinito

}

     Como eu tive que inverter o sinal de entrada pois usei um modulo de recepção infravermelho da sharp, neste caso o sinal ficou o oposto do que eu havia detectado antes,como me sobraram poucos pinos do microcontrolador tive que usar o recurso de o mesmo pino funcionar como entrada e saída ao mesmo tempo.Na simulação o circuito ficou assim a porta NOT é para simular o modulo receptor de IR


Site com um projeto legal usando esse tipo de modulo :  http://zorktronics.blogspot.com.br/2011/06/medindo-velocidade-angular-sem-um.html ,nestes links vendem uns modelos destes : 

Para demonstração do algoritmo funcionando, veja o vídeo a seguir: