Solución de problemas de emisores de infrarrojos con un Arduino

He estado tratando de hacer que un Arduino replique la señal remota de mi aire acondicionado durante bastante tiempo y casi me doy por vencido solo por lo molesta que es esta tarea. Lo primero es lo primero:

  • El Arduino que estoy usando es el Mega 2560 que usa el chip Mega2560 AVR , y el receptor IR es el VS1838b (nivel lógico bajo).

  • La frecuencia de la portadora IR es de 38 KHz (13 ciclos de temporizador de EE. UU.), y la velocidad máxima a la que he podido obtener lecturas confiables del Arduino es de alrededor de 250 KHz (4 ciclos de temporizador de EE. UU.). Las señales IR se decodifican a través de la longitud de una señal corrupta por una señal portadora de 38 KHz, pero el VS1838b devuelve solo la longitud de la señal (sin la porción de 38 KHz).

  • El Mega tiene 8192 bytes de memoria, lo que significa que necesito ser muy eficiente. Las impresiones en serie están fuera de discusión, ya que agregan un retraso significativo al programa que afectaría la lectura. He estado usando una matriz de bytes, almacenando la información secuencialmente de izquierda a derecha, pero esto significa que puedo tener un máximo de 64 000 muestras.

  • El LED IR que estoy usando es completamente funcional, y la velocidad de lectura/escritura de Arduino es suficiente, ya que pude abrir un control remoto de CA, soldar un cable a uno de los terminales IR del control remoto, conectarlo a un Arduino y retransmitir ese resultado a mi LED IR.

  • Así es como se configura el temporizador:

void setup()
{
//  Serial.begin (115200);
  pinMode(8, INPUT);
  pinMode(4, OUTPUT);

  TCCR1A = B00000000;
  TCCR1B = B00001001; // CTC mode with 1:1 prescaler
  TIMSK1 = B00000010; // Enables Timer 1 A compare interrupt
  OCR1A = 0x003E; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}

Pensé que sería una implementación fácil. Todo lo que tenía que hacer era copiar la señal exactamente como se leyó (directamente desde el control remoto de CA), y funcionaría bien (aparte de ser ineficiente en el almacenamiento). Copié la señal en 4 ciclos de uS, así:

ISR (TIMER1_COMPA_vect)
{
  temp = temp << 1;

  if (PINH == 32)
    temp++;

  j++;
  if (j == 8)
  {
    store[k] = temp;
    k++;
    j = 0;
    temp = 0;
  }

  if (k == len)
  {
    k = 0;
    j = 0;
    flag = 1;
    StopTimer();
  }
}

Donde PINH == 32 es el pin de recepción para el control remoto de CA (32 porque estoy leyendo directamente desde el registro). Luego, usando la misma interrupción del temporizador, escribí la señal cuando terminó de leer:

ISR (TIMER1_COMPA_vect)
{
  if (k == len)
  {
    StopTimer();
    CLR(5);
    k = 0;
    flag = 1;
  }
  
  if (bitRead(store[k], j) == 1)
  {
    SET(5);
  }
  else
  {
    CLR(5);
  }

  j--;

  if (j == -1)
  {
    k++;
    j = 7;
  }
}

Pero esto no funcionó y no tenía idea de por qué. Luego traté de usar el VS1838b y lo combiné con una frecuencia PWM fija de 38 KHz a través del temporizador, y activé/desactivé el temporizador según las lecturas del sensor.

  • Resumen: Arduino lee VS1838b constantemente y habilita/deshabilita el temporizador según el resultado. No funcionó.

Pensé que tal vez el programa estaba afectando la frecuencia PWM de alguna manera, así que usé un Arduino diferente para escribir la frecuencia PWM y otro para leer el VS1838b. Luego conecté la escritura PWM fija de 38 KHz a la base de un transistor TIP41C, el pin de control Arduino al colector.

  • Resumen: dos Arduinos, uno envía una frecuencia PWM de 38 KHz a la base de un transistor y el otro envía el resultado NOT del VS1838b al colector. No funcionó.

