#include <Mega32.h>
#include <stdio.h>
#include <delay.h>
#include <stdlib.h>

#define LCDwidth 16
#define t1 1
#define t2 40 
#define t3 120
#define MAX_STEP 10   
#define X_SCALE 0
#define Y_SCALE 0
#define HIGH_TIME 7
#define STORAGE 500



void initialize(void);
void get_cmd_init(void); 
void mouse_lookup(void);  
void step_motors(void); 
void draw(void);    
void debounce(void);     
void res_pos(void);
void teach(void); 
void playback(void);


//array for storing the movement by the mouse during recording
char rec_x[STORAGE];
char rec_y[STORAGE];

                   
char mouse_in[3];                         //input from the mouse
char mouse_in_byte;                       //individual byte from the mouse
int time1, time2, time3;            //timer variables
char debug_ctr;                          
char cmd_rdy;
char byte_ctr;
char lb,rb;                               //for left and right mouse buttons                     
signed int xin, yin;  
char x_dir, x_amt, y_dir, y_amt;   
char step_state = 0;        
char step_ctr = 0; 
int dx, dy;
signed char frac;                   //line drawing algorithm variable  
char done_step;
char step_ready;
char yscale, xscale;  
signed char xdif, ydif;     
char xdir, ydir;
char d_state = 0;                   //debouncer state variable
char res_flag = 0;                        //reset mode flag
char teach_flag = 0;                //record/teach mode flag
char res_state = 0;                 //reset state variable                          
char res_ctr=0;   
char step_size=0;                   //step size variable
char let_ctr=0;
int rec_ctr;  
char play=0;    
int play_ctr = 0;  
char nobut;





//timer0 ISR                                                                                  
interrupt[TIM0_COMP] void cool(void)
{
 
    if (time1) time1--; 
    if (time2) time2--;
    if (time3) time3--;


}



void main(void)
{
        initialize();
        while(1) {
           
            //received 1 byte from the mouse
            if (cmd_rdy == 1)
            {    
                  cmd_rdy = 0;
                 
                  mouse_lookup();
                       
           }
   
                       
            if (done_step)
                  {
                        draw();
                        done_step = 0;
                  }    
                 
                  if (time1 == 0){
                        step_motors();
                        time1 = t1;
                  }    
                 
                  if(time3 ==0){    //interrupt at 30ms
                        time3 = t3;
                        debounce(); //call debounce state machine
                        if (res_flag)
                              res_pos();
                        if (play)
                              playback();
                  }
                 
                  PORTC = ~teach_flag;
                   
                         
         }
}


void get_cmd_init(void) {
         cmd_rdy=0;
         UCSRB.7=1;

}


interrupt[USART_RXC]void interface(void)  {

      mouse_in[byte_ctr] = UDR;    //read in value

      if ((mouse_in[0] & 0x40) >> 6) //check for first byte in packet
            byte_ctr++;
      else byte_ctr = 0;
     
      if (byte_ctr == 3){     //packet complete
        cmd_rdy = 1;
        byte_ctr = 0;        
      }   
     
  
     
  
     

   
}

void initialize(void)
{
           byte_ctr = 0;
        
         DDRB = 0xff;

                  DDRA = 0;
                  DDRC = 0xff;
                 
         TCCR0 = 0b00001011;
         OCR0 = 62;     //set PIT to .25ms
        
         UCSRB = 0x10; 
       
         UBRRL = 0x40;  //set baud rate to 1200
         UBRRH = UBRRH | 0x03;
         led = UBRRH; 
        
         UCSRC = 0b10000110;
  
         TIMSK = 0b00000010;

         done_step = 1;  
         step_ready = 0;
        
         PORTB = 0xff;
        
         xscale = X_SCALE;
         yscale = Y_SCALE;
         
         get_cmd_init();
 
         led = 0;
                     
            time1 = 0;
            debug_ctr = 0;   
            cmd_rdy = 0;  
            byte_ctr = 0; 
                         
            xdif = 0;
            ydif = 0;      
            rec_ctr = 0;
                           
            byte_trash = 0;
            time1 = t1;
            //enable interrupts
         #asm
                 sei
         #endasm  
        
}
 

