


/* The software in this file is based on code from FU Berlin. */

/*
Copyright 2003/2004, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
 
- Neither the name of Freie Universitaet Berlin (FUB) nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

This software is provided by FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

This implementation was developed by the CST group at the FUB.
Contributors: Thomas Pietsch, Bjoern Lichtblau

*/

/*  \file recir.c
 ** \ingroup Firmware
 ** \brief Receiving RC5 via IR Receiving Diode.
 **
 ** \code
 ** RC5: 1780 us bitlength (manchester encoded, so half bitlength of 890 us is important)
 ** Transferred packet (2 start + toggle bit + 5 address bits + 6 comand bits)): 
 **                                   | S | S | T | A4 | A3 | A2 | A1 | A0 | C5 | C4 | C3 | C2 | C1 | C0 |
 ** irdata format: | ? | ? | error  | newData | T | A4 | A3 | A2 | A1 | A0 | C5 | C4 | C3 | C2 | C1 | C0 |
 ** \endcode
 **
 ** <img src="../pics/rc5.jpg">
 ** See detailed description at <a href="http://users.pandora.be/davshomepage/rc5.htm">http://users.pandora.be/davshomepage/rc5.htm</a>
 **
 ** Some common addresses and commands:
 ** \code
 ** Address:          Device:          Command:
 **   0               TV1              0...9    Numbers 0...9 (channel select)
 **   1               TV2              12       Standby
 **   5               VCR1             16       Master Volume +
 **   6               VCR2             17       Master Volume -
 **  17               Tuner            18       Brightness +
 **  18               Audio Tape       19       Brightness -
 **  20               CD Player        50       Fast rewind
 **                                    52       Fast run forward
 **                                    53       Play
 **                                    54       Stop
 **                                    55       Recording
 ** \endcode
 **/

#include <io.h>
#include <signal.h>

#include "dev/ir.h"

#include "dev/leds.h"
#include "dev/beep.h"

PROCESS(ir_process, "IR receiver");
process_event_t ir_event_received;
/*---------------------------------------------------------------------------*/
#define SIR1  (P1OUT |= 0x01) ///< MACRO: Puts IR sending diode high.
#define SIR0  (P1OUT &= 0xFE) ///< MACRO: Puts IR sending diode low.
#define BIT75 3282      ///< 3 quarters of a bit after start, 3282 cyc @ 2,4576Mhz = 1335us.
#define BIT50 2188      ///< Half of bit length, 2188 cyc @ 2,4576Mhz = 890 us.

/*---------------------------------------------------------------------------*/
/* Sends a logical one via IR, method is timed for the 2.4576Mhz SMCLK!!!
 */
static volatile void
send1bit(void)
{
  volatile int i;
  for(i = 0; i < 34; ++i) {
    SIR1; SIR1; SIR1; SIR1;
    SIR0; SIR0; SIR0; SIR0; 
    SIR0; SIR0; SIR0; SIR0; 
    SIR0; 
  }  
}
/*---------------------------------------------------------------------------*/
/* Sends a logical 0 via IR, method is timed for the 2.4576Mhz SMCLK!!!
 */
static volatile void
send0bit(void)
{
  volatile int i;
  for(i = 0; i < 34; ++i) {
    SIR0; SIR0; SIR0; SIR0; 
    SIR0; SIR0; SIR0; SIR0; 
    SIR0; SIR0; SIR0; SIR0; 
    SIR0; 
  }  
}
/*---------------------------------------------------------------------------*/
/* Sends the lower 12 bits of data via IR, turns interrupt off while
   it's sending.
 */
void
ir_send(unsigned short data)
{
  volatile unsigned short mask = 0x2000;
  data |= 0xF000;
  
  dint();
  while(mask != 0){
    if(!(mask & data)){
      send1bit();
      send0bit();
    } else {
      send0bit();
      send1bit();
    }
    mask /= 2;
  }
  eint();
}
/*---------------------------------------------------------------------------*/
/* Testroutine which repetedly sends two commands.
 */
/*void
ir_test_send(void)
{
  volatile unsigned int i;
  send12bits(0xF010);
  for(i=0; i<0xFFFF; i++) nop(); 
  send12bits(0xF011);
  for(i=0; i<0xFFFF; i++) nop();
}*/
/*---------------------------------------------------------------------------*/


static void setErrorBit(void);
static void clearErrorBit(void);
static void setDataAvailableBit(void);
static void clearDataAvailableBit(void);


/// \name Internal variables.
//@{
static unsigned int ir_pos;             ///< current position in frame
static unsigned int recvdata;            ///< here a received packet is saved
static unsigned int recvdatabuffer;      ///< temporary buffer for receiving
static unsigned char ir_temp;           ///< saves the first half of the manchester bit
//@}

/// \name Public functions. 
/// If ::recir_dataAvailable()==1 use the get* functions.
//@{
unsigned char recir_getCode(void){ return (recvdata & 0x003F); }
unsigned char recir_getAddress(void){ return ((recvdata & 0x07C0) >> 6); }
unsigned char recir_getToggle(void){  return ((recvdata & 0x0800) >> 11); }
unsigned char recir_getError(void){ return ((recvdata & 0x2000) >> 13); }

u16_t
ir_data(void)
{
  return recvdata;
}

u8_t
ir_poll(void)
{
  if(recvdata & 0x1000) {
    clearDataAvailableBit();
    return 1;
  } else {
    return 0;
  }
}


///\name Internal functions.
//@{
static void setErrorBit(void){ recvdata |= 0x2000; }
static void clearErrorBit(void) { recvdata &= 0xDFFF; }
static void setDataAvailableBit(void){ recvdata |= 0x1000; }
static void clearDataAvailableBit(void){ recvdata &= 0xEFFF; }


/// Timer B0 interrupt service routine
interrupt(TIMERB1_VECTOR) Timer_B1 (void) {

  /*P2OUT = (P2OUT & 0xf7) | (8 - (P2OUT & 0x08));*/
  
  if(ir_pos <= 25) {
      if(ir_pos % 2) {                             // odd position
        if(ir_temp && !(P1IN & 0x04)) {           // 1 - 0 -->  write 1 
          recvdatabuffer +=1;
          recvdatabuffer = recvdatabuffer << 1;  
        } else if(!ir_temp && (P1IN & 0x04)) {      // 0 - 1 -->  write 0 
          recvdatabuffer = recvdatabuffer << 1;  
        } else { 
          setErrorBit();
          if(P1IN & 0x04) {
	    recvdatabuffer += 1;
	  }
          recvdatabuffer = recvdatabuffer << 1;
        }
      } else {                                    // even position
        ir_temp = P1IN & 0x04;
      }
    }
    
    if(ir_pos == 25) {                          // end reached
      recvdatabuffer = recvdatabuffer >> 1; 

      if(!recir_getError() && ( (recvdatabuffer & 0x0FFF) != (recvdata & 0x0FFF) ) ){
        recvdata = recvdatabuffer;
        setDataAvailableBit();
      } else {
        _NOP();
      }      
    }
    
    if(ir_pos==27) {
      TBCCTL1 &= ~CCIE;  

      //GREENOFF;      
      // temporary debug output
      //sendRS232Address(recvdatabuffer);
      //if(recir_getError()) sendRS232('E');
      //sendRS232String("\r\n");
      if(!recir_getError()) beep_beep(20);
      
      // reenable interrupt for falling edge
      P1IFG &= ~(0x04);
      P1IE  |= 0x04;                    // enable interrupt for recir RC5
      leds_red(LEDS_OFF);
    }
    
  ir_pos++;
  TBCCR1 += BIT50;        // set new interrupt
 
  TBCCTL1 &= ~CCIFG;
}


/** \brief IR Interrupt routine 
 **
 ** For the falling edge (start of RC5 packet)( mid of first start bit ), IRReceiver is on P12
 ** real interrupt routine, which calls this, is in sensors.c */
void
ir_irq(void)
{
  if(P1IN & 0x04) return; // high again, just a peak

  ir_pos = 0;
  recvdatabuffer = 0;  
  clearErrorBit();

  // the first timer interrupt will occur in the mid of the first half of the second start bit 
  TBCCR1 = TBR + BIT75;     // set first TBCCR1 IRQ to 75% of RC5 bitlength
  TBCCTL1 &= ~CCIFG;        // clear previous compare flag
  TBCCTL1 |= CCIE;          // CCR0 interrupt enabled, interrupt occurs when timer equals CCR0
  
  P1IE &= ~0x04;   // disable interrupt for P12 ( ReceiveIR )
  leds_red(LEDS_ON);
  //GREENON;
} 

//@}
/*---------------------------------------------------------------------*/
PROCESS_THREAD(ir_process, ev, data)
{
  PROCESS_BEGIN();

  // init TIMERB ccr0 to run continouslycreate the 5 ms interval
  // ccr1 is used for ir receiving (RC5)
  TBCTL = TBSSEL1 + TBCLR;         // select SMCLK (2.4576MHz), clear TBR
  TBCTL |= MC1;                         // Start Timer_A in continuous mode
  

  P1IES |= 0x04;  // Important for IR-RC5 receive to detect the first FALLING edge

  ir_event_received = process_alloc_event();

  while(1) {
    PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL);
    
    if(ir_poll() == IR_DATA) {
      unsigned short irdata;
      irdata = ir_data() & 0x7ff;    
      process_post(PROCESS_BROADCAST, ir_event_received, (process_data_t)irdata);
    }
    
  }
  
  PROCESS_END();
}
/*---------------------------------------------------------------------*/