No sé cuál podría ser la causa, verifiqué dos veces la hoja de datos del temporizador, incluso calibré manualmente el temporizador usando las funciones micros (), pero nada parece funcionar. ¿Hay algo que estoy haciendo fundamentalmente mal?

editar: simplifiqué el circuito y subí fotos de cómo se ve. Básicamente, D10 es una entrada para el sensor VS1838b, el temporizador usa D6 para escribir una frecuencia PWM de 38 KHz y D3 se usa para generar el resultado NOT del sensor. El código se ve así:

#define CLR(x) (PORTD&=(~(1<<x)))
#define SET(x) (PORTD|=(1<<x))
#define TOGGLE(x) (PORTD^=(1<<x))

void setup()
{
  Serial.begin(2000000);
  pinMode(10, INPUT);
  pinMode(3, OUTPUT);
  pinMode(6, OUTPUT);
  TCCR1A = B00000000;
  TCCR1B = B00001001;
  TIMSK1 = B00000010;
  OCR1A = 0x00CF; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}

void loop()
{

  if ((PINB && B00000100) == 0)
  {
    SET(3);
  }
  else
  {
    CLR(3);
  }

}

ISR (TIMER1_COMPA_vect)
{
  TOGGLE(6);
}

Y sigue sin funcionar.

"el pin de control de Arduino al colector". ¿Por qué? Mostrar esquema del controlador
Rb = 1k desde la salida con Carrier Y datos a la base, si usa 5V, LED IR = 1.3V @ 100mA necesita la serie Rc = 3.5V/0.1 = 35 Ohms
@TonyStewartSunnyskyguyEE75 Bueno, resulta que pensé que el TIP41C era un MOSFET de tipo N y estaba usando resistencias desplegables. Utilizará un IRFZ44n y actualizará los resultados. Sin embargo, los otros intentos sin usar un transistor deberían haber funcionado.
Aún así, un esquema de cómo conecta las cosas parece muy necesario para nuestra comprensión de su sistema. ¡No asuma que solo hay una manera posible de hacerlo!
@mmmm actualizó la publicación
Un mega2560 usa un mega2560 AVR, no un mega328. IR se ha hecho hasta la muerte con Arduino, entonces, ¿por qué reinventar la rueda? Para generar la portadora de 38 kHz, use el modo ctc en el temporizador. No se requieren interrupciones. Use un temporizador y un código de 16 bits para generar la secuencia que desee activando y desactivando el temporizador de la portadora.
@Kartman Pero eso es lo que hice, pero con interrupciones. ¿No se requiere la interrupción para activar y desactivar el pin? A menos que te refieras a algo como usar el pin digital vinculado a la coincidencia de comparación del temporizador.

Respuestas (2)

IR es algo así como la radio AM, un poco. Tienes una señal y un portador, pero una cosa de onda cuadrada. Tú y la señal y el portador juntos. Para transmitir necesitas hacer esto al revés. Aunque me las arreglé para romper esto con un PIC en un punto, asuma que no puede y no se moleste. Básicamente, necesita generar un portador (38Khz) y junto con su señal, pulsos de ms o fracciones de. Suponga que esto no siempre es posible dentro de una MCU, debe evaluar cada mcu para ver si puede hacerlo de alguna manera.

Para algunos, usa el temporizador para generar una señal (no está usando esto para interrumpir el bit bang, está generando la señal en sí) y luego algunos le permiten activar la salida en función de otro temporizador o algo más. Algunos STM32 tienen específicamente un temporizador IR que es esencialmente una puerta entre dos temporizadores.

Puede lograrlo con un PWM, donde genera un ciclo de trabajo del 50% a veces y un ciclo de trabajo del 0% a veces, cronometrando cada uno.

