/********************************************************************************
 *    Description: ECE331 term project - Cable box IR transmitter.
 *    Source:      project_main.c
 *    Date:        11-14-2006
 *    Author:      Eric Lindberg, Andrew Toth
 ********************************************************************************/

#include "project_main.h"   //include the header file
#include "String.h"         //we use strcmp() from the string library



//These are the IR codes --
// A '1' represents of a short high, then long low signal
// A '0' represents of a short high, then short low signal
const ulong number_codes[10] = {
  0b0000000000000000,   //0
  0b1000000000001111,   //1
  0b0100000000000111,   //2
  0b1100000000001011,   //3
  0b0010000000000011,   //4                 
  0b1010000000001101,   //5
  0b0110000000000101,   //6
  0b1110000000001001,   //7
  0b0001000000000001,   //8
  0b1001000000001110    //9
};
const ulong power_code = 0b0101000000000110;
const ulong exit_code =  0b0100100000001011;
const ulong chup_code =  0b1101000000001010;
const ulong chdown_code =0b0011000000000010;


int hours,minutes,seconds,fiftyms;      //for our clock;
int progchannel, proghour, progminute;  //for a scheduled channel change


/****************************************************************************
 * Main routine.
 *
 * Takes in commands over serial, and sends IR signals out over PT5 
 *  according to the received command. Commands supported at this time 
 *  include 'channel ###', 'power', 'chup', 'chdown', 'exit', 'time' and 
 *  'prog'. 
 ***************************************************************************/
void main ()
{
	SEI();								             // Disable Interrupts
	SetLedindicator();
	CLI();                             // enable I bit interrupts


  SetClockSpeed();
  busclk = oscclk/2;					       // Set Bus Freq. = (1/2) * Oscillator Freq.
  (void) InitSCI(BaudRate);				   // Initialize SCI module
 	(void) printf("ECE331 Fall 06\n\r");
  (void) printf("Eric Lindberg and Andrew Toth\n\r");
  (void) printf("Term Project\n\r");


  //Setup pwm output carrier wave
  Pwm.pwmdty[4].byte = 0xff;         //We set duty to 0xff here to drive the output
                                     // low initially. To enable the output, just 
                                     // set duty to the correct value. That way, we 
                                     // get clean square waves. 
  Pwm.pwmprclk.bit.pcka = 0b010;     //Use bus clock/4 to drive clka
  Pwm.pwmper[4].byte = CARRIER_PER;  //Set the period
  Pim.modrr.bit.modrr4 = 1;          //Enable pwm output on T4
  Pwm.pwme.bit.pwme4 = 1;

  
  //Setup timers
  Tim0.tios.bit.ios1 = 1;            //Enable output compare on ch1
  Tim0.tios.bit.ios2 = 1;            //Enable output compare on ch2
  Tim0.tios.bit.ios3 = 1;            //Output compare for the real-time clock
  Tim0.tie.bit.c1i = 0;              //Disable interrupt on ch1
  Tim0.tie.bit.c2i = 0;              //Disable interrupt on ch2


  //Intialize the real-time clock
  (void) printf("please set the 24hr time (hh:mm:ss):\r\n");
  scanf("%d:%d:%d", &hours, &minutes, &seconds);
  printf("\r\n");
  printf("\r\n");
  printf("Commands include [power, channel ###, exit, chup, chdown, time]\r\n");
  proghour = -1;                    //Disable the programmed channel changes
  Tim0.tie.bit.c3i = 1;             //Enable clock interrupt


  //Main loop
  for (;;) {
    char n[16];
    int chan,t;
    int d1,d2,d3;
    chan = -1;                      //Reset the channel input, in case it is not scanned correctly
    scanf("%s %d",&n[0],&chan);     //Scan for input commands over serial
    if(strcmp(&n[0],"power") == 0) {
      //Send the 'power' command over IR
      (void)printf("\n\rSending Power Button\n\r");
      send_ir_signal('p');
    } else if (strcmp(&n[0], "exit") == 0) {
      //Send the 'exit' command over IR
      printf("\r\nSending exit button\r\n");
      send_ir_signal('e');
    } else if (strcmp(&n[0], "chup") == 0) {
      //Send the 'channel up' command over IR
      printf("\r\nSending channel up button\r\n");
      send_ir_signal('u');
    } else if (strcmp(&n[0], "chdown") == 0) {
      printf("\r\nSending channel down button\r\n");
      //Send the 'channel down' command over IR
      send_ir_signal('d');
    } else if(strcmp(&n[0],"channel") == 0) {
      if(chan >= 0 && chan <= 999){
        //Change the channel to the number specified
        (void)printf("\n\rChanging to channel %d\n\r", chan);
        change_to_channel(chan);
      } else {
        //Invalid channel input
        (void)printf("\n\rInvalid Channel Number %d Try again\n\r", chan);
      }
    } else if (strcmp(&n[0], "time") == 0) {
      //Display the time of the real-time clock over serial
      printf("The current time is:  %d:%d:%d\r\n", hours, minutes, seconds);
    } else if (strcmp(&n[0], "prog") == 0) {
      //We are programming a time to change the channel
      printf("\r\nEnter time for channel change (hh:mm): ");
      scanf("%d:%d", &proghour, &progminute);
      printf("\r\nEnter channel to change to at that time: ");
      scanf("%d", &progchannel);
      printf("\r\nProgrammed.\r\n");
    } else {
      (void)printf("\n\rInvalid Command\n\r");
      chan = -1;                    //Clear the channel value
      for(t=0;t<16;n[t++]=0);       //Also, reset the command buffer
    }
  }
	 
  //This should never be reached
  while(1) {};
}