void mouse_lookup(void)
{
     
      lb =  (mouse_in[0] >> 5) & 1;       //check button position
      rb = (mouse_in[0] >> 4) & 1;
 
                  yin = ((mouse_in[0] << 4) & 0xc0) | ((mouse_in[2] & 0x3f)); //decode packet
                  xin = ((mouse_in[0] << 6) & 0xc0) | ((mouse_in[1] & 0x3f));
       
     if (rb ==1) xin = 0;    //disable movement if button is depressed
     if (lb ==1) yin = 0;
    
    
      xdif =xin;
      ydif =yin; 
     
      done_step = 1; 
     
      if (teach_flag){  //save data if recording
                  rec_x[rec_ctr] = xdif;
                  rec_y[rec_ctr] = ydif;
                  rec_ctr++;
           
                  if (rec_ctr == STORAGE) //check for end of array
                        teach_flag = 0;         //stop recording
            }    
     
             
} 

//step each motor 1 step in either direction
//dir = 1     step up or right
//dir = 0     step left or down
// clk is the step clock (1 for step and 0 for no step)
void step_motors(void)
{

      //PORTB output is setup as such:
      // bit   7      6      5      4      3      2      1      0
      //                           size  x_clk  y_clk  x_dir  y_dir
     
     
      switch(step_state){
            
            case 0:
                  PORTB = 0; 
                  if (step_ready == 1) {
                        step_state = 1;
                        step_ready = 0;  
                       
                        //setup variables for Line algorithm
                  dx = x_amt << 1;
                        dy = y_amt << 1;
                  }

            break;
           
            case 1:
                        if (dx > dy)      //if slope is less than 1
                        {
                                    frac = dy - (dx>>1);
                                    step_state = 2;  
                        }
                        else
                        {
                              step_state = 6;    
                              frac = dx - (dy>>1);
                        }
                  break;                
                 
                  case 2:

                              if (x_amt > 0)
                              {
                                    //setup direction bits
                                    PORTB = (step_size<<4) | (ydir) | (xdir << 1);
                                    step_state = 3;
                              }
                              else
                              {
                                    step_state = 0;
                              }
                  break;
                 
                  case 3:
                          step_ctr = 0;
                        if (frac >= 0)
                        {
                              step_state = 5;
                        }
                        else step_state = 4;
                       
                        frac = frac + dy;
                        x_amt--;
                  break;
                                         
                  case 4:
                        step_ctr++;
                        PORTB =  (step_size<<4) | ydir | (xdir << 1) | (0x08);            //only step the x motor
                        if (step_ctr == HIGH_TIME)                      //do this case 7 times to obey clock reqr
                              step_state = 2;
                  break;
                 
                  case 5:
                        step_ctr++;
                        PORTB =  (step_size<<4) | ydir | (xdir << 1) | (0x0c);      //step both the x and y motor       
                        if (step_ctr == HIGH_TIME)                      //do this case 7 times to obey clock reqr
                              step_state = 2;
                  break;  
                        
                 
                  case 6:
                        PORTB = 0;      
                        if (y_amt > 0)
                              {
                                    //setup direction bits
                                    PORTB = (step_size<<4) | ydir | (xdir << 1);
                                    step_state = 7;
                              }
                              else
                              {
                                    step_state = 0;  
                              }
                  break;
                 
                  case 7:
                          step_ctr = 0;
                        if (frac >= 0)
                        {
                              step_state = 9;
                        }
                        else step_state = 8;
                       
                        frac = frac + dx;
                        y_amt--;
                  break;
                                         
                  case 8:
                        step_ctr++;
                        PORTB =  (step_size<<4) | ydir | (xdir << 1) | (0x04);            //only step the y motor
                        if (step_ctr == HIGH_TIME)                            //do this case 7 times to obey clock reqr
                              step_state = 6;
                  break;
                 
                  case 9:
                        step_ctr++;
                        PORTB =  (step_size<<4) | ydir | (xdir << 1) | (0x0c);      //step both the x and y motor       
                        if (step_ctr == HIGH_TIME)                                  //do this case 7 times to obey clock reqr
                              step_state = 6;
                  break;
      }
} 

