// file: irbeacon.c
// vers: 1.0
//
// This is a program for the PIC16F84 running on an iRX board
// (see www.media.mit.edu/~r/projects/irx2_1/).  
//
// This program repeatedly writes "Hello, world\r\n" to the
// serial port and over the IR port using IrDA signalling
// protocols.  Its primary function is as a debugging aid for
// other IrDA programs.
//
#include <16F84.H>
#fuses HS,WDT,NOPROTECT,PUT
#use DELAY(clock=10000000)

#use fast_io(B)

// Standard definitions for the irx2_1 board
//
#define RS232_XMIT      PIN_B1  // (output) RS232 serial transmit
#define RED_LED         PIN_B2  // (output) Red LED (low true)
#define IR_LED          PIN_B3  // (output) Infrared LED (low true)
#define IR_SENSOR       PIN_B4  // (input) IR sensor (Sharp IS1U30)
#define RS232_RCV       PIN_B5  // (input) RS232 serial receive

// Macros to simplify I/O operations
//
#define RED_LED_ON      output_low(RED_LED)
#define RED_LED_OFF     output_high(RED_LED)
#define IR_LED_ON       output_low(IR_LED)
#define IR_LED_OFF      output_high(IR_LED)
#define	IR_RECEIVED	(!input(IR_SENSOR))

// Default TRIS bits: RS232_RCV and IR_SENSOR are inputs, all
// others are outputs
//
#define IRX_B_TRIS      0b00110000

// ===================================================================
// General definitions
//

struct {
  short int RBIF;
  short int INTF;
  short int T0IF;
  short int RBIE;
  short int INTE;
  short int T0IE;
  short int PEIE;
  short int GIE;
} INTCON;
#byte	INTCON	= 0x0B

// linkage between interrupt routine (handle_ext_int) and
// foreground routines.  Set to true when an IR pulse was
// detected (at interrupt level).
//
int gFlags;
#bit	gIRDAPulseSeen = gFlags.0

// general I/O buffer
//
#define BUF_SIZE	32
char gBuf[BUF_SIZE];

// ===================================================================
// IRDA constants
//

// Port B0 is also an input (jumpered from IR_RECEIVE port)
#define IRDA_TRIS	(IRX_B_TRIS | 0b00000001)

// Minimum IRDA pulse is 1.63 uSec.  With 400 nSec instruction
// cycle time (10MHz clock), this requires 4+ cycles, ergo 5.
//
#define IRDA_PULSE_CYCLES	5

// The following assume an RTCC prescaler of 4:1, or 1600 nSec tics
//
#define	IRDA_BIT_TICS		65	// 1/9600 baud

// ===================================================================
// IR Output
//

// Wait until the *previous* bit period has expired, then
// write a single bit using IrDA 1.0 protocol: a 1.63 uSec
// pulse for a zero bit, no pulse for a one bit.
//
// Reads low order bit in b
//
void putIRDABit(int b) {
  do {} while (!INTCON.T0IF);	// wait for TMR0 to expire
  set_rtcc(256-IRDA_BIT_TICS);	// reset for next bit period
  INTCON.T0IF = 0;
  restart_wdt();
  output_high(PIN_A2);		// for 'scope testing
  if (!(b&1)) {			// pulse for 0 bit, no pulse for 1
    IR_LED_ON;
    delay_cycles(IRDA_PULSE_CYCLES-1);
    IR_LED_OFF;
  }
  output_low(PIN_A2);		// for 'scope testing
}

// write a start bit, 8 bits of b (LSB first), and a stop
// bit using IrDA 1.0 protocol.
//
void putIRDAByte(char b) {
  output_high(PIN_A1);
  putIRDABit(0);		// start bit
  putIRDABit(b);		// b0: lsb first
  b >>= 1;
  putIRDABit(b);		// b1
  b >>= 1;
  putIRDABit(b);		// b2
  b >>= 1;
  putIRDABit(b);		// b3
  b >>= 1;
  putIRDABit(b);		// b4
  b >>= 1;
  putIRDABit(b);		// b5
  b >>= 1;
  putIRDABit(b);		// b6
  b >>= 1;
  putIRDABit(b);		// b7: msb
  putIRDABit(1);		// stop bit
  output_low(PIN_A1);
}

// ===================================================================
// RS232 constants

#use rs232(baud=9600, xmit=RS232_XMIT, rcv=RS232_RCV, RESTART_WDT)

// ===================================================================
// Main read/write loop
//

// sleep() 27 times for 18 mSec for a total of 500 mSec delay.  
// Normally we'd use a prescaler on the watchdog timer instead, 
// but the prescaler is already assigned to the RTCC.
//
void snooze() {
  int i;
  for (i=0; i<27; i++) sleep();
}

void main() {
  set_tris_b(IRDA_TRIS);

  RED_LED_ON;			// reality check at startup
  delay_ms(200);
  RED_LED_OFF;

  printf("IrDA Beacon 1.0\r\n");

  setup_counters(RTCC_INTERNAL, RTCC_DIV_4);  
  while (1) {
    RED_LED_ON;
    printf("Hello, world.\r\n");
    RED_LED_OFF;
    INTCON.T0IF = 1;		// makes putIRBit() start immediately
    putIRDAByte("Hello, world.\r\n");
    snooze();
  }
}

