Control con mando IR con el TSOP4838

Escribe aqui todos lo relacionado con estos temas

Moderadores: Moderadores, Junta Directiva

Control con mando IR con el TSOP4838

Notapor isotopo el Mar Dic 29, 2009 11:28 pm

Quiero utilizar el TSOP4838 para mandar órdenes con un mando a distancia de TV, que por si alguien no lo sabe es un receptor de infrarrojos de bajo coste que lleva un amplificador y un oscilador para convertir las señales del mando en pulsos digitales en forma de códigos que identifican cada botón, utilizando el protocolo NEC. La verdad es que por el precio que tiene y con un único pin es una maravilla poder contar con tantos botones, y sin cables. Muy recomendable para todo tipo de proyectos, seguro que en casa todo el mundo tiene más de uno que sirva porque parece ser que está muy extendido ese protocolo, de hecho el primer mando que pillé sirvió.

Lo he probado con arduino y funciona perfectamente, gracias a la librería NECIRcv. El problema me surge cuando he intentado controlarlo con el BasicX24, utilizando el mismo receptor y su circuito, simplemente cambiando la conexión de uno a otro.

Bien, resulta que ejecuto, y pulso los botones del mando, y no consigo detectar nada, he probado con las funciones GetPin, InputCapture, WaitForInterrupt, RCTime, he probado y reprobado y no hay señales de vida de ninguna clase. Necesito un punto de partida, un algo, porque es que ya me estoy agobiando de dar palos de ciego. A lo mejor he hecho algo mal, o me falta algo, no sé...

Voy a dejar aquí pegado el código para el Arduino porque el enlace donde estaba la librería ya no funciona y no lo he vuelto a encontrar, por si sirve para que me ayudéis, o por si le interesa alguien.

NECIRrcv.cpp
Código: Seleccionar todo

// NECIRrcv
// Joe Knapp   jmknapp AT gmail DOT com

#include "WProgram.h"
#include "NECIRrcv.h"

NECIRrcv::NECIRrcv(int irpin)
{
  irparams.irpin = irpin ;
}

// initialization
void NECIRrcv::begin() {
  // setup pulse clock timer interrupt
  TCCR2A = 0;  // normal mode

  //Prescale /8 (16M/8 = 0.5 microseconds per tick)
  // Therefore, the timer interval can range from 0.5 to 128 microseconds
  // depending on the reset value (255 to 0)
  cbi(TCCR2B,CS22) ;
  sbi(TCCR2B,CS21) ;
  cbi(TCCR2B,CS20) ;

  //Timer2 Overflow Interrupt Enable
  sbi(TIMSK2,TOIE2) ;

  RESET_TIMER2;

  sei();  // enable interrupts
 
  // initialize state machine variables
  irparams.rcvstate = IDLE ;
  irparams.bitcounter = 0 ;
  irparams.ircode = 0 ;
  irparams.fptr = 0 ;
  irparams.rptr = 0 ;
  irparams.blinkflag = 0 ;

  // set pin modes
  pinMode(irparams.irpin, INPUT) ;
}

// return next IR code from buffer, or -1 if none
unsigned long NECIRrcv::read()
{
  unsigned long ircode ;
  if (irparams.fptr != irparams.rptr) {
    ircode = irparams.irbuf[irparams.rptr] ;
    irparams.rptr = (irparams.rptr + 1) % MAXBUF ;
    return((unsigned long)ircode) ;
  }
  else
    return((unsigned long)-1) ;
}

// return number of IR codes in buffer
int NECIRrcv::available()
{
  int n ;
  n = irparams.fptr - irparams.rptr ;
  if (n < 0)
    n += MAXBUF ;
  return(n) ;
}

// enable/disable blinking of pin 13 on IR processing
void NECIRrcv::blink13(int blinkflag)
{
  irparams.blinkflag = blinkflag ;
  if (blinkflag)
    pinMode(BLINKLED, OUTPUT) ;
}

// flush IR code buffer
void NECIRrcv::flush()
{
  irparams.rptr = irparams.fptr ;
}

// IR receiver state machine (TIMER2 interrupt)
ISR(TIMER2_OVF_vect)
{
  RESET_TIMER2 ;

  irparams.irdata = GETIR(irparams.irpin) ;
 
  if (irparams.blinkflag && (irparams.rcvstate != IDLE))
    PORTB |= B00100000 ;  // turn pin 13 LED on
 
  // process current state
  switch(irparams.rcvstate) {
    case IDLE:
      if (irparams.irdata == MARK) {  // got some activity
          nextstate(STARTH) ;
          irparams.timer = 0 ;
      }
      break ;
    case STARTH:   // looking for initial start MARK
      // entered on MARK
      if (irparams.irdata == SPACE) {   // MARK ended, check time
        if ((irparams.timer >= STARTMIN) && (irparams.timer <= STARTMAX)) {
          nextstate(STARTL) ;  // time OK, now look for start SPACE
          irparams.timer = 0 ;
        }
        else
          nextstate(IDLE) ;  // bad MARK time, go back to IDLE
      }
      else
        irparams.timer++ ;  // still MARK, increment timer
      break ;
    case STARTL:
      // entered on SPACE
      if (irparams.irdata == MARK) {  // SPACE ended, check time
        if ((irparams.timer >= SPACEMIN) && (irparams.timer <= SPACEMAX)) {
          nextstate(BITMARK) ;  // time OK, check first bit MARK
          irparams.timer = 0 ;
          irparams.bitcounter = 0 ;  // initialize ircode vars
          irparams.irmask = (unsigned long)0x1 ;
          irparams.ircode = 0 ;
        }
        else if ((irparams.timer >= RPTSPACEMIN) && (irparams.timer <= RPTSPACEMAX)) {  // not a start SPACE, maybe this is a repeat signal
          nextstate(RPTMARK) ;   // yep, it's a repeat signal
          irparams.timer = 0 ;
        }
        else
          nextstate(IDLE) ;  // bad start SPACE time, go back to IDLE
      }
      else {   // still SPACE
        irparams.timer++ ;    // increment time
        if (irparams.timer >= SPACEMAX)  // check against max time for SPACE
          nextstate(IDLE) ;  // max time exceeded, go back to IDLE
      }
      break ;
    case RPTMARK:
      irparams.timer++ ;  // measuring MARK
      if (irparams.irdata == SPACE) {  // MARK ended, check time
        if ((irparams.timer >= BITMARKMIN) && (irparams.timer <= BITMARKMAX))
          nextstate(IDLE) ;  // repeats are ignored here, just go back to IDLE
        else
          nextstate(IDLE) ;  // bad repeat MARK time, go back to IDLE
      }
      break ;
    case BITMARK:
      irparams.timer++ ;   // timing MARK
      if (irparams.irdata == SPACE) {   // MARK ended, check time
        if ((irparams.timer < BITMARKMIN) || (irparams.timer > BITMARKMAX))
          nextstate(IDLE) ;  // bad MARK time, go back to idle
        else {
          irparams.rcvstate = BIT ;  // MARK time OK, go to BIT
          irparams.timer = 0 ;
        }
      }
      break ;
    case BIT:
      irparams.timer++ ; // measuring SPACE
      if (irparams.irdata == MARK) {  // bit SPACE ended, check time
        if ((irparams.timer >= ONESPACEMIN) && (irparams.timer <= ONESPACEMAX)) {
          nextstate(ONE) ;   // SPACE matched ONE timing
          irparams.timer = 0 ;
        }
        else if ((irparams.timer >= ZEROSPACEMIN) && (irparams.timer <= ZEROSPACEMAX)) {
          nextstate(ZERO) ;  // SPACE matched ZERO timimg
          irparams.timer = 0 ;
        }
        else
          nextstate(IDLE) ;  // bad SPACE time, go back to IDLE
      }
      else {  // still SPACE, check against max time
        if (irparams.timer > ONESPACEMAX)
          nextstate(IDLE) ;  // SPACE exceeded max time, go back to IDLE
      }
      break ;
    case ONE:
      irparams.ircode |= irparams.irmask ;  // got a ONE, update ircode
      irparams.irmask <<= 1 ;  // set mask to next bit
      irparams.bitcounter++ ;  // update bitcounter
      if (irparams.bitcounter < NBITS)  // if not done, look for next bit
        nextstate(BITMARK) ;
      else
        nextstate(STOP) ;  // done, got NBITS, go to STOP
      break ;
    case ZERO:
      irparams.irmask <<= 1 ;  // got a ZERO, update mask
      irparams.bitcounter++ ;  // update bitcounter
      if (irparams.bitcounter < NBITS)  // if not done, look for next bit
        nextstate(BITMARK) ;
      else
        nextstate(STOP) ;  // done, got NBITS, go to STOP
      break ;
    case STOP:
      irparams.timer++ ;  //measuring MARK
      if (irparams.irdata == SPACE) {  // got a SPACE, check stop MARK time
        if ((irparams.timer >= BITMARKMIN) && (irparams.timer <= BITMARKMAX)) {
          // time OK -- got an IR code
          irparams.irbuf[irparams.fptr] = irparams.ircode ;   // store code at fptr position
          irparams.fptr = (irparams.fptr + 1) % MAXBUF ; // move fptr to next empty slot
        }
        nextstate(IDLE) ;  // finished with this code, go back to IDLE
      }
      break ;
  }
  // end state processing

  if (irparams.blinkflag)
    PORTB &= B11011111 ;  // turn pin 13 LED off
}



NECIRrcv.h
Código: Seleccionar todo
#ifndef NECIRrcv_h
#define NECIRrcv_h

#include "WConstants.h"

#define USECPERTICK 50  // microseconds per clock interrupt tick
#define CLKFUDGE 5      // fudge factor for clock interrupt overhead
#define CLKMAX 256      // max value for clock (timer 2)
#define PRESCALE 8      // timer2 clock prescale
#define SYSCLOCK 16000000  // main Arduino clock
#define CLKSPERUSEC (SYSCLOCK/PRESCALE/1000000)   // timer clocks per microsecond

#define MAXBUF 8       // IR command code buffer length (circular buffer)

// IR detector output is active low
#define MARK  0
#define SPACE 1

#define NBITS 32         // bits in IR code

#define BLINKLED 13

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

// clock timer reset value
#define INIT_TIMER_COUNT2 (CLKMAX - USECPERTICK*CLKSPERUSEC + CLKFUDGE)
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT2

// pulse parameters -- nominal usec
#define STARTNOM      9000
#define SPACENOM      4500
#define BITMARKNOM    620
#define ONESPACENOM   1600
#define ZEROSPACENOM  480
#define RPTSPACENOM   2180

#define TOLERANCE 20  // percent
#define LTOL (1.0 - TOLERANCE/100.)
#define UTOL (1.0 + TOLERANCE/100.)

// pulse parameters (tick counts)
#define STARTMIN (int)((STARTNOM/USECPERTICK)*LTOL) // start MARK
#define STARTMAX (int)((STARTNOM/USECPERTICK)*UTOL)
#define SPACEMIN (int)((SPACENOM/USECPERTICK)*LTOL)
#define SPACEMAX (int)((SPACENOM/USECPERTICK)*UTOL)
#define BITMARKMIN (int)((BITMARKNOM/USECPERTICK)*LTOL-2) // extra tolerance for low counts
#define BITMARKMAX (int)((BITMARKNOM/USECPERTICK)*UTOL+2)
#define ONESPACEMIN (int)((ONESPACENOM/USECPERTICK)*LTOL)
#define ONESPACEMAX (int)((ONESPACENOM/USECPERTICK)*UTOL)
#define ZEROSPACEMIN (int)((ZEROSPACENOM/USECPERTICK)*LTOL-2)
#define ZEROSPACEMAX (int)((ZEROSPACENOM/USECPERTICK)*UTOL+2)
#define RPTSPACEMIN (int)((RPTSPACENOM/USECPERTICK)*LTOL)
#define RPTSPACEMAX (int)((RPTSPACENOM/USECPERTICK)*UTOL)

// receiver states
#define IDLE     1
#define STARTH   2
#define STARTL   3
#define BIT      4
#define ONE      5
#define ZERO     6
#define STOP     7
#define BITMARK  8
#define RPTMARK  9

// macros
#define GETIR(X) ((byte)digitalRead(X))    // used to read IR pin
#define nextstate(X) (irparams.rcvstate = X)

// state machine variables irparams
static volatile struct {
  byte rcvstate ;          // IR receiver state
  byte bitcounter ;        // bit counter
  byte irdata ;            // MARK or SPACE read from IR input pin
  byte fptr ;              // irbuf front pointer
  byte rptr ;              // irbuf rear pointer
  byte irpin ;             // pin for IR data from detector
  byte blinkflag ;         // TRUE to enable blinking of pin 13 on IR processing
  unsigned int timer ;     // state timer
  unsigned long irmask ;   // one-bit mask for constructing IR code
  unsigned long ircode ;   // IR code
  unsigned long irbuf[MAXBUF] ;    // circular buffer for IR codes
} irparams ;

// main class
class NECIRrcv
{
  public:
    NECIRrcv(int irpin);
    unsigned long read();
    void begin();
    int available() ;
    void flush() ;
    void blink13(int blinkflag) ;
  private:
} ;
#endif


Printcodes
Código: Seleccionar todo
// look for IR codes and print them as they are received
#include <WProgram.h>
#include <NECIRrcv.h>
#define IRPIN 8    // pin that IR detector is connected to

#include "WProgram.h"
void setup();
void loop();
NECIRrcv ir(IRPIN) ;

void setup()
{
  Serial.begin(9600) ;
  Serial.println("NEC IR code reception") ;
  ir.begin() ;
}

void loop()
{
  unsigned long ircode ;
 
  while (ir.available()) {
    ircode = ir.read() ;
    Serial.print("got code: 0x") ;
    Serial.println(ircode,HEX) ;
  }

}

int main(void)
{
   init();
   setup();
   for (;;)
      loop();
       
   return 0;
}

Avatar de Usuario
isotopo
Forero Habitual
Forero Habitual
 
Mensajes: 106
Registrado: Jue Ene 04, 2007 3:38 pm
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor Ranganok el Jue Dic 31, 2009 10:26 am

Mira con una sonda lógica o un osciloscópio en el pin a ver si recibes algo.

S2

Ranganok Schahzaman
skiras.blogspot.com

"En igualdad de condiciones la explicación más sencilla es la cierta"
Ranganok
Usuario Experto
Usuario Experto
 
Mensajes: 3382
Registrado: Lun Nov 07, 2005 4:10 pm
Ubicación: Barbaros del Valle
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor isotopo el Jue Dic 31, 2009 1:07 pm

No tengo osciloscopio, soy un desgraciao....

El caso es que al conectarlo al arduino sí que recibe perfectamente los códigos, y como no hay que hacer ninguna llamada ni activar nada ¿no?, pues debería de recibir algo también.

Seguiré investigando a ver porque creo que es tema de programación....
Avatar de Usuario
isotopo
Forero Habitual
Forero Habitual
 
Mensajes: 106
Registrado: Jue Ene 04, 2007 3:38 pm
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor Ranganok el Jue Dic 31, 2009 1:19 pm

Revisa a ver si lo estás conectando bien. Si no es eso, ponlo en un pin de interrupción y mira que la interrupción se esté realizando. Si no es eso, habrá que seguir investigando.

S2

Ranganok Schahzaman

PD: Cuando lo tengas arreglado si haces un pequeño esquema y pones el código lo subimos a la wiki.
skiras.blogspot.com

"En igualdad de condiciones la explicación más sencilla es la cierta"
Ranganok
Usuario Experto
Usuario Experto
 
Mensajes: 3382
Registrado: Lun Nov 07, 2005 4:10 pm
Ubicación: Barbaros del Valle
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor furri el Jue Dic 31, 2009 2:03 pm

Sin utilizar libreria ninguna por el pin de entrada tienes que detectar cambios de valor entre 0 y 1 cuando pulsas el mando a distancia, pon un led que se encienda o apague en función del pin de entrada tan solo para ver si hay recepción, algo así como:

while(1)
{
if (input(pinIR) == 1) { output_high(pinLED); }
else { output_low(pinLED); }
}

Al pulsar el mando el led deberia parpadear algo, si no puede ser un problema hardware que se solucione tan solo cambiando la resistencia o el condensador...

Yo lo utilicé en MiniTarribot con un PIC18F2550 (ver fotos en http://www.tarribot.com/?id=52&idm=3), aunque no usaba ninguna libreria recibia bastante bien con cualquier mando. El TSOP te filtra la portadora y te da la secuencia de bits, ahora solo tienes que "cronometrar" los tiempos de duración de ceros y unos...

Yo almacenaba esos valores, los volcaba al PC para analizar las secuencias y montaba mi propia libreria específica para cada mando... pudiendo conmutar entre un mando de TV, de Hi-Fi e incluso uno de un aparato de aire acondicionado... cualquier cosa que emitiera IR entre 36 y 40kHz... :)