Algunos pueden obtener esto con un controlador SPI usando el mosi enviando una cantidad de 0x55 y otros valores para generar la cantidad correcta de ciclos de reloj y luego la cantidad correcta de ceros para generar los espacios. Esta es probablemente la forma más fácil de hacerlo, si la MCU tiene un fifo lo suficientemente profundo o una forma de dma y usted tiene suficientes ciclos o memoria para mantener alimentado el fifo/dma.

O simplemente podría poner una puerta discreta y fuera de la mcu...

Luego, como cualquier otro LED, alimenta esta señal de salida al LED IR con una resistencia para limitar/controlar el voltaje a través del LED.

Si no tiene un alcance, encontrará esto extremadamente difícil. Es posible que deba reducir la velocidad ajustando sus divisores y luego alimentarlo a otra placa mcu o raspberry pi o fpga o algo que se use como un analizador lógico casero para examinar el tiempo. Los buenos osciloscopios son muy económicos en estos días, por lo que vale la pena obtener uno de cuatro canales si planea continuar trabajando con mcu (esto cubrirá uart, spi (dos líneas de datos), i2c y una serie de otras cosas) (aprenda a leer los protocolos, no use el software que intenta leerlos por usted, fallan tanto como tienen éxito y empeoran la vida, estos protocolos son fáciles de leer a partir de las señales sin procesar).

sbprojects.net/knowledge/ir/index.php este es mi sitio de referencia cuando estoy haciendo trabajo de IR. Lo uso para averiguar qué protocolos IR estoy mirando con un control remoto en particular... Un osciloscopio ayuda a recibir, pero no es necesario que los haya decodificado durante muchos años sin un osciloscopio. Pero para transmitirlo hace una gran diferencia.
Las interrupciones complican la vida, solo úsalas cuando sea necesario. Yo hasta ahora no he necesitado. Hice un repetidor quizás similar al tuyo, tenía un televisor antiguo (uno de los primeros con infrarrojos) que no usaba un protocolo estándar (era anterior a los protocolos estándar). Hice un repetidor decodificador para traducir un código más nuevo a los códigos más antiguos y podía usar un control remoto universal, funcionó muy bien. Ahora que lo pienso, es muy posible que yo también lo haya golpeado un poco, pero tenía mucha más memoria y flash que tú y básicamente hice nops y/o escrituras en E/S en asm. como en cientos. hoy usaria un irtim en un stm32
o un controlador spi en casi cualquier mcu (mire cómo están haciendo algo similar para controlar los leds WS2812B usando spi, convirtiendo spi en un generador de señal temporizado)

No estoy seguro de cuál era el problema, pero mi lógica inicial era correcta.

Para solucionarlo, cambié los microcontroladores del Arduino Nano al nuevo Arduino Nano Every, cambié el LED IR por uno nuevo (aunque el anterior supuestamente funcionaba) y reescribí el código usando el nuevo Arduino (la misma lógica pero con el nuevo temporizador Atmega4809):

// Macros for easier bit manipulation
#define CLR(x) (VPORTB.OUT&=(~(1<<x)))
#define SET(x) (VPORTB.OUT|=(1<<x))
#define TOGGLE(x) (VPORTB.OUT^=(1<<x))

void setup()
{
  pinMode(5, OUTPUT); // IR LED output
  pinMode(9, INPUT); // VS1838b input sensor

  TCA0.SINGLE.PER = 0x00D2;
  TCA0.SINGLE.CMP1 = 0x00D2;
  TCA0.SINGLE.INTCTRL = B00100000;
  TCA0.SINGLE.CTRLA = B00000001;
}

void loop()
{
}

ISR(TCA0_CMP1_vect) {
    TCA0.SINGLE.INTFLAGS |= B00100000;
    if ((VPORTB.IN & B00000001) == 0)
    {
      TOGGLE(2);
    }
    else
    {
      CLR(2);
    }
}

Esto ahora funciona. ¡Gracias a todos por su ayuda!