/****************************************************************************
 * Changes the cable box to a certain decimal channel number.
 * 
 * The function splits the number into three digits and sends codes for each
 *  number, with delays between the digits. 
 *
 * @param chan The channel number
 ***************************************************************************/
void change_to_channel(int chan) {
  int d1,d2,d3,t;
  d1 = chan % 10;
  d2 = (chan / 10) % 10;
  d3 = (chan / 100) % 10;
  send_ir_signal(d3);
  for(t=0;t<NUMBER_OF_50MS_DELAYS_BETWEEN_DIGITS;t++) {
    delay_50_ms();
  }
  send_ir_signal(d2);
  for(t=0;t<NUMBER_OF_50MS_DELAYS_BETWEEN_DIGITS;t++) {
   delay_50_ms();
  }
  send_ir_signal(d1);
}

/****************************************************************************
 * ISR that updates the counters in the real-time clock. 
 * 
 * This function is designed to be called every 50ms. It updates the 
 *  variables fiftyms, seconds, minutes, and hours. It also checks the 
 *  programmed channel change time every minute, and triggers that channel
 *  change if the appropriate time has arrived. 
 ***************************************************************************/
void clock_isr() {
  Tim0.tflg1.bit.c3f = 1;  //Clear the flag;
  Tim0.tc[3] += FIFTY_MS;  //Re-set the timer for another 50ms interrupt
  fiftyms ++;
  if (fiftyms >= FIFTY_MS_IN_A_SECOND) {
    fiftyms %= FIFTY_MS_IN_A_SECOND;
    seconds ++;
    if (seconds >= SECONDS_IN_A_MINUTE) {
      seconds %= SECONDS_IN_A_MINUTE;
      minutes ++;
      if (minutes >= MINUTES_IN_AN_HOUR) {
        minutes %= MINUTES_IN_AN_HOUR;
        hours ++;
        if (hours >= HOURS_IN_A_DAY) {
          hours %= HOURS_IN_A_DAY;
        }
      }
      //Every minute, check our programmed channel change variables
      if (proghour == hours && progminute == minutes) {
        //Time to initiate channel change
        change_to_channel(progchannel);
        proghour = PROG_DISABLED; 
      }
    }
  }
  
}

/****************************************************************************
 * Sends out a single IR command. 
 *
 * This function is essentially a wrapper around send_code().
 * 
 * @param s A character/integer corresponding to the code to be sent. 
 *  Currently supported are the digits 0-9, 'p' for power, 'e' for exit, 'u'
 *  for channel up, and 'd' for channel down. 
 ***************************************************************************/
void send_ir_signal(char s) {
  ulong code;

  switch(s) {
  case 0x0:
  case 0x1:
  case 0x2:
  case 0x3:
  case 0x4:
  case 0x5:
  case 0x6:
  case 0x7:
  case 0x8:          
  case 0x9:
    code = number_codes[s];
    send_code(code);
    break;
  case 'p':
    send_code(power_code);
    break;
  case 'e':
    send_code(exit_code);
    break;
  case 'u':
    send_code(chup_code);
    break;
  case 'd':
    send_code(chdown_code);
    break;
  }
}

/****************************************************************************
 * Utility function to delay program execution by 50ms.  
 * 
 * Makes use of timer channel 2. 
 ***************************************************************************/
void delay_50_ms() {
  Tim0.tflg1.bit.c2f = 1;  //clear the flag;
  Tim0.tc[2] = Tim0.tcnt.word + FIFTY_MS; //set the compare register
  while (!Tim0.tflg1.bit.c2f);
}

/****************************************************************************
 * Sends a code over IR given a 16-bit binary sequence.
 * 
 * This function makes use of the output_xxx function listed below to 
 *  generate the correct series of on/off transitions of the carrier wave, 
 *  emulating a remote control. 
 *
 * @param ir_code The 16-bit IR code -- one of the xxx_code definitions 
 *                 from the header file. 
 ***************************************************************************/