void draw(void)
{
      //do calculation for x and y amount and direction
      //set cmd_ready = 1 to start the motors stepping
      //checks two's complement and calculates magnitude
      if (xdif < 0)
      {
            xdif = ~xdif;
            xdif++;
            xdir = 0;
                  }
      else
            {
                        xdir = 1;
            }
      if (ydif < 0)
      {
            ydir = 0;
            ydif = ~ydif;
            ydif++;
      }
      else  ydir = 1;
     
      x_amt = xdif >> 1;
      y_amt = ydif >> 1;
 
      if ((x_amt + y_amt) > (25) )
      step_size = 0;
   else step_size = 1; 
  
      //set max steps at anytime to be 10
      //since stepping at 444 Hz, each step takes ~2.25ms
      //10 steps take 22ms which would be the lowest resolution of the mouse updating
      if (x_amt > MAX_STEP){
            x_amt = MAX_STEP;  
            }
      if (y_amt > MAX_STEP)  {
            y_amt = MAX_STEP;
      }  
     
      step_ready = 1;
}

void debounce(void)     //state machine for 3 button debouncing
{
         switch(d_state){
        
            case 0:
                  if (~PINA == 1)
                        d_state = 1;
                 
                  if (~PINA == 2)
                        d_state = 4;
                       
                  if (~PINA == 4)
                        d_state = 7;           
            break;
                              
            case 1:
                  if (~PINA == 1)
                        d_state = 2;
                  else d_state = 0;
            break;
           
            case 2:
                  if (~PINA == 1){
                        res_flag = 1;
                        d_state = 2;
                  }
                  else d_state = 3;
            break;
           
            case 3:
                  if (~PINA == 1)
                        d_state = 2;
                  else {
                        d_state = 0;
                        }
            break;
           
            case 4:
                  if (~PINA == 2)
                        d_state = 5;
                  else d_state = 0;
            break;
           
            case 5:
                  if (~PINA == 2){
                       
                 
                        d_state = 5;
                  }
                  else d_state = 6;
            break;
           
            case 6:
                  if (~PINA == 2)
                        d_state = 5;
                  else
                  {
                        d_state = 0;
                        teach_flag = teach_flag ^ 1;
                       
                             
                        if (teach_flag == 1)
                              rec_ctr = 0;
                  }
            break;
           
            case 7:
                  if (~PINA == 4)
                        d_state = 8;
                  else d_state = 0;
            break;
           
            case 8:
                  if (~PINA == 4){
                        d_state = 8;
                        play = 1;
                  }
                  else d_state = 9;
            break;
           
            case 9:
                  if (~PINA == 4)
                        d_state = 8;
                  else
                  {
                        d_state = 0;
                  }
            break;
         }
}


void res_pos(void)      //state machine to center stylus
{

      UCSRB.7 = 0;      //disable interrupts
  
  switch(res_state)
  {
            case 0:     //move stylus to bottom
                  xdif = 0;
                  ydif = 60;
                  done_step = 1;
                  res_ctr++;
                  if (res_ctr>=80)   {
                        res_ctr = 0;
                        res_state = 1;
                  }
            break;
           
            case 1:
                  xdif = 0;   //move stylus up a bit from bottom
                  ydif = -60;
                  done_step = 1;
                  res_ctr++;
                  if (res_ctr>=2)   {
                        res_ctr = 0;
                        res_state = 2;
                  }
            break;
           
            case 2:
                  xdif = 60;  //move to far left
                  ydif = 0;
                  done_step = 1;
                  res_ctr++;
                  if (res_ctr>=160)   {
                        res_ctr = 0;
                        res_state = 3;
                  }
            break;
           
            case 3:
                  xdif = -60; //move to be centered horizontally
                  ydif = 0;
                  done_step = 1;
                  res_ctr++;
                  if (res_ctr>=50)   {
                        res_ctr = 0;
                        res_state = 4;
                  }
            break;     
           
            case 4:
                  xdif = 0;
                  ydif = -60; //move to be centered vertically
                  done_step = 1;
                  res_ctr++;
                  if (res_ctr>=40)   {
                        res_ctr = 0;
                        res_state = 0;
                        res_flag = 0;
                        get_cmd_init();
                  }
            break;     
  }
                 

}

void playback(void)
{
            UCSRB.7 = 0;      //disable mouse
           
            xdif = rec_x[play_ctr]; //load value from array
            ydif = rec_y[play_ctr];
            done_step = 1;
           
            play_ctr++;
           
            if(play_ctr == rec_ctr)  {    //check for end of record
                  play = 0;                     //end playback
                  play_ctr = 0;
                  get_cmd_init();               //reset variables
            }
}