Jump to content
43oh

LaunchPad IR Receiver


Recommended Posts

Since I did the IR transmitter, I figured I need a receiver, so here's my first iteration.
For this project, I am using Vishay's TSOP38238, which takes care of detecting, amplifying, filtering, and demodulating IR signal.
Output from IR module is connected directly to port P1.1, which is configured as capture input.
Timer's capture/compare capability is used to process the signal.
P1.3-P1.5 are connected to LEDs to show the result (LEDs are toggled so sometimes they stay on, it's not a bug.)
The only problem with this design is that some remotes could have shorter off period, which would screw up the logic (at least that's what I've read somewhere, but then again, you cannot always believe what you read on the net.)


#include "msp430g2231.h"

#define T05 300
#define T65 T05*13
#define T2 T05*4
#define T3 T05*6

unsigned int rxData = 0; // received data: A4-A0 and C6-C0 0000 AAAA ACCC CCCC
unsigned int bitCounter = 0;

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // stop WDT
BCSCTL1 = CALBC1_1MHZ; // load calibrated data
DCOCTL = CALDCO_1MHZ;

P1DIR &= ~BIT1; // P1.1 input
P1SEL = BIT1; // P1.1 Timer_A CCI0A

P1OUT &= ~(BIT3 + BIT4 + BIT5); // P1.3-1.5 out
P1DIR |= (BIT3 + BIT4 + BIT5);

TACTL = TASSEL_2 | MC_2; // SMCLK, continuous mode
CCTL0 = CM_2 | CCIS_0 | CAP | CCIE; // falling edge capture mode, CCI0A, enable IE

__bis_SR_register(LPM0_bits + GIE); // switch to LPM0 with interrupts
}

#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
if(CCTL0 & CAP) { // start bit
bitCounter++; // start counting bits
CCR0 += T65; // add 6.5 bits to counter
CCTL0 &= ~ CAP; // compare mode
} else {
switch (bitCounter) {
case 0x1000: // received all bits
bitCounter = 0; // reset counter
// process received data, for example toggle LEDs
switch (rxData & 0x001F) { // mask device number
case 19: // Volume - 0010011 = 19
P1OUT ^= BIT3;
break;
case 18: // Volume + 0010010 = 18
P1OUT ^= BIT4;
break;
case 21: // Power 0010101 = 21
P1OUT ^= BIT5;
break;
}
rxData = 0;
// end process received data
CCTL0 |= CAP; // capture mode
break;
default: // data bit
if (CCTL0 & SCCI) { // bit = 1
CCR0 += T2; // add 2 bits to counter
} else { // bit = 0
rxData |= bitCounter; // set proper bit of rxData
CCR0 += T3; // add 3 bits to counter
}
bitCounter <<= 1; // increase (shift) bit counter
break;
}
}
}

The first diagram shows how the current code works, the second one shows how it should work to be more reliable (new code to follow.)
post-197-135135494294_thumb.png
Link to post
Share on other sites

I have a comment about "the second one shows how it should work to be more reliable (new code to follow.)"

 

I think the way you intend to detect "Start" is not very robust. It will misidentify a "1" followed by a "0", or a "0" followed by a "1" as a "Start".

Link to post
Share on other sites

True, I was thinking about that too and here's the solution, after we capture falling edge, we switch to capture on rising edge.

We can then time the start bit and all following bits.

 

post-197-135135494301_thumb.png

 

Also, this whole experiment made me change my mind about what protocol to use for my next IR remote project, looks like RC5 will be much easier to implement.

Link to post
Share on other sites
  • 1 month later...

Hi Rob,

 

I wonder if your SIRC remotes generate multiple output command packets per key press, and if so, how you might have worked around it? It seems even the quickest button press on my Sony remote generates three (3) output packets at around 45-msec intervals. I ended up using a repeat timer to filter out the extra commands, which also allows single or repeat operation. I'd love to hear how you did it.

 

If you or other members are interested, I use a slightly different method for decoding SIRC commands on my PIC projects. I simply count the number of 100-usec intervals the SIRC decoder IC output is low (24, 12, or 6). It's probably not the most robust or noise immune method but it works extremely well in my environment whether I'm using it in a an isochronous loop in main or in an ISR (interrupt service routine). I can hardly wait to try it on the MSP430 but with mid-terms next week I've had very little time to study the MSP430 Family Reference Manual.

 

Cheerful regards, Mike

 

  /*
  *  Mike McLaren's PIC SIRC decoder (100-usec intervals)
  */
  if(reptmr)                   // if repeat timer running
  { reptmr--;                  // decrement it
  }
  if(irpin == 0)               // if IR pin lo
  { count++;                   // inc 100-usec counter
  }
  else                         // if IR pin hi
  { if(count)                  // if new bit received
    { if(bitctr)               // if rx-in-progress
      { sirc >>= 1;            // make room for new bit
        sirc.12 = 0;           // set new bit to '0'
        if(count > 900/100)    // if a '1' bit (>900-us)
          sirc.12 = 1;         // set new bit to '1'
        if(--bitctr == 0)      // if last bit
        { if(reptmr == 0)      // if repeat timer timed out
        { sonycmd = sirc;    // pass new command to main
          reptmr = 2500;     // reset 250-ms repeat timer
        }                    //
        }                      //
      }                        //
      else                     // not rx-in-progress so
      { if(count > 2000/100)   // if new 'start' bit (>2000-us)
          bitctr = 12;         // set rx-in-progress flag
      }
      count = 0;               // clear 100-usec counter
    }
  }

Link to post
Share on other sites
Hi Rob,

I wonder if your SIRC remotes generate multiple output command packets per key press, and if so, how you might have worked around it? It seems even the quickest button press on my Sony remote generates three (3) output packets at around 45-msec intervals. I ended up using a repeat timer to filter out the extra commands, which also allows single or repeat operation. I'd love to hear how you did it.

I guess it depends on how you want to implement it.

If you need to toggle, I would add some sort of timer/counter that would check the time between packets and only toggle when that time is bigger than lets say 200ms.

Link to post
Share on other sites

Ok, that's cool... Let's say you wanted to remote control a couple AC outlets via relays, or zero-crossing opto-isolators and triacs, or whatever. Obviously you would want to simply toggle the outlets to avoid making the outlets go on and off repeatedly with the extra command packets, so how would you filter out the extra SIRC command packets with your method?

 

Regards, Mike

Link to post
Share on other sites
  • 1 year later...

Hi Guys

 

I'm trying to use the above code to only process one IR packet but cant seem to get it working.

 

I'm not at home so I cant post any code examples currently.

 

Basically:

 

I only need to toggle on/off or increment a counter once then listen for the next press. my latest (and not so greatest) idea was to read the packets into a array and process one packet and clear the others but I cant seem to get it working.

 

any ideas are MUCH APPRECIATED!

Link to post
Share on other sites
  • 4 months later...

 

the second one shows how it should work to be more reliable (new code to follow.)

 

Hi Robg,

Can you post the alternative code for it to work. I am still at the stage of learning MSP, so it is very much appreciated if you would like to post it. =)

 

Warm Regards,

Kent 

Link to post
Share on other sites

Here's the alternate code. It uses method from post #3 and port interrupt instead of timer's capture/compare feature.

Timer is used to measure pulse's width and for timeout when signal is lost half way through.

 

Enjoy.

 

#include "msp430g2553.h"

#define T05	300
#define T25 T05*5
#define T35 T05*7
#define T50 T05*10
#define TMAX 65000

#define IR_DETECTOR_PIN BIT1

void reset();

unsigned int rxData = 0; // received data: A4-A0 and C6-C0 0000 AAAA ACCC CCCC
unsigned int bitCounter = 0;

void main(void) {
	WDTCTL = WDTPW + WDTHOLD; // stop WDT
	BCSCTL1 = CALBC1_1MHZ; // load calibrated data
	DCOCTL = CALDCO_1MHZ;

	P1OUT &= ~(BIT0 + BIT6); // P1.0 & P1.6 out (LP LEDs)
	P1DIR |= (BIT0 + BIT6);

	P1REN |= IR_DETECTOR_PIN; // P1.1 pull-up
	P1OUT |= IR_DETECTOR_PIN; // P1.1 pull-up
	P1IE |= IR_DETECTOR_PIN; // P1.1 interrupt enabled
	P1IES |= IR_DETECTOR_PIN; // P1.1 high/low edge
	P1IFG &= ~IR_DETECTOR_PIN; // P1.1 IFG cleared

	CCR0 = TMAX; // interrupt if no edge for T32
	TACTL = TASSEL_2 + MC_1; // SMCLK, up mode

	__bis_SR_register(LPM0_bits + GIE);
	// switch to LPM0 with interrupts
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void) {

	if (P1IFG & IR_DETECTOR_PIN) {
		P1IE &= ~IR_DETECTOR_PIN;

		if (bitCounter == 0) {
			P1IES &= ~IR_DETECTOR_PIN; // P1.1 low/high edge
			bitCounter++;
			TACTL |= TACLR;
			TACTL |= MC_1;
			CCTL0 = CCIE;
		} else {
			switch (bitCounter) {
			case 14: // received all bits
				// process received data, for example toggle LEDs
				switch (rxData & 0x001F) { // mask device number
				case 19: //	Volume -	0010011 = 19
					P1OUT ^= BIT0;
					break;
				case 18: //	Volume +	0010010 = 18
					P1OUT ^= BIT6;
					break;
				case 21: //	Power	0010101 = 21
					P1OUT |= BIT6;
					P1OUT |= BIT0;
					break;
				case 20: //	Mute	0010100 = 20
					P1OUT &= ~BIT6;
					P1OUT &= ~BIT0;
					break;
				}
				reset();
				break;
			case 1: // start bit?
				if (TA0R < T35) { // could also add || TA0R > T50
					reset();
				} else {
					TACTL |= TACLR;
					TACTL |= MC_1;
					bitCounter++;
				}
				break;
			default: // data bit
				rxData >>= 1;
				if (TA0R > T25) {
					rxData |= 0x0800; // set bit 12 of rxData
				}
				TACTL |= TACLR;
				TACTL |= MC_1;
				bitCounter++; // increase bit counter
				break;
			}
		}
		P1IFG &= ~IR_DETECTOR_PIN;
		P1IE |= IR_DETECTOR_PIN;
	}
}

void reset() {
	CCTL0 &= ~CCIE;
	P1IES |= IR_DETECTOR_PIN; // P1.1 high/low edge
	rxData = 0;
	bitCounter = 0;
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A(void) {
	// reset
	P1IE &= ~IR_DETECTOR_PIN;
	reset();
	P1IFG &= ~IR_DETECTOR_PIN;
	P1IE |= IR_DETECTOR_PIN;
}

Link to post
Share on other sites

Hi,

 

wow~ you are real code master, no wonder you got the award of code Guru. Thanks a lot!! =D  

 

Can I ask you a question? If I want to make the LED toggle only once for each key pressed, how should it be implemented in the code? 

 

Cheerful regards,

Kent =)

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...