void send_code(ulong ir_code) {
  int n;
  //Start our second timer to generate the later sync pulses
  Tim0.tflg1.bit.c2f = 1; 
  Tim0.tc[2] = Tim0.tcnt.word + SYNC_TIME_1;

  output_init_pulse();
  for (n=0;n<BITS_IN_CODE;n++) {
    if (ir_code & FIRST_BIT_MASK) {
      output_one_bit();
    } else {
      output_zero_bit();
    }
    ir_code = ir_code << 1;
  }
  CARRIER_ON;
  //Keep the carrier on for SHORT_HIGH amount of time.
  Tim0.tflg1.bit.c1f = 1;  
  Tim0.tc[1] += SHORT_HIGH; 
  while (!Tim0.tflg1.bit.c1f);
  CARRIER_OFF;
  
  //Wait for our sync timer to hit
  while (!Tim0.tflg1.bit.c2f);
  //Set it again, and wait
  Tim0.tflg1.bit.c2f = 1;  
  Tim0.tc[2] += SYNC_TIME_2;
  while (!Tim0.tflg1.bit.c2f);
  
  //set up the next one
  Tim0.tc[2] += SYNC_TIME_1;

  //now we are ready for a sync pulse
  output_sync_pulse();

  //Wait for the sync timer to hit again
  while (!Tim0.tflg1.bit.c2f);
  //set it again, and wait
  Tim0.tflg1.bit.c2f = 1; 
  Tim0.tc[2] += SYNC_TIME_2;
  while (!Tim0.tflg1.bit.c2f);
  
  //Send the second sync pulse
  output_sync_pulse();
}

/****************************************************************************
 * Sends out a sync pulse, consisting of a long high, short low, and short
 *  high signal. 
 * 
 * Sync pulses, as we like to call them, appear exactly .1 sec after the 
 *  first signal from the remote control and repeat two or three times. We 
 *  assume they are used to synchronize the timing of the bits sent in the
 *  coded signal. We send two of them in send_code(), which seems to work. 
 ***************************************************************************/
void output_sync_pulse() {
  CARRIER_ON;
  Tim0.tflg1.bit.c1f = 1;  //clear the flag;
  Tim0.tc[1] = Tim0.tcnt.word + LONG_HIGH; //set the compare register
  while (!Tim0.tflg1.bit.c1f);
  CARRIER_OFF;
  Tim0.tflg1.bit.c1f = 1; //clear the flag
  Tim0.tc[1] += SHORT_LOW;
  while (!Tim0.tflg1.bit.c1f);
  CARRIER_ON;
  Tim0.tflg1.bit.c1f = 1; //clear the flag
  Tim0.tc[1] += SHORT_HIGH;
  while (!Tim0.tflg1.bit.c1f);
  CARRIER_OFF;
}

/****************************************************************************
 * Sends out an init pulse, the first thing sent. It consists of a long high
 *  'on' period followed by a long low 'off' period.
 ***************************************************************************/
void output_init_pulse() {
  CARRIER_ON;
  Tim0.tflg1.bit.c1f = 1;  //clear the flag;
  Tim0.tc[1] = Tim0.tcnt.word + LONG_HIGH; //set the compare register
  while (!Tim0.tflg1.bit.c1f);
  CARRIER_OFF;
  Tim0.tflg1.bit.c1f = 1; //clear the flag
  Tim0.tc[1] += LONG_LOW;
  while (!Tim0.tflg1.bit.c1f);
}

/****************************************************************************
 * Sends out a coded 'zero' bit. It consists of a short high
 *  'on' period followed by a short low 'off' period.
 ***************************************************************************/
void output_zero_bit() {
  CARRIER_ON;
  Tim0.tflg1.bit.c1f = 1;  //clear the flag;
  Tim0.tc[1] += SHORT_HIGH; //set the compare register
  while (!Tim0.tflg1.bit.c1f);
  
  CARRIER_OFF;
  Tim0.tflg1.bit.c1f = 1; //clear the flag
  Tim0.tc[1] += SHORT_LOW;
  while (!Tim0.tflg1.bit.c1f);
}

/****************************************************************************
 * Sends out a coded 'one' bit. It consists of a short high
 *  'on' period followed by a long low 'off' period.
 ***************************************************************************/
void output_one_bit() {
  CARRIER_ON;
  Tim0.tflg1.bit.c1f = 1;  //clear the flag;
  Tim0.tc[1] += SHORT_HIGH; //set the compare register
  while (!Tim0.tflg1.bit.c1f);
  
  CARRIER_OFF;
  Tim0.tflg1.bit.c1f = 1; //clear the flag
  Tim0.tc[1] += LONG_LOW;
  while (!Tim0.tflg1.bit.c1f);
}


/****************************************************************************
 * Sets the clock speed to 24MHz.
 ***************************************************************************/
void SetClockSpeed() {
  Crg.clksel.byte &=~PLLSEL;            // disconnect PLL
  Crg.pllctl.byte |=PLLON;              // turn on PLL
  Crg.synr.byte = 2;                    // set PLL multiplier
  Crg.refdv.byte = 0; 
	while(!(Crg.crgflg.byte & LOCK)){ };  // wait for PLL to lock in
	Crg.clksel.byte |=PLLSEL;             // connect PLL
}

