/* Consumer IR receiver demo for the NEC protocol for MSP430G2231 (Launchpad)
 * Requires a 38kHz IR detector such as Vishay TSOP4838 on Pin 4 (P1.2/TA0.1)
 * Toggles a LED when a particular IR remote button is pushed
 * For more details on the NEC protocol, see http://www.sbprojects.com/knowledge/ir/nec.php
*/

#include <msp430.h>

// IR pulse timing constants in microseconds

#define HP_MIN      8500    // Header pulse minimum duration
#define HP_MAX      9500    // Header pulse maximum duration
#define HPL_MIN     4100    // Header pulse low minimum duration
#define HPL_MAX     4800    // Header pulse low maximum duration
#define RPT_MIN     1900    // Repeat pulse minimum duration
#define RPT_MAX     2500    // Repeat pulse minimum duration
#define BP_MIN      450     // Bit pulse minimum duration
#define BP_MAX      650     // Bit pulse maximum duration
#define ONE_MIN     2000    // Logical one minumum duration
#define ONE_MAX     2500    // Logical one maxumum duration
#define ZERO_MIN    1000    // Logical zero minumum duration
#define ZERO_MAX    1300    // Logical zero maxumum duration

#define FALSE 0
#define TRUE (!FALSE)
#define IR_REPEAT 0xFF00    // Special repeat code
#define MAX_DATA_BITS 32    // NEC protocol provides for 32 bits of data

#define PIN_IR      BIT2    // This must be TA0.1
#define PIN_LED1    BIT0    // LED1 on the Launchpad

#define BUTTON_POWER    0x00FD00FF    // This is a code for the button to toggle the LED

static volatile unsigned long data;

void ir_init(void)
{
    _disable_interrupts();
    BCSCTL1 = CALBC1_1MHZ;                  // Set DCO to 1MHz to measure pulses in microseconds
    DCOCTL = CALDCO_1MHZ;
    P1DIR &= ~PIN_IR;                       // Set IR detector pin to input
    P1OUT |= PIN_IR;                        // Set resistor to pull-up
    P1REN |= PIN_IR;                        // Enable resistor
    P1SEL = PIN_IR;                         // set IR detector pin to peripheral function
    TACTL = TASSEL_2 | ID_0 | MC_2;         // SMCLK, Continuous mode, Input divider /1
    TACCTL1 = CM_3 |  CCIS_0 | CAP | CCIE;  // Capture on both edges, Capture Input Select CCI0A, Capture mode, Capture interrupt enable
    _enable_interrupts();
}

void main(void)
{
    WDTCTL = WDTPW | WDTHOLD;       // Stop the Watchdog timer
    P1DIR = PIN_LED1;               // LED1 to output
    ir_init();                      // Initialize IR detection
    while(1)                        // Loop forever
    {
        _low_power_mode_0();        // Wait in LPM0 until the IR remote code is received
        // Set a breakpoint on the next line and add a watch for the data variable to see the IR code received
        if (data == BUTTON_POWER)   // If remote power button is pressed
            P1OUT ^= PIN_LED1;      // Toggle LED1
    }
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A1(void)
{
    static unsigned char hp, hr;                    //header pulse received, header received
    static unsigned char br;                        // bits received
    static unsigned char pulse=FALSE;               // pulse in progress
    static volatile unsigned char ready = FALSE;    // data ready
    static unsigned int pw=0, bw=0;                 // pulse width, bit width
    unsigned int i = TAIV;                          // Read TAIV to clear CCIFG

    // Is this the beginning or the end of the pulse?
    if (!pulse)
    // start of the pulse
    {
        bw=TACCR1;
        // reset Timer
        TACTL |= TACLR;
        pulse=TRUE;
        // Has the header pulse just been received?
        if (hp)
        {
            // Is the header duration in range?
            if (bw>HPL_MIN && bw<HPL_MAX)
            {
                hr=TRUE;
                br=0;
                data=0;
                ready=FALSE;
            }
            // is it a repeat code?
            else if (bw>RPT_MIN && bw<RPT_MAX)
            {
                data=IR_REPEAT;
                hr=FALSE;
                ready=TRUE;
            }
        }
        // Was there a bit pulse before, and also has the header been received?
        if(hr && pw>BP_MIN && pw<BP_MAX)
        {
            if (bw>ONE_MIN && bw<ONE_MAX)
            {
                data<<=1;
                data++;
                br++;
            }
            else if (bw>ZERO_MIN && bw<ZERO_MAX)
            {
                data<<=1;
                br++;
            }
            if (br==MAX_DATA_BITS)
            {
                hr=FALSE;
                ready=TRUE;
            }
        }
        hp=FALSE;
    }
    else
    // end of the pulse
    {
        pw=TACCR1;
        pulse=FALSE;
        // Is this a header pulse?
        if (pw>HP_MIN && pw<HP_MAX)
        {
            // set Header Pulse Received flag
            hp=TRUE;
            // Reset timer
            TACTL |= TACLR;
        }
        else
        {
            hp=FALSE;
        }
    }
    if (ready)
    {
        ready = FALSE;
        _low_power_mode_off_on_exit();  // Return from LPM
    }
}
