/******************************************************************************
 Title:    Code for an IR reciever 
 
 made for a SONY RM-D505 remote,
 tiny26 running at 1Mhz,
 and an array of 123 leds on a shift register (I used for testing)
 
 0           1ms       8-10   counts
         Barrier at 14
 1           2.5ms     18-19 counts
         Barrier at 28 
 start bit   5ms       37-38 counts

 
 Author:   rue_mohr
 Date:     Jan 8-14 2006
 Software: AVR-GCC 3.3 
 Hardware: attiny26
 
    
*******************************************************************************/
#include<avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

#define Forward                  1
#define Reverse                  -1

#define OUTPUT                   1
#define INPUT                    0

// Bit positions
#define Strobe                   7
#define Data                     5
#define Clock                    4

#define IRDet                    6

#define NumLeds                  123

#define SetBit(BIT, PORT)        PORT |= (1<<BIT)
#define ClearBit(BIT, PORT)      PORT &= ~(1<<BIT)
#define DefBit(BIT, PORT, VALUE) PORT &= (~(1<<BIT))|((VALUE==0?0:1)<<BIT)
#define IsHigh(BIT, PORT)       (PORT & (1<<BIT)) != 0
#define IsLow(BIT, PORT)        (PORT & (1<<BIT)) == 0
#define NOP()                    asm volatile ("nop"::)

#define StrobeH()                SetBit  (Strobe, PORTA)
#define StrobeL()                ClearBit(Strobe, PORTA)
#define DataH()                  SetBit  (Data,   PORTB)
#define DataL()                  ClearBit(Data,   PORTB)
#define SetData(B)               DefBit  (Data,   PORTB, B)
#define ClockH()                 SetBit  (Clock,  PORTB)
#define ClockL()                 ClearBit(Clock,  PORTB)
#define ClockPulse()             NOP(); ClockH(); NOP(); NOP(); ClockL(); NOP()
#define SendOne()                DataH(); ClockPulse()
#define SendZero()               DataL(); ClockPulse()

#define TimerReset()             TCNT0 = 0

#define IRH()                    IsHigh(IRDet, PINB)
#define IRL()                    IsLow(IRDet, PINB)

void Send (unsigned int bits);
void SetLights(unsigned int bits);
void Delay(unsigned int delay);
void inline OnEdge();
void TimerInit();
void Int0Init();

volatile unsigned int  IRData;

// IR Data Ready set if byte is available in buffer, user clears.
#define IRDR            7
volatile unsigned char IRStatus;

int main (void)  {
 
  DDRA = (INPUT << PA0 | INPUT << PA1 |OUTPUT << PA2 |INPUT << PA3 \
         |INPUT << PA4 | INPUT << PA5 |INPUT << PA6 |OUTPUT << PA7);
         
  DDRB = (INPUT << PB0 | INPUT << PB1 | INPUT << PB2 | INPUT << PB3\
        | OUTPUT << PB4 | OUTPUT << PB5 |INPUT << PB6 |INPUT << PB7);
      
  TimerInit();   // initialize timer    
  Int0Init();    // initialize int0  
  sei();         // turn on interrupts
  
  SetBit(IRDet, PORTB);            // sets pullup on IR reciever
                  
  while(1) {      
     if (IsHigh(IRDR, IRStatus)) { // when the flag gets set
       SetLights(IRData);          // a byte is ready in IRData
       ClearBit(IRDR, IRStatus);   // this is to ack the byte was read
     }
  }
}
    
//------------------------| FUNCTIONS |------------------------

void Int0Init() {
  MCUCR = (1<<ISC00);  // trigger interrupt on any edge !!!PROCESSOR!!!!
  SetBit(INT0, GIMSK); // enable the interrupt !!!PROCESSOR!!!!
}

SIGNAL (SIG_INTERRUPT0) { 
	if (IRH()) {  // bit just went high, check value of time
	  OnEdge();
    } else {      // bit just went low, reset timer
      TimerReset();  // start timing      
    }
}

void inline OnEdge() {
  static unsigned      BitCount;
  static unsigned int  IRRecReg;
  unsigned int         count;  
  
  count = TCNT0;   // snap shot of timer0 !!!PROCESSOR!!!!
  
  if (0) {
  } else if (count > 28) {    
      BitCount = 0;  // clear reciever register counter
      IRRecReg = 0;
  } else if (count > 14) {
      IRRecReg = IRRecReg << 1;
      IRRecReg |= 0x01;      
      BitCount++;
  } else {      
      BitCount++;
      IRRecReg = IRRecReg << 1;
  } 
  
  // set to recieve 12 bits
  if (BitCount == 12){    
     IRData = IRRecReg;     
     SetBit(7, IRStatus); // set data ready flag
  }
  
}

//------| Timerc Functions |--------

void TimerInit() { //set up 10Khz timer (15.625Khz)
  TCCR0 = ((1<<PSR0)|(3<<CS00));  // 1Mhz / 64  !!!PROCESSOR!!!!
}

//------| Led array output functions |------

void SetLights(unsigned int bits) {
  StrobeH();
  Send(bits);
  StrobeL();
}

void Send (unsigned int bits) {
  unsigned int temp;  
  for( temp = 32768; temp != 0; temp = temp >> 1) {    
    if ( (bits & temp) == temp ) {
      SendOne();
    } else {
      SendZero();
    }    
  } 
}

//------| Generic Functions |--------

void Delay(unsigned int delay) {
  unsigned int x;
  for (x = delay; x != 0; x--) {
    NOP();
  }
}

