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).
const int PINO_LED = 3;

// Prepara o PWM para gerar um sinal de 38KHz com duty cycle de 1/3
static void preparaPWM () {
  uint8_t pwmval = F_CPU / 2000 / 38;  // contagem para gerar 38KHz
  
  pinMode (PINO_LED, OUTPUT);
  digitalWrite (PINO_LED, LOW);     // não ativar o IR
  TCCR2A = _BV(WGM20);              // PWM, não conectado ao pino
  TCCR2B = _BV(WGM22) | _BV(CS20);  // sem prescalling, phase correct
  OCR2A = pwmval;                   // periodo
  OCR2B = pwmval / 3;               // tempo com sinal alto
}
Feito isto, ligar e desligar o LED é feito conectando ou desconectando o PWM ao pino:
// Transmite por t microsegundos
static void txIR (int t) {
  TCCR2A |= _BV(COM2B1);    // conecta PWM ao pino
  delayMicroseconds(t);
}

// Silencio por t microsegundos
static void silencioIR (int t) {
  TCCR2A &= ~(_BV(COM2B1));    // desconecta PWM ao pino
  delayMicroseconds(t);
}
Feita esta infraestrutura, a rotina para enviar um comando fica simples:
static void enviaCmd (unsigned long dados) {
  // transmissão inicial para ajuste do AGC
  txIR (9000);
  silencioIR (4500);
  // Envia os 32 bits de dados (endereço e comando)
  for (int i = 0; i < 32; i++) {
    if (dados & 0x80000000) {
      txIR (560);
      silencioIR (1690);
    } 
    else {
      txIR (560);
      silencioIR (560);
    }
    dados <<= 1;
  }
  // Fecha o código
  txIR (560);
  silencioIR (0);  // para desligar o IR
}
Para completar, um programa principal de teste que envia o comando mute a cada 10 segundos:
void setup () {
  preparaPWM ();
}

void loop () {
  enviaCmd (0x00FFE21D);
  delay (10000);
}
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: