/*
 * Z_Sync_Strb.c
 *
 * Created: 5/22/2012 9:58:57 PM
 *  Author: Cythia Mavros
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define output(directions,pin) (directions |= pin)   // set port pins direction for output
#define input(directions,pin) (directions &= (~pin)) // set port pins direction for input  
#define set(port,pin) (port |= pin)                  // set port pin
#define clear(port,pin) (port &= (~pin))             // clear port pin
#define pin_test(pins,pin) (pins & pin)              // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit))       // test for bit set

#define STROBE_pin (1 << PB1)
#define SPEED_CLK_pin (1 << PB0)
#define HALL_SWITCH_pin (1 << PB2)
#define OUTPUT_port PORTB
#define INPUT_port PORTB
#define INPUT_pins PINB
#define ACTIVE_direction DDRB

//Implements two simple FSM for two independent channels
//PB2 is the trigger for the strobe FSM
//PB1 is the output for the strobe FSM
//PB0 is the output for the speed clock FSM 

enum STROBE_STATES {TRG_START, TRG_WAIT, TRG_LOW, TRG_HIGH, TRG_DWELL}; 
enum SPEED_CLK_STATES {CLK_STOP, CLK_LOW, CLK_HIGH};

int strobe_low_time = 9 * 40;                 // Delay 9 ms before...
int strobe_high_time = 1 * 40;                // driving strobe on for 1 ms
int strobe_dwell_time = 30 * 40;              // then ignore input for 30 ms
                                              //    to stop double triggers on the same magnet
// Approximate 400 Hz 50% duty cycle clock to idle spindle
int speed_clk_time_idle = 40;                 // Stay high/ low for 1.25 ms

// Approximate 384 Hz 50% duty cyclce clock to power spindle 
int speed_clk_time_pwr = 42;                  // Stay high/ low for 1.3 ms

// Upper and lower bounds for 60 rpm and start speed threshold
int trigger_start_speed = 60 * 40;            // 60 ms start speed threshold 56.4 RPM
int trigger_low_speed = 57 * 40;              // 57 ms low speed threshold 58.5 RPM
int trigger_high_speed = 54 * 40;             // 54 ms high speed threshold 61.7 RPM

volatile int strobe_state = TRG_START;        // Initialize strobe FSM state to WAIT 
volatile int speed_clk_state = CLK_STOP;      // Initialize clock FSM state to STOP
volatile int strobe_timer = 1;                // Initalize strobe timer
volatile int speed_clk_time = 0;              // Initalize clock time
volatile int speed_clk_timer = 1;             // Initalize clock timer
volatile int trigger_time = 0;                // Used to measure time between triggers
          
int main(void) 
{ 

   output(ACTIVE_direction, STROBE_pin);      // Set the LED strobe pin for output
   output(ACTIVE_direction, SPEED_CLK_pin);   // Set the speed clock pin for output
   input(ACTIVE_direction, HALL_SWITCH_pin);  // Set the hall switch pin for input
   clear(OUTPUT_port, STROBE_pin);            // Set the LED strobe pin low
   clear(OUTPUT_port, SPEED_CLK_pin);         // Set the speed clock pin low
   set(OUTPUT_port, HALL_SWITCH_pin);         // Turn on the pull up resister
   OCR1A = 199;                               // Set interrupt for every 25 microseconds  
   TCCR1 |= (1 << CTC1 | 1 << CS11);          // Fcpu/2 prescaling and CTC mode
   TIMSK |= (1 << OCIE1A);                    // Turn on count up timer interrupt on
   sei(); 

   speed_clk_time = speed_clk_time_pwr;     // Set the intial spindle speed to start

   while(1) 
   {      
       // run forever, timer interrupt driven system
   } 
   return 1; 
} 

ISR(TIMER1_COMPA_vect) 
{ 
    
   //interrupts should be fast 

   strobe_timer--;
   trigger_time++;
    
   switch (strobe_state) {

      case TRG_START: //waiting for the spindle to come up to speed
	 strobe_timer = 1;
		 
         if(!(pin_test(INPUT_pins, HALL_SWITCH_pin))) { // got trigger now if...       

            if(trigger_time < trigger_start_speed) {    // spindle is up to start speed
               strobe_state = TRG_WAIT;                 // move on to wait state
               speed_clk_state = CLK_LOW;               // start the clock FSM
               speed_clk_time = speed_clk_time_pwr;     // and drop clk to power speed
            }

            trigger_time = 0;
         }

      break;    

      case TRG_WAIT: //looking for a hall switch trigger
	 strobe_timer = 1;
		 
         if(!(pin_test(INPUT_pins, HALL_SWITCH_pin))) {       
            strobe_timer = strobe_low_time;    
            strobe_state = TRG_LOW;    

            if(trigger_time > trigger_low_speed) {
               speed_clk_time = speed_clk_time_pwr;
            }
            else if(trigger_time < trigger_high_speed) {
               speed_clk_time = speed_clk_time_idle;
            }

            trigger_time = 0;
         } 
		 
      break; 

      case TRG_LOW: //waiting out the low period
	  
         if(!strobe_timer) {
            strobe_timer = strobe_high_time; 
            set(OUTPUT_port, STROBE_pin);
            strobe_state = TRG_HIGH; 
         }
		 
      break; 

      case TRG_HIGH: //waiting out the high period 
	  
         if(!strobe_timer) {
            strobe_timer = strobe_dwell_time; 
            clear(OUTPUT_port, STROBE_pin);
            strobe_state = TRG_DWELL; 
         } 
		 
      break; 

	case TRG_DWELL: //ignore input period
	   if(!strobe_timer){
            strobe_timer = 1; 
            strobe_state = TRG_WAIT; 
           }

      break;
		
   } 

   speed_clk_timer--;
   
   switch (speed_clk_state) {

      case CLK_STOP: // waiting trigger FSM to start the machine 
         speed_clk_timer = 1;
      break; 
         
      case CLK_LOW: // waiting out the low period
	  
         if(!speed_clk_timer) {
            speed_clk_timer = speed_clk_time; 
            set(OUTPUT_port, SPEED_CLK_pin);
            speed_clk_state = CLK_HIGH; 
         } 
		 
      break; 
	  
      case CLK_HIGH: // waiting out the high period
         if(!speed_clk_timer) {
            speed_clk_timer = speed_clk_time; 
            clear(OUTPUT_port, SPEED_CLK_pin);
            speed_clk_state = CLK_LOW; 
         } 
		 
      break;
	  
   } 
   
}
