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: