fbpx

Expresate

Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:

Avisos
Vaciar todo

Control con mando IR con el TSOP4838

15 Respuestas
7 Usuarios
0 Reactions
12.5 K Visitas
isotopo
Respuestas: 113
Topic starter
(@isotopo)
Estimable Member
Registrado: hace 18 años

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


// 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
#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
// 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;
}

Responder
14 respuestas
maggoo878
Respuestas: 1
(@maggoo878)
New Member
Registrado: hace 13 años

Hola a todos:

Yo tengo una pregunta con respecto al TSOP4838, Se podria utilizar un laser IR para que reconozca la señal del mismo?

Responder
furri
Respuestas: 2708
(@furri)
Ardero
Registrado: hace 19 años

Modulando el laser IR a 38kHz deberia funcionar igual que si usas un led IR... con el laser lo que haces es ganar distancia a costa de tener que apuntar con precisión.

Responder
beamspot
Respuestas: 1132
(@beamspot)
Noble Member
Registrado: hace 17 años

¿De que láser IR estaríamos hablando? Entiendo que se supone que uno tipo LED 'habitual', ¿no? Entonces, la pregunta no es tanto el si funcionaría (que va a ser que sí), si no el cómo hacerlo (mucho más complicado que lo que un padawan pueda suponer). Trabajar con láseres, por pequeños que sea, y especialmente con los de semiconductor, es una buena manera de quemar dinero más rápidamente que la crisis actual.

Responder
furri
Respuestas: 2708
(@furri)
Ardero
Registrado: hace 19 años

¿a que te refieres con "quemar dinero"?

Responder
beamspot
Respuestas: 1132
(@beamspot)
Noble Member
Registrado: hace 17 años

Hasta la fecha, los LED láser son los componentes electrónicos más sensibles a la ESD que conozco, y con mucho. Además, tienen dos problemas mas: una elevadísima sensibilidad a la temperatura, y una gran sensibilidad a la suciedad. La humedad es otro factor a tener en cuenta.

Hay que manejarlos en lugares con ESD controlado, sala blanca (por la suciedad), y deben tener un buen acoplo térmico, con elementos de una cierta masa térmica, para darle estabilidad. Y aún así, suelen llevar circuitería que sensa la potencia emitida, para mantenerla estable. Es un lazo de control bastante rápido, y necesario.

Una ligera descarga, un mal acoplo térmico, una variación algo brusca en la temperatura, o una mota de polvo encima de la superfície de salida del haz en el propio silicio, y el bicho 'se muere'. Una mirada un poco sospechosa, y el bicho se muere. Una respiración algo agitada, y el bicho se muere. Un soplido de aire frio, y se muere. Un estornudo, y ...

Responder
Página 3 / 3
Compartir: