Fork me on GitHub

05/07/13

PIC: Controle Remoto IR - Protocolo NEC

PIC: Controle Remoto IR - Protocolo NEC




Esse é um decodificador de controle remoto infravermelho, para o protocolo NEC.


A decodificação do sinal funciona da seguinte maneira:
O sinal é conectado ao pino de interrupção externa. Quando ocorre o primeiro pulso do sinal, uma interrupção é gerada. Após isso, o código entra em loop e a cada novo pulso, é calculado o tempo desse pulso ( através da contagem da variável 'timer' ) e é salva na variável 'timerBuffer'. Após o fim do sinal, o código sai do loop. Depois, vem a decodificação. Ele faz a comparação do tempo de cada pulso (salvo na variavel 'timerBuffer' ), com os tempos padronizado do protocolo NEC.

No código, o loop dentro da rotina de interrupção ocorre a cada +-55us (por causa do delay). Então o valor de tempo da variável 'timerbuffer' é igual ao valor verdadeiro do tempo dividido por 55.








O sensor utilizado é um módulo receptor infravermelho, pode ser o IRM2638.


DOWNLOAD:
Firmware: NEC_ir_pic18f4550.hex
Projeto: NEC_Ir_pic18f4550.rar

O código abaixo está configurado para utilizar o PIC18F2550/4550 com clock de 16Mhz. Para utilizar em outros PICs, verifique o datasheet e substitua os registros de interrupção( INT0IF_Bit e INT0IE_Bit ) e veja qual é o PINO de entrada da interrupção.

CÓDIGO
// LCD module connections
sbit LCD_RS at RD0_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISD0_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
// End LCD module connections

bit decoded_flag;

#define DEBUG
#define PINO RB0_Bit

//Estados
#define _IDLE 0
#define _MARK 1
#define _SPACE 2
#define _STOP 3

// (tempo / 55.5)
//NEC Protocol
#define NEC_HDR_MARK 162 //9000us
#define NEC_HDR_SPACE 81 //4500us
#define NEC_BIT_MARK 10 //560us
#define NEC_ONE_SPACE 29 //1600us
#define NEC_ZERO_SPACE 10 //560us
#define NEC_RPT_SPACE 41 //2250us
#define NEC_TIME_EXCESS 4

struct irParam
{
char length;
char timer;
char state;
char timerBuffer[75];
} IrRecv = {0, 0, 0};

unsigned long value;

void interrupt()
{
volatile char pinValue = 0;

if(INT0IF_Bit)
{
while(1)
{
pinValue = PINO; //PINO DE INTERRUPÇÃO INT0
IrRecv.timer++;
if(IrRecv.length > 99)
{
IrRecv.state = _STOP;
}
if(IrRecv.state == _IDLE)//Estado inicial
{
if(pinValue == 1) //Mark
{
IrRecv.length = 0;
IrRecv.timerBuffer[IrRecv.length++] = IrRecv.timer;
IrRecv.timer = 0;
IrRecv.state = _MARK;
}
}
else if(IrRecv.state == _MARK)//Estado em nivel logico alto
{
if(pinValue == 0)//Space
{
IrRecv.timerBuffer[IrRecv.length++] = IrRecv.timer;
IrRecv.timer = 0;
IrRecv.state = _SPACE;
}
}
else if(IrRecv.state == _SPACE)//Estado em nivel logico baixo
{
if(pinValue == 1)//Mark
{
IrRecv.timerBuffer[IrRecv.length++] = IrRecv.timer;
IrRecv.timer = 0;
IrRecv.state = _MARK;
}
else
{
if(IrRecv.timer > 100)
IrRecv.state = _STOP;
}
}
else if(IrRecv.state == _STOP)//Fim do sinal
{
decoded_flag = 1;
IrRecv.timer = 0;
break;
}
delay_us(49);
}
INT0IF_Bit = 0;//reseta a flag de interrupção
}
}

//prepara a proxima decodificacao
void Resume()
{
IrRecv.state = _IDLE;
IrRecv.length = 0;
}

