sexta-feira, 27 de março de 2015

Controle remoto com PIC - Parte2

         Conforme demonstrado anterior o projeto é dividido em codificador e decodificador, agora vou mostrar como é o código do receptor de controle remoto.

O código fonte:

/*
 *                                               usando o modo CCP no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F648A
 * Autor: aguivone
 * Versão: 1
 * Data :  01 de março de 2013
 */
#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

#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;

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


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

void main(void) {
    TRISA = 0XFF;//como entrada
    TRISB = 0X08;//configura portB  B3 (ccp1) como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    inicializa_CCP();
    inicializa_timer1();
    inicializa_RS232(9600,1);//modo de alta velocidade
    imprime("controle remoto");
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    for(;;)
    {
          if(largura_pulso > 0)//leu pulsos
                     {
                      valor_pulso = largura_pulso;
                      if(primeiro_pacote == 0)
                          {
                           primeiro_pacote = 1;//descarta pois virá com lixo
                          }
                      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))
                            {
                               escreve('N');
                               bit_0 = 1200;
                               bit_1 = 1800;
                               start_stop=3000;
                            }
                           if(valor_pulso < 2900)
                            {
                               escreve('P');
                               bit_0 = 1800;
                               bit_1 = 2700;
                               start_stop=0;
                               if(valor_pulso > (bit_0 + range_bit))
                              {//bit 1
                                escreve('1');
                              }
                              else
                              {//bit0
                                 escreve('0');
                              }
                            }
                            if((valor_pulso > 8000)&&(valor_pulso < 10000))
                            {
                               escreve('S');
                               bit_0 = 1100;
                               bit_1 = 2250;
                               start_stop=9000;
                            }
                            if(valor_pulso > 10000)
                            {
                               escreve('L');
                               bit_0 = 1100;
                               bit_1 = 2250;
                               start_stop=13500;
                            }

                        }
                      else
                      {
                         if((valor_pulso > (start_stop - range_bit))&&(valor_pulso < (start_stop + range_bit)))
                              {//start bit
                                escreve('S');//pra indicar que recebeu um bit de inicio ou fim de pacote
                              }
                      if(valor_pulso < (bit_1 + range_bit))
                      {
                          if(valor_pulso > (bit_0 + range_bit))
                              {//bit 1
                                escreve('1');
                              }
                              else
                              {//bit0
                                 escreve('0');
                              }

                      }
                      }
                      }
                    largura_pulso=0;
                        timeout = 3000;//prepara contagem
                     }//fim do leu pulsos
     if(timeout>0)
     {
     timeout--;
     }
     if(timeout==1)
     {// tempo usado para resetar ou descartar pacotes com defeito
      primeiro_pacote = 0;
     }
    }//loop infinito

}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Depois de montado os códigos vamos a simulação,na simulação o primeiro pacote dá um erro, mas logo em seguida tudo funciona normalmente.Veja a imagem da simulação e do esquemático no proteus :


O esquemático:
    
     Testando com um controle remoto e com o prototipo montado numa proto-o-board recebi os seguintes códigos do controle:

Padrão samsung, 33bits + start_stop_bit:
 
Power = 111000001110000001000000101111110
vol+  = 111000001110000011100000000111110
vol-  = 111000001110000011010000001011110
ch+   = 111000001110000001001000101101110
ch-   = 111000001110000000001000111101110

Padrão LG, 32bits + start_stop_bit:

Power = 01000001101111100010000111011110
vol+  = 01000001101111101000000101111110
vol-  = 01000001101111111000000001111110
ch+   = 01000001101111100000000111111110
ch-   = 01000001101111110000000011111110

padrão sony, repete 3 vezes um codigo de 11 bits + start_bit
com intervalos de 27ms 
         
Power = 10101001000
vol+  = 01001001000
vol-  = 11001001000
ch+   = 00001001000
ch-   = 10001001000

padrão philips envia 10 bits sem pulsos de inicio e fim

Power = 0100000101
vol+  = 0100001100
vol-  = 0000001101
ch+   = 0000011000
ch-   = 0100011001

Para pegar os dados eu montei na proto-o-board o esquema elétrico seguinte:



Em breve vou postar um artigo usado este projeto como base para acionar leds com diferentes controles, até a próxima!

Controle remoto com PIC - Parte 1

             Olá, hoje vou demonstrar como montar um controle remoto com o PIC e também como montar um receptor de controle remoto, a ideia inicial era montar apenas o decodificador(receptor do controle remoto) mas para isso eu precisava simular um controle remoto a medida que montava todo o firmware então acabei montando também o simulador de controle remoto(codificador), mas eu também queria que meu circuito reconhecesse pelo menos uns 4 tipos de marcas para que vocês pudessem testar em casa sem grandes dificuldades, então peguei 4 marcas mais vistas no mercado: LG, PHILIPS, SAMSUNG e SONY.Então comecei a ver a forma de ondas no osciloscópio e levantar informações(o que não é tão simples de encontrar), dai encontrei sobre a Sony e a Lg(protocolo NEC) embora o padrão da Samsung é o mesmo mas com diferença no tamanho do pulsos, o padrão Philips deu um pouco mais de trabalho pois não possuem um bit de "start".
               A característica do padrão NEC é a presença de um bit de inicio(start bit) e outro de fim(stop bit) e mais 1 ou 2 bits após o "stop bit", sendo que antes do "stop bit" existe um espaço de 46ms(padrão samsung) ou 44ms(LG), ao todo são 33bits, no padrão Sony existe um "start bit" e em seguida 12 bits(mas no circuito montado foi lido somente 11 mas deve ser devido ao fato de eu estar ignorando os espaços entre os bits mas isso não atrapalha o reconhecimento) que é enviado 3 vezes em intervalos de 27ms . Já o padrão Philips não tem "stop bit" e nem "start bit" ao todo dá 10bits. Veja como é a onda gerada pelo controle remoto do PIC:
               veja que não coloquei  a onda inteira do padrão NEC (LG e Samsung) para facilitar a visualização mas abaixo coloquei a onda completa do padrão samsung para dar uma idéia, a diferença entre Samsung e LG é o tamanho do "start bit":

           Os bits "0" e "1" se diferenciam pelo tempo em que dura o nível alto e o nível baixo, se tempo iguais vale 0 se os tempos forem diferentes nível alto,mas para facilitar a decodificação eu peguei o valor do período do sinal ou seja na borda de subida do sinal, o valor desses períodos não são padrões cada fabricante escolhe o seu, mas o da Samsung e LG são iguais os bits "0" e "1"(talvez por usar o mesmo protocolo e com diferença apenas no "start bit").
         Vamos agora ao código fonte do codificador de controle remoto, vale lembrar que colocando um led infravermelho ele deve acionar uma tv, bastando para isso fazer pequenas alterações neste código fonte(não testei na TV ainda pois gerei ele só pra testar o decodificador):

O código fonte:

/*
 *                               Emulando um controle remoto de TV no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F628A
 * Autor: aguivone
 * Versão: 1
 * Data :  16 de março 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>
#include <delays.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#define LED_STATUS RB0
#define LED_STATUS RB0


#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)

long tempo_us1=0;

void interrupt interrupcao(void)//vetor de interrupção
 {
    if(T0IF)
    {//interrupção do timer0 - estoura quando atinge 255(0XFF)- estoura a cada 62.5us
        if(tempo_us1>0)
        {
            tempo_us1--;
        }
        TMR0 = 254;//reinicia timer com o valor calculado
        T0IF = 0;
        //62.5us + os delay da rotinas  = 75us
    }
 }

void inicializa_timer0()
{
    OPTION_REG = 0X03;//timer0 com prescaler dividido por 16
    T0IE = 1;//habilita interrupção do timer0
    TMR0 = 254;//zera registrador de contagem
}

void envia_bit(char protocolo,int val)
{
    switch(protocolo)
    {
        case 'G'://Samsung
        {
             if(val == 0)
            {//bit 0
             LED_STATUS = 1;
             __delay_us(560);
             LED_STATUS = 0;
             __delay_us(560);
            }
            else
            {//bit 1
             LED_STATUS = 1;
             __delay_us(560);
             LED_STATUS = 0;
             __delay_ms(1);
             __delay_us(680);
            }

        }break;
        case 'L'://LG 
        {
             if(val == 0)
            {//bit 0
             LED_STATUS = 1;
             __delay_us(560);
             LED_STATUS = 0;
             __delay_us(560);
            }
            else
            {//bit 1
             LED_STATUS = 1;
             __delay_us(560);
             LED_STATUS = 0;
             __delay_ms(1);
             __delay_us(680);
            }

        }break;
        case 'S'://sony
        {
                        if(val == 0)
            {//bit 0
             LED_STATUS = 1;
             __delay_us(600);
             LED_STATUS = 0;
             __delay_us(600);
            }
            else
            {//bit 1
             LED_STATUS = 1;
             __delay_ms(1);
             __delay_us(200);
             LED_STATUS = 0;
             __delay_us(600);
            }
        }break;
        case 'P'://
        {
           if(val == 0)
            {//bit 0
             LED_STATUS = 1;
             __delay_us(890);
             LED_STATUS = 0;
             __delay_us(890);
            }
            else
            {//bit 1
             LED_STATUS = 1;
             __delay_ms(1);
             __delay_us(800);
             LED_STATUS = 0;
             __delay_us(890);
            }

        }break;
    }
   
}

void envia_start_bit(char proto)
{
    switch(proto)
    {
    case 'L'://LG
    {
        LED_STATUS = 1;
        __delay_ms(9);
        LED_STATUS = 0;
        __delay_ms(4);
        __delay_us(500);
    }break;
   case 'G'://samsung
    {
        LED_STATUS = 1;
        __delay_ms(4);
        __delay_us(500);
        LED_STATUS = 0;
        __delay_ms(4);
        __delay_us(500);
    }break;
    case 'S'://sony
    {
        LED_STATUS = 1;
        __delay_ms(2);
        __delay_us(400);
        LED_STATUS = 0;
        __delay_us(600);
    }break;
    case 'P'://philips
    {
        LED_STATUS = 1;
        __delay_ms(1);
        __delay_us(800);
        LED_STATUS = 0;
        __delay_ms(1);
        __delay_us(800);
    }break;
}
}
void simula_pacote(char protocolo)
{
    switch(protocolo)
    {
        case 'L'://LG
        {
           envia_start_bit(protocolo);
           int temp = 0;
           while(temp<=10)//envia 10 bits 0
           {
              envia_bit(protocolo,0);
              temp++;
           }
           while(temp<=33)//envia 23 bit 1
           {
              envia_bit( protocolo,1);
              temp++;
           }
           __delay_ms(44);
           envia_start_bit(protocolo);
           envia_bit(protocolo,0);
           envia_bit(protocolo,1);
        }break;
       case 'G'://samsung
        {
           envia_start_bit(protocolo);
           int temp = 0;
           while(temp<=10)//envia 10 bits 0
           {
              envia_bit(protocolo,0);
              temp++;
           }
           while(temp<=33)//envia 23 bit 1
           {
              envia_bit( protocolo,1);
              temp++;
           }
           __delay_ms(46);
           envia_start_bit(protocolo);
           envia_bit(protocolo,0);
           envia_bit(protocolo,0);
        }break;
       case 'S'://sony
        {
           int envio = 0;
           while(envio<3)
           {
           envia_start_bit(protocolo);
           int temp = 0;
           while(temp<=6)//envia 6 bits 0
           {
              envia_bit(protocolo,0);
              temp++;
           }
           while(temp<=13)//envia 6 bit 1
           {
              envia_bit( protocolo,1);
              temp++;
           }
           __delay_ms(27);
           envio++;
           }
        }break;
        case 'P'://philips
        {
           int temp = 0;
           while(temp<3)//envia 2 bits 0
           {
              envia_bit(protocolo,0);
              temp++;
           }
           while(temp<7)//envia 4 bit 1
           {
              envia_bit( protocolo,1);
              temp++;
           }
           envia_start_bit(protocolo);
           while(temp<11)//envia 3 bit 1
           {
              envia_bit( protocolo,1);
              temp++;
           }
        }break;
    }
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void) {
    TRISB = 0;//configura portB  como saida
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
   // inicializa_timer0();
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    for(;;)
    {
         simula_pacote('L');//simula um sinal de controle remoto LG
         __delay_ms(2000);//minimo de 100ms entre cada pacote mas para os teste vou colocar 5s
         simula_pacote('G');//simula um sinal de controle remoto Samsung
         __delay_ms(2000);//minimo de 100ms entre cada pacote mas para os teste vou colocar 5s
         simula_pacote('S');//simula um sinal de controle remoto Sony
         __delay_ms(2000);//minimo de 100ms entre cada pacote mas para os teste vou colocar 5s
         simula_pacote('P');//simula um sinal de controle remoto Philips
         __delay_ms(2000);//minimo de 100ms entre cada pacote mas para os teste vou colocar 5s

    }//loop infinito

}


Na segunda parte vou mostrar o código do receptor e mais algumas informações.