/* irlink.c */

/* Originally intended for an IR link, this was modified
   for a 433.9 mhz RF link instead. It doesn't work as well 
   as it should and I intend to completely redo it. This code
   creates one or two pulses for each transmitted data bit.
   A single pulse at the start of a Bit interval indicates
   a ZERO while 2 pulses indicate a ONE bit.  This is compatible
   with the way IR remote control receivers work but wastes lots
   of bandwidth when used with an RF transmitter.
   
   Dale Heatherington  Aug. 1999
   
   */

#define p16c71
#define FOSC   8000000
#pragma CLOCK_FREQ 8000000

//For 1200 baud
//#define TMR0DIV 256-(FOSC/(4800*16)) Compiler will not do this math
//#define TMR0DIV 157
//#define SYNCPOINT TMR0DIV + ((255 - TMR0DIV) / 2)
//#define SYNCPOINT 204

//for 2400 baud
#define TMR0DIV 210


#ifdef p16c71
char  ADCON0@0x08;
char  ADCON1@0x88;
char  ADRES@0x09;
char  GPIO@0x06;
char  OPTION@0x81;

//INTCON bits
#define  ADIE   6
#define  T0IE   5
#define  INTE   4
#define  RBIE   3
#define  T0IF   2
#define  INTF   1
#define  RBIF   0 

#endif


#ifdef p12c671
/* Special registors for 16C671 chip */
char  GPIO@0x05;
char  PIR1@0x0c;
char  PIE1@0x8c;
char  ADRES@0x1e;
char  ADCON0@0x1f;

char  OPTION@0x81;
char  TRIS@0x85;
char  ADCON1@0x9f;
char  PCON@0x8e;
char  OSCCAL@0x8f;

#define  PEIE   6
#define  T0IE   5
#define  INTE   4
#define  GPIE   3
#define  T0IF   2
#define  INTF   1
#define  GPIF   0 

#endif
  

char  work;
//char  biphase;          /* 600 hz manchester data + clock */
char  tx_data;
char  tx_sample;
char  bits;                /* count bits in async char frame */
char  tx_clk, rx_clk;
char  rx_data;

char  rxtmr;

#define  DI       3     /* 600 baud RS232 data input on GP3*/
#define  IR_IN    2     /* IR input pulses */
#define  IR_OUTB  1     /* out to IR LED  */
#define  IR_OUTA  0     /* out to IR LED (Tie A and B in parallel) */
#define  DO       4     /* 600 Baud RS232 data out on GP4 */




//--------------------------------------------------------------
/* Emit 15 cycles of 40khz IR pulse on PORTA bit 1.
   An external transistor should be used to drive the
   IR LED at up to 500 MA. 
   PORTA bit 0 goes high to key an AM RF transmitter
    */

void ir_pulse()
{  char pcount,work;
   pcount = 15;
   work = GPIO;
   
L40KHZ:          
  //Make 15 cycles of 40 KHZ on PORTA bit 1                                   
   asm{bsf     _work_ir_pulse,0  ;  //Raise bit 0 for until were are done                                                      
       bsf     _work_ir_pulse,1  ;  //Pulse bit 1 at 40 khz
       movf    _work_ir_pulse,w   ;                                      
       movwf   _GPIO  ;    //Pin goes high                                     
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       nop   ;
       nop   ;
       nop   ;
       nop   ;                                                
       bcf     _work_ir_pulse,1  ;
       nop  ;
       movf    _work_ir_pulse,w ;                                        
       movwf   _GPIO  ; // pin goes low                                          
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       nop   ;                                                
       decfsz   _pcount_ir_pulse,f   ;                       
       goto  _L40KHZ 
       bcf  _GPIO,0                  ;Lower bit 0 (RF off)                   
      }
   
   return;
}
                                            
//---------------------------------------------------------

/* Timer 0 generates 2400 interrupts/sec */

void interrupt()
{
   
   char pcount;
   clear_bit(STATUS,RP0);  //BANK 0
   
   
   if(INTCON & (1 << T0IF)){  //Make sure it's a TMR0 interrupt

      
      TMR0 = TMR0DIV;         //Restart timer0
      clear_bit(INTCON,T0IF); //Reset interrupt flag

      rx_clk++;
      switch(rx_clk){  //PORTA,3 high during window for second pulse
      case  1:         // for debuging .
      case  2: set_bit(PORTA,3);
               break;
      case 3:
      case  4:
      case  5:clear_bit(PORTA,3);
               break;
      }

      if(rx_clk == 3) {
         
         if(rx_data) set_bit(GPIO,DO);
              else clear_bit(GPIO,DO);//output the rx data bit
      }


      if(rx_clk >= 5){
          rx_clk = 5;
          set_bit(GPIO,DO);  //Force RX mark (1) if no IR pulses
      }
      

      tx_clk++;
      tx_clk = tx_clk & 3;    //tx_clk rolls over after 4 ticks
      if(tx_clk & 2) set_bit(PORTB,5); 
         else clear_bit(PORTB,5);

      if((bits < 10) || (~tx_data & (1 << DI))){ //Only do IR pulses if bit count is 0..10
                                                 //or sending zeros
         switch(tx_clk){
                  
         case     2: clear_bit(GPIO,0); 
                     tx_data = GPIO;     //Sample the tx data bit
                     break;

         case     1: //ir_pulse();   //Always pulse here
                     set_bit(GPIO,0);
                     break;

         case     0: clear_bit(GPIO,0);
                     //tx_sample = GPIO;
                     break;

         case     3: if(tx_data & (1 << DI))
                        //ir_pulse(); //another pulse if bit = 1
                        set_bit(GPIO,0);
                     
                                        
                     break;
                  
                
                     
         }

        
      } else clear_bit(GPIO,0); //Finished a 10 bit frame

      if((tx_clk == 3) && (bits < 11)) bits++;  //Count data bits
   
     
   
   }
   
   
}

//-------------------------------------------------------------
main()
{
   char lastbuf,buf;

   STATUS   = 0;
   INTCON   = 0;
   GPIO     = 0;

#ifdef p16c71
   PORTA    = 0;
   PORTB    = 0;
#endif

   TMR0     = 0;

   set_bit(STATUS,RP0);    //BANK 1

#ifdef p12c671
   TRIS = 0x0c;            //GP2 and GP3 inputs. Others outputs
#endif
#ifdef p16c71
   TRISB = 0x0c;
   TRISA = 0;
#endif

   OPTION = 0x81;          //prescaler to Timer 0

   clear_bit(STATUS,RP0);  //BANK 0
   bits = 11;              //Transmiter dormant
   TMR0 = TMR0DIV;         //Start timer 0
   clear_bit(INTCON,T0IF);
   set_bit(INTCON,T0IE);   //Enable timer 0 interrupts
   set_bit(INTCON,GIE);    //Enable global interrupts

 
   while(1){
      
      set_bit(PORTB,6);
      buf = GPIO;
      

      if(~buf & (1 << DI)){              // DI is space (zero)
         if(bits >= 9){
            clear_bit(INTCON,T0IE);    //Disable timer interrupts
            
            if((buf ^ lastbuf) & (1 << DI)){  //mark to space transition (1->0)
               set_bit(PORTB,7);
               TMR0 = TMR0DIV;  //Set up for start of a 10 bit async frame
               
               tx_clk = 3;
               if(bits == 11) tx_data = 0;
               bits = 0;
               clear_bit(INTCON,T0IF);
               clear_bit(PORTB,7);
               set_bit(INTCON,T0IF);   //force an interrupt now
            }
            set_bit(INTCON,T0IE);       //enable timer interrupts 
            
         }
      }
      
      if(bits == 11){               // In receive mode
         //if(~buf & (1<< IR_IN)){    // IR pulse in progress (low true)
        if(buf & (1<< IR_IN)){        // RF pulse in progress (High true)
         disable_interrupt(T0IE);
         if((buf ^ lastbuf) & (1 << IR_IN)) {  //OFF to ON IR transition
               //set_bit(PORTA,3);
               
               switch(rx_clk){ //Discover when it happened relative to our clock
               case  0: 
                        break;
               case  3:
               case  4: 
               case  5: clear_bit(PORTA,3);
                        rx_clk = 0;
                        TMR0 = TMR0DIV;         //Reset prescaler
                        clear_bit(INTCON,T0IF);//make sure tmr int flag is clear
                        if(rx_data) set_bit(PORTA,2);
                           else clear_bit(PORTA,2);
                        rx_data = 0;           //assume bit will be zero
                        break;
               case  1:
               case  2: set_bit(PORTA,3);
                        rx_data = 0xFF;       //Data 1 bit detected
                        break;

               }
              //clear_bit(PORTA,3);                          
            }
            
            enable_interrupt(T0IE);
         }
      }

    
   lastbuf = buf;          //Update difference buffer
   
   clear_bit(PORTB,6);
   nop();
   nop();
   
 }


}

//------------------------------------------------------------

 

/* End of code */







