//
// Title        : IR message encoding
// Author       : Lars Pontoppidan Larsen
// Date         : February 12, 2006
// Version      : 1.00
// Target MCU   : Atmel AVR Series
//
// USAGE:
// This module implements encoding DENVER IR messages. The message is created 
// by manipulating a bit in a timer interrupt.
//
// RESOURCES:
// One timer and OC output
//
// 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.     *
*                                                                             *
******************************************************************************/

// Signal output
#define IROUT_BIT        (1<<3)
#define IROUT_PORT       PORTD
#define IROUT_PIN        PIND
#define IROUT_DDR        DDRD

// Using timer2:
// Set CTC mode, prescaler 1024, compare interrupt
#define TIMER_INIT()        TCCR2A = (1<<WGM21); TIMSK2 = (1<<OCIE2A)
#define TIMER_VALUE         TCNT2
#define TIMER_CTC_VALUE     OCR2A
#define TIMER_USEC_P_TICK   (F_CPU/1000000/1024)
#define TIMER_ISR           TIMER2_COMPA_vect
#define TIMER_START()       TCCR2B = (1<<CS20)|(1<<CS21)|(1<<CS22)
#define TIMER_STOP()        TCCR2B = 0

typedef unsigned char timer_type;


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


// IR message:
//
//        
//
// ____   _____             _______   ___[____]      ___[____]   ______________
//     /\/     _____________       ___         ___/\/         ___
//                                                              
//             <---    Prologue   ---><--- BIT -->   <--- BIT --><- Epilogue ->                           
//                                    
//                                 


// DENVER message timing:          usec (ideally)
#define MSG_PROLOGUE_LO    68  //   8700
#define MSG_PROLOGUE_HI    34  //   4400

#define MSG_LO             4   //   550
#define MSG_HI_SHORT       4   //   550
#define MSG_HI_LONG        12  //   1650

#define MSG_EPILOGUE       200 

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

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


unsigned char irsend_msg[MSG_BYTES]; // Message buffer

unsigned char irsend_bit; // Progress in message

// IR sending status.
#define IRSEND_IDLE       0  
#define IRSEND_PROLOGUE   1  
#define IRSEND_LO         2  
#define IRSEND_HI         3  
#define IRSEND_EPILOGUE1  4  
#define IRSEND_EPILOGUE2  5  

volatile unsigned char irsend_status; 

// Next pulse setup
unsigned char irsend_next;

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


// Timer compare match interrupt
ISR(TIMER_ISR)
{
  unsigned char c=0;
  
  // epilogue2 is not followed by a toggle 
  if (irsend_status != IRSEND_EPILOGUE2) {
  
    // Toggle bit
    IROUT_PORT = IROUT_PORT ^ IROUT_BIT;    
    
  }
  
  // Set next pulselength
  TIMER_CTC_VALUE = irsend_next;
  
  // Prepare next pulselength
  switch (irsend_status) {
  case IRSEND_PROLOGUE:
    // Next pulse is MSG_LO, which ends the prologue
    irsend_next = MSG_LO;
    irsend_status = IRSEND_LO;
    break;
  
  case IRSEND_LO:
    // A low pulse is sending. Prepare length of next, high pulse
    
    // Get next message bit:
    if (irsend_bit < (MSG_BYTES*8)) {
      c = irsend_msg[(irsend_bit>>3)] & 0x80;
      irsend_msg[(irsend_bit>>3)] <<= 1;
      irsend_bit++;
      
      irsend_next = c ? MSG_HI_LONG : MSG_HI_SHORT;
      irsend_status = IRSEND_HI;
    }
    else {
      // Thats is, send epilogue
      irsend_next = MSG_EPILOGUE;
      irsend_status = IRSEND_EPILOGUE1;
    }
    
    break;
    
  case IRSEND_HI:
    // After hi follows a low bit
    irsend_next = MSG_LO;
    irsend_status = IRSEND_LO;
    break;
    
  case IRSEND_EPILOGUE1:
    // Now the epilogue is actually sending
    irsend_status = IRSEND_EPILOGUE2;
    break;
  case IRSEND_EPILOGUE2:
    // Now we are completely finished
    TIMER_STOP();
    irsend_status = IRSEND_IDLE;
    break;
  }
}
 
void irsend_send(char *msg)
{
  unsigned char c;
  
  // Wait for sender
  while (irsend_status != IRSEND_IDLE);
 
  // Copy message
  for (c=0; c<MSG_BYTES; c++)
    irsend_msg[c] = msg[c];
   
  irsend_bit = 0;
  
  // Start prologue low pulse
  IROUT_PORT = IROUT_PORT & ~IROUT_BIT;
  TIMER_VALUE = 0;
  TIMER_CTC_VALUE = MSG_PROLOGUE_LO;
  TIMER_START();
  
  // Prepare prologue high pulse
  irsend_next = MSG_PROLOGUE_HI;
  
  // Thus, status at next timer interrupt is:
  irsend_status = IRSEND_PROLOGUE;
  
}

void irsend_init(void)
{
  // Setup output signal to high
  IROUT_PORT |= IROUT_BIT;
  IROUT_DDR |= IROUT_BIT;
  
  TIMER_INIT();
  TIMER_STOP(); 
  
  irsend_status = IRSEND_IDLE;
 
}