char DecodeNEC()
{
char offset = 1;//o primeiro pulso é ignorado
char i=0;

if(!decoded_flag) return 0;

//compara com o tempo de SPACE do sinal de start
if((IrRecv.timerBuffer[offset] < (NEC_HDR_SPACE - NEC_TIME_EXCESS)) ||
(IrRecv.timerBuffer[offset] > (NEC_HDR_SPACE + NEC_TIME_EXCESS)))
{
#ifdef DEBUG
lcd_out(2, 1, "start erro");
#endif
decoded_flag = 0;
Resume();
return 0;
}

//Tamnho do buffer é 2 * 32(nBits) + 4
if(IrRecv.length < 68)
{
decoded_flag = 0;
Resume();
return 0;
}

offset++;

//Recupera o valor transmitido pelo sinal
for(i=0;i < 32;i++)
{
if((IrRecv.timerBuffer[offset] < (NEC_BIT_MARK - NEC_TIME_EXCESS)) ||
(IrRecv.timerBuffer[offset] > (NEC_BIT_MARK + NEC_TIME_EXCESS)))
{
#ifdef DEBUG
lcd_out(2,1," bit error ");
#endif
decoded_flag = 0;
Resume();
return 0;
}

offset++;

if((IrRecv.timerBuffer[offset] > (NEC_ONE_SPACE - NEC_TIME_EXCESS)) &&
(IrRecv.timerBuffer[offset] < (NEC_ONE_SPACE + NEC_TIME_EXCESS)))
{
value <<= 1;
value |= 1;
#ifdef DEBUG
lcd_out(2,1,"data ok ");
#endif
}
else if((IrRecv.timerBuffer[offset] > (NEC_ZERO_SPACE - NEC_TIME_EXCESS)) &&
(IrRecv.timerBuffer[offset] < (NEC_ZERO_SPACE + NEC_TIME_EXCESS)))
{
value <<= 1;
#ifdef DEBUG
lcd_out(2,1,"data ok ");
#endif
}
else
{
#ifdef DEBUG
lcd_out(2,1,"data error");
#endif
decoded_flag = 0;
Resume();
return 0;
}

offset++;
}

#ifdef DEBUG
lcd_out(2,1," value ok ");
#endif
decoded_flag = 0;
Resume();
return 1;
}

void main()
{
char msg[16];

TRISB.F0 = 1;
TRISB.F6 = 0;
TRISB.F7 = 0;
GIE_Bit = 1;//habilita interrupção global
//PEIE_Bit = 1;//habilita interrupção dos perifericos
ADCON1 = 0x0F; //desativa a entrada analogica do pino RB0
//Pode-sedesativar a entrada analogica do pino RB0, configurando os fusíveis

lcd_init();
lcd_cmd(_LCD_CURSOR_OFF);
lcd_out(1,1,"VALOR:");

delay_ms(100);
INT0IE_Bit = 1;//habilita interrupção externa

while(1)
{
if(DecodeNEC())
{
longwordtohex(value, msg);//converte o valor para hexadecimal
lcd_out(1, 8, msg);
if(value == 0x807608f7) PORTB.F6 = ~PORTB.F6;
if(value == 0x80768877) PORTB.F7 = ~PORTB.F7;
}
}
}

10 comentários:

  1. Boa Tarde companheiro. Estou tendo seguinte problema, tanto com o MPLab quanto para o CCS:

    Error 128 "arquivo.c" Line(1,1): A #device required before this line

    Teriam como me ajudar?

    Abraços

    Yuri

    ResponderExcluir
  2. Como adaptar esse circuito para o pic16f628a

    ResponderExcluir
  3. Por gentileza teria o msm código para CCS compiler?

    ResponderExcluir
  4. OLA
    Bom trabalho ! eu usei o hexa que compilado por você e funcionou de primeira
    Porem tentei montar um projeto e compilar no Mikroc e deu dois erros na linha 2
    " ; expected but LCD_RS found e internal error " por que será não manjo bem do mikroc
    talvez ele considerou sbit como sendo final da linha ????
    abs

    ResponderExcluir
  5. consegui montar todo o código no MikroC e configurar para o MCU PIC16F628A para testar.
    porém não decodifica, provávelmente porque estou usando uma frequencia de 4MHz.
    ***Teria como usar assim? se eu mudar a divisão do tempo de 55,5us seria o suficiente?
    pq tbm não sei calcular qual seria a divisão correta a colocar nesse caso.
    Vc poderia dizer como vc chegou no 55,5us de divisão nesse projeto?
    Vlw obrigado.

    ResponderExcluir
    Respostas
    1. Ola teria como passar o codigo que foi adaptado para 16f628a

      Excluir
    2. Poderia por gentileza passar o código adaptado para MikroC

      Excluir

Postagens Relacionadas!!