furri.

PD: casi 3 años con Minitarribot acabado y aún sin completar la web... ¡¡ que verguenza !! ... jajajaja
Robotica para torpesxBotTarriBot

"El uso principal de un PC es confirmar la ley de Murphy."
furri
Usuario Experto
Usuario Experto
 
Mensajes: 2474
Registrado: Lun Ago 22, 2005 8:16 pm
Ubicación: Marbella
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor isotopo el Lun Ene 04, 2010 10:02 pm

Ya he conseguido algo. Creo que ha sido al quitar una resistencia de 10k (la opcional **) , que no sabía muy bien para qué servía, pero que con Arduino iba bien.

Imagen

De todos modos no sé si ha sido por eso, porque de primeras no ha funcionado, ha funcionado al final cuando se me ha ocurrido ponerlo en un pin ADC y he visto que recibía valores entre 0 y 1023 (10 bits). También obtengo ceros y unos con GetPin, aunque para capturar un tren de pulsos con InputCapture que era lo que me interesaba como ha de usarse en el pin 12 pues se ve que no va.

Bueno, al menos ya tengo el camino. Seguiré investigando, cuando lo consiga ya pegaré el código.
Avatar de Usuario
isotopo
Forero Habitual
Forero Habitual
 
Mensajes: 106
Registrado: Jue Ene 04, 2007 3:38 pm
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor isotopo el Mar Ene 05, 2010 1:20 am

Nada, lo he vuelto a poner después de cenar y ya no funciona.

El bus con Arduino da 5 y 4,3v (al pulsar el mando) pero con el BasicX da 0,82 y 0,75v. No entiendo nada.

Adios, me voy al himalaya...

Imagen
Avatar de Usuario
isotopo
Forero Habitual
Forero Habitual
 
Mensajes: 106
Registrado: Jue Ene 04, 2007 3:38 pm
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor isotopo el Mar Ene 05, 2010 1:37 am

Perdón, he vuelto porque ya sé porqué es, parece que debe estar conectado a un pin analógico por alguna razón que desconozco.

Aunque en el Arduino está en uno digital....
Avatar de Usuario
isotopo
Forero Habitual
Forero Habitual
 
Mensajes: 106
Registrado: Jue Ene 04, 2007 3:38 pm
Sexo: Hombre
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor franzi el Jue Feb 11, 2010 12:23 am

Buenas, estoy intentado hacer lo mismo que isotopo, pero con la PIC16F877, pero se como programar la lectura, lo tengo todo en los pines correspondientes, pero no se si he de utilizar alguna rutina especifica o una libreria, ya que la lectura todo el rato me da 33.

Gracias.
franzi
Iniciado
Iniciado
 
Mensajes: 42
Registrado: Lun Dic 14, 2009 12:10 am
Sexo: No especificado
País: Spain (es)

Re: Control con mando IR con el TSOP4838

Notapor Sphinx el Jue Feb 11, 2010 12:44 pm

Hola, no sé si conocíais lirc.org, pero si no es así, os recomiendo que paséis por la sección de Hardware. Tienen un montón de circuitos de interfaces tanto de Tx como de Rx.
Y en la sección de "suported remote controls", tenéis todos los mandos TV soportados por lirc y los códigos emitidos por ellos para cada botón pulsado. Lo cual os facilitará la vida en gran medida.

Yo en su momento construí un interfaz para el puerto serie del PC, con un TSOP1738 (hermano del TSOP4838), y con WINlirc, generaba la lista de códigos para mis mandos de TV, DVD, etc...

En la sección software de lirc.org encontrareis también cosas interesantes...

Espero que os sea de utilidad.

Slds,
Sphinx.
Sphinx
Usuario Experto
Usuario Experto
 
Mensajes: 465
Registrado: Jue Abr 03, 2008 4:45 pm
Ubicación: Madrid
Sexo: Hombre
País: Spain (es)

Siguiente

Volver a Sensores y actuadores

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados