/*
===========================================================================
Copyright (C) 2010 Matthew Hirsch, MIT Media Lab

This file is part of the shutter glasses driver example source code provided
for the Build Your Own 3D course for SIGGRAPH 2010.

The shutter glasses driver source code is free software; you can redistribute 
it and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

The shutter glasses driver source code is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the shutter glasses driver source code; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
02110-1301 USA
===========================================================================
*/

/* 04/22/10 LCD shutter glasses driver code for PIC 16f506 */
/* by Matt Hirsch mhirsch@media.mit.edu */

/* to compile with HI-TECH c: */
/* wine ~/.wine/drive_c/Program\ Files/HI-TECH\ Software/PICC/9.70/bin/picc.exe  --chip=16f506 driver.c */

/* there are no interrupts on this part - do everything by polling */

#include <pic.h>

/* set configuration bits during programming */
/* * internal clock with extra io pins enabled */
/* * external MCLR disabled (extra io pin) */
/* * no code protection */
/* * no watchdog timer */

__CONFIG(INTIO & INTOSC8 & MCLRDIS & UNPROTECT & WDTDIS);

/* pin mapping : 
 *
 * RB5: status LED
 *
 * :supplies:
 * RC0: high voltage sense resistor input
 * RC2: high voltage driver PWM signal
 * RB2: lv supply enable
 *
 * :lcd driver:
 * RB4: 1khz background output (polarity pin)
 * RC5: high voltage enable (controlls state of LCD1)
 * RC3: high voltage enable (controlls state of LCD2)
 * RC4: polarity switch
 * RC1: vsync input
 */

#define _XTAL_FREQ 8000000

void main() {

  unsigned char i;
  unsigned char k;
  unsigned char hv_duty = 0;
  unsigned char vsync_last = 0;
  unsigned char pol_last = 0;
  unsigned char pol_switch_debounce = 0;
  unsigned char q;
  unsigned char trig = 0;
  unsigned char delay = 160;
  unsigned char dcnt = 0;

  /* set tri state buffers (inputs and outputs) */

  /* TRISB */
  /* b7 b6 b5 b4 b3 b2 b1 b0 */
  /* i  i  o  o  i  o  i  i; */
  TRISB = 0xCB;
  /* TRISC */
  /* c7 c6 c5 c4 c3 c2 c1 c0 */
  /* i  i  o  i  o  o  i  i  */
  TRISC = 0xD3;

  C1OUT = 0; /* needed for RB2 digital operation */
  ANS1 = 0;  /* a/d pin select (none) */
  ANS0 = 0;

  PORTB = 0; /* set all ports to 0 */
  PORTC = 0;

  /* notify power on */
  for(i=0;i<5;i++) {
    RB5 = 1;
    __delay_ms(95);
    RB5 = 0;
    __delay_ms(95);
  }
  
  /* setup timer0 to run at 1khz */
  /* 8Mhz / 4 = 2MHz instruction clock */
  /* 2MHz / 256 = 7812.5 Hz rollover */
  /* 7812.5 Hz / 4 = 1953.125 / 976.5625 Hz ~~ 1000 Hz. */

  /* select timer mode */
  /* T0CS = 0; */
  /* assign prescaler to timer 0 */
  /* PSA = 0; */
  /* assign prescaler */
  /* PS2:0 = 000 : /1 */
  /* PS2:0 = 001 : /2 */
  /* PS2:0 = 010 : /4 */
  /* PS2:0 = 011 : /8 */
  OPTION = 0B10000010;

  /* setup comparitor 2 (used for high voltage source) */
  /* comparitor output not placed on output pin */
  C2OUTEN = 1;
  /* polarity is not inverted */
  C2POL = 1;
  /* positive reference is c2in+ */
  C2PREF2 = 1; 
  /* comparitor enabled */
  C2ON = 1;
  /* negative reference is internal cvref */
  C2NREF = 0;
  /* positive reference is c2in+ pin (ignore c2pref2) */
  C2PREF1 = 1;
  /* no wakeup on compare */
  C2WU = 1;

  /* enable internal voltage reference for comparitor 2 */
  VREN = 1;
  /* disable output of voltage reference */
  VROE = 0;
  /* 35 * 10/(10+100) = 3.181818 */
  /* vdd/4 + (vr/32)*vdd =
     5/4+(13/32)*5 = 3.28125 */
  /* select high range */
  VRR = 0;
  /* VR3:0 = 13 */
  VR3 = 1;
  VR2 = 1;
  VR1 = 0;
  VR0 = 1;

  /* disable analog to digital converter */
  ADON = 0;

  /* turn on LED to indicate ready operation */
  RB5 = 1;

  /* enable low voltage source */
  RB2 = 1;

  /* start shutters in opposite configuration */
  RC3 = 1;
  RC5 = 0;

  /* allow lv power to settle */
  __delay_ms(10);
  RB5 = 0;
  __delay_ms(20);
  RB5 = 1;

  i = 0;
  /* loop forever */
  for(;;) {

    /* rate limit updates */
    if(i++ > 3) {
      i = 0;
      if(C2OUT == 1) {
    	/* hv is too high, reduce duty cycle */
    	if(hv_duty>0) hv_duty--;
      } else {
    	/* hv is too low, increase duty cycle */
    	if(hv_duty<4) hv_duty++;
      }

    } else { /* if not handling an update, handle switch */

      if(RC4 == 0) {  /* switch pressed, toggle */
	if(pol_switch_debounce == 0 && pol_last == 1) {
	  pol_last = 0;
	  pol_switch_debounce = 1;

	  PORTC ^= 0b00101000;

	}
	if(dcnt++==255) { /* increment delay if button held */
	  delay+=2;       /* for some time */
	}
      } else {  /* begin waiting debounce interval before */
	dcnt=0; /* reading switch again */
	if(pol_switch_debounce > 0)
	  pol_switch_debounce++;
	else
	  pol_last = 1;
      }

    }

    /* pwm will be slightly inaccurate because of above checks */
    for(k=0;k<5;k++) {
      RC2 = (hv_duty > k);

      q = RC1;
      
      /* uncomment this section to flip the shutters 
       * every transition */
      
      /* if(q != vsync_last) { /\* vsync toggle *\/ */
      /*   vsync_last = q; */
      /*   trig = 1; */
      /* } */
      
      /* uncomment this section to flip the shutters 
       * on the positive edge */
      
      if(q != vsync_last && q == 1) { /* vsync toggle */
	trig = 1;
      }
      vsync_last = q;

      if(trig) { /* when triggered, flip shutters after a delay */
      	if(trig++>delay) {
      	  PORTC ^= 0b00101000;
      	  RB5 = !RB5;
      	  trig=0;
      	}
      }

    }
    
    RB4 = (TMR0 >> 7); /* 1KHz output (msb of timer) */
  }

}

