sexta-feira, setembro 13, 2013

Controle Remoto Infravermelho Parte 2

Como primeiro exercício prático, vamos transmitir "na raça" um comando usando um Arduino UNO e um LED IR TIL32 comum. Como "cobaia" vou usar um tocador de MP3.


O tocador de MP3 testado utiliza um protocolo muito comum, que costuma ser chamado de "NEC". Os detalhes estão aqui, vejamos um resumo:
  • Quando o LED IR for acionado, a frequência deve ser 38KHz e o duty de 1/3.
  • No começo de cada código acionamos o LED por 9 ms e o apagamos por 4,5ms (isto serve para ajustar o controle de ganho do receptor).
  • Em seguida transmitimos um endereço de 8 bits duas vezes, invertendo os bits na segunda vez.
  • Depois, enviamos o comando de 8 bits também duas vezes, invertendo os bits na segunda vez.
  • Para fechar o código, acionamos o LED por 560µs e o desligamos.
  • Para transmitir um "1" acionamos o LED por 560µs e o desligamos por 1690µs.
  • Para transmitir um "0" acionamos o LED por 560µs e o desligamos por 560µs (ou seja estamos codificando por "distância de pulso").
O tocador de MP3 usa o endereço 0x00, o comando que vamos usar é o mute, que tem o código 0xE2. Portanto os bits a enviar são 0x00FFE21D.

O hardware é bastante simples, um LED IR TIL32  com o catodo ligado ao pino GND do Arduino UNO e o anodo ligado através de um resistor de 220 ohms ao pino 3 do Arduino UNO.

Nosso primeiro desafio é gerar o sinal de 38KHz com duty de 1/3. Para isto vamos programar o Timer2, que é quem gera o PWM para o pino 3 no ATmega328 (isto muda para outros modelos do ATmega, como o usado no Arduino Mega).
  1. const int PINO_LED = 3;  
  2.   
  3. // Prepara o PWM para gerar um sinal de 38KHz com duty cycle de 1/3  
  4. static void preparaPWM () {  
  5.   uint8_t pwmval = F_CPU / 2000 / 38;  // contagem para gerar 38KHz  
  6.     
  7.   pinMode (PINO_LED, OUTPUT);  
  8.   digitalWrite (PINO_LED, LOW);     // não ativar o IR  
  9.   TCCR2A = _BV(WGM20);              // PWM, não conectado ao pino  
  10.   TCCR2B = _BV(WGM22) | _BV(CS20);  // sem prescalling, phase correct  
  11.   OCR2A = pwmval;                   // periodo  
  12.   OCR2B = pwmval / 3;               // tempo com sinal alto  
  13. }  
Feito isto, ligar e desligar o LED é feito conectando ou desconectando o PWM ao pino:
  1. // Transmite por t microsegundos  
  2. static void txIR (int t) {  
  3.   TCCR2A |= _BV(COM2B1);    // conecta PWM ao pino  
  4.   delayMicroseconds(t);  
  5. }  
  6.   
  7. // Silencio por t microsegundos  
  8. static void silencioIR (int t) {  
  9.   TCCR2A &= ~(_BV(COM2B1));    // desconecta PWM ao pino  
  10.   delayMicroseconds(t);  
  11. }  
Feita esta infraestrutura, a rotina para enviar um comando fica simples:
  1. static void enviaCmd (unsigned long dados) {  
  2.   // transmissão inicial para ajuste do AGC  
  3.   txIR (9000);  
  4.   silencioIR (4500);  
  5.   // Envia os 32 bits de dados (endereço e comando)  
  6.   for (int i = 0; i < 32; i++) {  
  7.     if (dados & 0x80000000) {  
  8.       txIR (560);  
  9.       silencioIR (1690);  
  10.     }   
  11.     else {  
  12.       txIR (560);  
  13.       silencioIR (560);  
  14.     }  
  15.     dados <<= 1;  
  16.   }  
  17.   // Fecha o código  
  18.   txIR (560);  
  19.   silencioIR (0);  // para desligar o IR  
  20. }  
Para completar, um programa principal de teste que envia o comando mute a cada 10 segundos:
  1. void setup () {  
  2.   preparaPWM ();  
  3. }  
  4.   
  5. void loop () {  
  6.   enviaCmd (0x00FFE21D);  
  7.   delay (10000);  
  8. }  
Você deve estar se perguntando como eu descobri que o controle remoto do tocador usa o padrão NEC e qual era o código do mute. Eu simplesmente usei uma biblioteca pronta (aqui) que veremos com mais detalhes no próximo post. A maior parte do código apresentado é uma adaptação do que a biblioteca faz internamente.

Nenhum comentário: