//
// Title        : IR message decoding
// Author       : Lars Pontoppidan Larsen
// Date         : February 12, 2006
// Version      : 1.00
// Target MCU   : Atmel AVR Series
//
// USAGE:
// This module implements decoding typical IR messages. A suitable MSG_THRESH 
// and MSG_MAX value should be selected based on an analysis of the message
// format.
//
// ir_msg_pending is nonzero if a message is pending in the buffer. When 
// finished dealing with the message, ir_msg_pending must be set back to 0. 
// If a message is recieved while ir_msg_pending is nonzero, it is discarded.
//
// RESOURCES:
// One external interrupt and one timer, 10 bytes of sram.
//
// DISCLAIMER:
// The author is in no way responsible for any problems or damage caused by
// using this code. Use at your own risk.
//
// LICENSE:
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//

#include <avr/io.h>
#include <avr/interrupt.h>
#include "ir.h"

/******************************************************************************
*                                                                             *
*                           HARDWARE ABSTRACTION                              *
*                                                                             *
*  In this section it is defined what hardware resources the module uses.     *
*                                                                             *
******************************************************************************/


// Ext interrupt 0 implementation.
// Set ddr input, no pullup, interrupt on rising and falling edge
#define IR_INIT()           DDRD &= ~(1<<2); PORTD &= ~(1<<2); EICRA = (1<<ISC00); EIMSK = (1<<INT0)
#define IR_SIGNAL           (PIND & (1<<2))
#define IR_ISR              INT0_vect

// // Using timer1
// // Set normal mode, prescaler 8
// #define TIMER_INIT()        TCCR1A = 0; TCCR1B = (1<<CS11); TIMSK1 = (1<<OCIE1A)
// #define TIMER_VALUE         TCNT1
// #define TIMER_CTC_VALUE     OCR1A
// #define TIMER_USEC_P_TICK   (F_CPU/1000000/8)
// #define TIMER_ISR           TIMER1_COMPA_vect
// typedef unsigned short timer_type;

// Using timer0:
// Set normal mode, compare interrupt, prescaler 1024
#define TIMER_INIT()        TCCR0A = 0; TCCR0B = (1<<CS02)|(1<<CS00); TIMSK0 = (1<<OCIE0A)
#define TIMER_VALUE         TCNT0
#define TIMER_CTC_VALUE     OCR0A
#define TIMER_USEC_P_TICK   (F_CPU/1000000/1024)
#define TIMER_ISR           TIMER0_COMPA_vect
typedef unsigned char timer_type;

/******************************************************************************
*                                                                             *
*                               MODULE SETUP                                  *
*                                                                             *
******************************************************************************/
 

// Pulses below MSG_THRESH will be regarded 0, otherwise 1
// Above MSG_MAX will cause timer compare match, and be interpreted as end of message
#define MSG_THRESH  11 //(timer_type)(1400 * TIMER_USEC_P_TICK)
#define MSG_MAX     78 //(timer_type)(10000 * TIMER_USEC_P_TICK)

// Initial pulses to skip
#define MSG_PROLOGUE 1

// Length of message (must be < 32)
#define MSG_BYTES 6

/******************************************************************************
*                                                                             *
*                                  GLOBALS                                    *
*                                                                             *
******************************************************************************/


unsigned char ir_msg[MSG_BYTES]; // Message buffer
volatile unsigned char ir_msg_pending;    // If nonzero, messagebuffer has a message

unsigned char ir_msg_tmp[MSG_BYTES]; // Message double buffer

unsigned char ir_bit; // Progress in message

// IR decoding status.
#define IR_IDLE           0  //
#define IR_PROLOGUE       1  // These are receiver status
#define IR_RECEIVING      2  //

volatile unsigned char ir_status; 

/******************************************************************************
*                                                                             *
*                               IMPLEMENTATION                                *
*                                                                             *
******************************************************************************/

// IR external interrupt
ISR(IR_ISR)
{
  // Save pin status and timer value now
  unsigned char c;
  timer_type tvalue;
  c = IR_SIGNAL;
  tvalue = TIMER_VALUE;
  
  switch(ir_status) {
  case IR_IDLE:
    TIMER_VALUE = 0;
    ir_status = IR_PROLOGUE;
    // Clear msg
    for (c=0; c<MSG_BYTES; c++)
      ir_msg_tmp[c]=0;
      
    ir_bit=0;
    break;
    
  case IR_PROLOGUE:
    if (c) {
      // Lo -> Hi. 
      TIMER_VALUE = 0;
      ir_bit++;
      if (ir_bit > MSG_PROLOGUE) {
        // Prologue finished
        ir_status = IR_RECEIVING;
        ir_bit = 0;
      }
    } 
    
    break;
  
  case IR_RECEIVING:
    
    if (c) {
      // Lo -> Hi.
      TIMER_VALUE = 0;
      
      // Assemble message
      if (ir_bit < (MSG_BYTES*8)) {
        ir_msg_tmp[(ir_bit>>3)] = (ir_msg_tmp[(ir_bit>>3)] << 1) | (tvalue > MSG_THRESH ? 1 : 0);       
        ir_bit++;
      }
      
    }
      
  }
  
}

// Timer compare match interrupt
ISR(TIMER_ISR)
{
  unsigned char c;

  if (ir_status == IR_RECEIVING) {
    // Message have finished, copy buffer, if allowed
    if (!ir_msg_pending) {    
      for (c=0; c<MSG_BYTES; c++)
        ir_msg[c] = ir_msg_tmp[c];
        
      ir_msg_pending = 1;
    }
    
    // Set status
    ir_status = IR_IDLE;
  }
}

void ir_init(void)
{
  IR_INIT();
  TIMER_INIT();
  TIMER_CTC_VALUE = MSG_MAX; 
  ir_status = IR_IDLE;
 
}


