#include "defines.h"
#include "ardustim.h"
#include "enums.h"
#include "comms.h"
#include "storage.h"
#include "user_defaults.h"
#include "wheel_defs.h"
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>   //M.M.

/* Sensistive stuff used in ISR's */
volatile uint8_t fraction = 0;
volatile uint16_t adc0; /* Buttons */  //M.M.
volatile uint16_t adc1; /* POT RPM */  //pot moved to adc1, adc0 now is for buttons
volatile uint32_t oc_remainder = 0;
/* Setting rpm to any value over 0 will enabled sweeping by default */
/* Stuff for handling prescaler changes (small tooth wheels are low RPM) */
volatile uint8_t analog_port = 0;
volatile bool adc0_read_complete = false;
volatile bool adc1_read_complete = false;
volatile bool reset_prescaler = false;
volatile bool normal = true;
volatile bool sweep_reset_prescaler = true; /* Force sweep to reset prescaler value */
volatile bool sweep_lock = false;
volatile uint8_t output_invert_mask = 0x00; /* Don't invert anything */
volatile uint8_t sweep_direction = ASCENDING;
volatile byte total_sweep_stages = 0;
volatile uint8_t sweep_stage = 0;
volatile uint8_t prescaler_bits = 0;
volatile uint8_t last_prescaler_bits = 0;
volatile uint8_t mode = 0;
volatile uint16_t new_OCR1A = 5000; /* sane default */
volatile uint16_t edge_counter = 0;

/* Less sensitive globals */
uint8_t bitshift = 0;
uint16_t sweep_low_rpm = 250;
uint16_t sweep_high_rpm = 4000;
uint16_t sweep_rate = 1;                               

sweep_step *SweepSteps;  /* Global pointer for the sweep steps */

// LCD definitions M.M.
const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

unsigned long previousMillis = 0;
const long interval = 200;  
volatile bool debouce = true;  
volatile bool serial_upd = false; 
 
#define pri_out		12
#define pri_outN	13

#define sec_out		2
#define sec_outN	3

#define sync_out	11


// M.M. representacion en grados puede ser 360 o 720 (segun si tiene CMP o no)
// cuando es 360  Prescaler = (cant flancos) / 120
// cuando es 720  Prescaler = (cant flancos) / 240

wheels Wheels[MAX_WHEELS] = {
#if defined	_HONDA_30DEG
  { honda_30deg_friendly_name, Honda_30deg, 0.1, 12, 360 },
#endif
#if defined	_HONDA_30DEG90
  { honda_30deg_90_friendly_name, Honda_30deg90, 0.1, 12, 360 },
#endif
#if defined	_HONDA_30DEG60
  { honda_30deg_60_friendly_name, Honda_30deg60, 0.1, 12, 360 },
#endif
#if defined	_YAMAHA_60DEG
  { yamaha_60deg_friendly_name, Yamaha_60deg, 0.1, 12, 360 },
#endif
#if defined	_SINGLE40
  { single40_friendly_name, Single40, 0.075, 9, 360 },
#endif

#if defined	_KAWAZX6
  { kawasaki_zx6_friendly_name, KawasakiZX6, 0.6, 72, 360 },
#endif
#if defined	_KAWAZXR250
  { kawasaki_zxr250_friendly_name, KawasakiZXR250, 0.5, 60, 360 },
#endif
#if defined	_SUZIBANDIT
  { suzuki_bandit_friendly_name, SuzukiBandit, 0.6, 72, 360 },
#endif


#if defined	_BMW_BOXER
  { bmwp_friendly_name, bmwp, 0.0667, 8, 360 },
#endif

#if defined	_BMW_BOXER_
  { bmwn_friendly_name, bmwn, 0.0667, 8, 360 },
#endif

#if defined	_HONDA_CG150_
  { honda_cg150_friendly_name, hondacg150, 0.6, 72, 360 },
#endif

#if defined	_DUCATI_
  { Ducati_friendly_name, ducati48_3, 0.8, 96, 360 },
#endif

#if defined	_VIRAGO250_
  { Virago250_friendly_name, virago250, 0.3, 36, 360 },
#endif



//--------------------------------------------------------------
  /* Pointer to friendly name string, pointer to edge array, RPM Scaler, Number of edges in the array, whether the number of edges covers 360 or 720 degrees */
#if defined _DIZZY_FOUR_CYLINDER   
  { dizzy_four_cylinder_friendly_name, dizzy_four_cylinder, 0.03333, 4, 360 },
#endif  
#if defined _DIZZY_SIX_CYLINDER    
  { dizzy_six_cylinder_friendly_name, dizzy_six_cylinder, 0.05, 6, 360 },
#endif  
#if defined _DIZZY_EIGHT_CYLINDER     
  { dizzy_eight_cylinder_friendly_name, dizzy_eight_cylinder, 0.06667, 8, 360 },
#endif  
#if defined _SIXTY_MINUS_TWO         
  { sixty_minus_two_friendly_name, sixty_minus_two, 1.0, 120, 360 },
#endif  
#if defined _SIXTY_MINUS_TWO_WITH_CAM    
  { sixty_minus_two_with_cam_friendly_name, sixty_minus_two_with_cam, 1.0, 240, 720 },
#endif  
#if defined _SIXTY_MINUS_TWO_WITH_HALFMOON_CAM   
  { sixty_minus_two_with_halfmoon_cam_friendly_name, sixty_minus_two_with_halfmoon_cam, 1.0, 240, 720 },
#endif  
#if defined _THIRTY_SIX_MINUS_ONE    
  { thirty_six_minus_one_friendly_name, thirty_six_minus_one, 0.6, 72, 360 },
#endif  
#if defined _TWENTY_FOUR_MINUS_ONE    
  { twenty_four_minus_one_friendly_name, twenty_four_minus_one, 0.5, 48, 360 },
#endif  
#if defined _FOUR_MINUS_ONE_WITH_CAM     
  { four_minus_one_with_cam_friendly_name, four_minus_one_with_cam, 0.06667, 16, 720 },
#endif  
#if defined _EIGHT_MINUS_ONE          
  { eight_minus_one_friendly_name, eight_minus_one, 0.13333, 16, 360 },
#endif  
#if defined _SIX_MINUS_ONE_WITH_CAM    
  { six_minus_one_with_cam_friendly_name, six_minus_one_with_cam, 0.15, 36, 720 },
#endif  
#if defined _TWELVE_MINUS_ONE_WITH_CAM   
  { twelve_minus_one_with_cam_friendly_name, twelve_minus_one_with_cam, 0.6, 144, 720 },
#endif  
#if defined _FOURTY_MINUS_ONE        
  { fourty_minus_one_friendly_name, fourty_minus_one, 0.66667, 80, 360 },
#endif  
#if defined _DIZZY_FOUR_TRIGGER_RETURN     
  { dizzy_four_trigger_return_friendly_name, dizzy_four_trigger_return, 0.15, 9, 720 },
#endif  
#if defined _ODDFIRE_VR                
  { oddfire_vr_friendly_name, oddfire_vr, 0.2, 24, 360 },
#endif  
#if defined _OPTISPARK_LT1            
  { optispark_lt1_friendly_name, optispark_lt1, 3.0, 720, 720 },
#endif  
#if defined _TWELVE_MINUS_THREE       
  { twelve_minus_three_friendly_name, twelve_minus_three, 0.4, 48, 360 },
#endif  
#if defined _THIRTY_SIX_MINUS_TWO_TWO_TWO     
  { thirty_six_minus_two_two_two_friendly_name, thirty_six_minus_two_two_two, 0.6, 72, 360 },
#endif  
#if defined _THIRTY_SIX_MINUS_TWO_TWO_TWO_H6    
  { thirty_six_minus_two_two_two_h6_friendly_name, thirty_six_minus_two_two_two_h6, 0.6, 72, 360 },
#endif  
#if defined _THIRTY_SIX_MINUS_TWO_TWO_TWO_WITH_CAM    
  { thirty_six_minus_two_two_two_with_cam_friendly_name, thirty_six_minus_two_two_two_with_cam, 0.6, 144, 720 },
#endif  
#if defined _FOURTY_TWO_HUNDRED_WHEEL   
  { fourty_two_hundred_wheel_friendly_name, fourty_two_hundred_wheel, 0.6, 72, 360 },
#endif  
#if defined _THIRTY_SIX_MINUS_ONE_WITH_CAM_FE3   
  { thirty_six_minus_one_with_cam_fe3_friendly_name, thirty_six_minus_one_with_cam_fe3, 0.6, 144, 720 },
#endif  
#if defined _SIX_G_SEVENTY_TWO_WITH_CAM    
  { six_g_seventy_two_with_cam_friendly_name, six_g_seventy_two_with_cam, 0.6, 144, 720 },
#endif  
#if defined _BUELL_ODDFIRE_CAM       
  { buell_oddfire_cam_friendly_name, buell_oddfire_cam, 0.33333, 80, 720 },
#endif  
#if defined _GM_LS1_CRANK_AND_CAM      
  { gm_ls1_crank_and_cam_friendly_name, gm_ls1_crank_and_cam, 6.0, 720, 720 },
#endif  
#if defined _LOTUS_THIRTY_SIX_MINUS_ONE_ONE_ONE_ONE     
  { lotus_thirty_six_minus_one_one_one_one_friendly_name, lotus_thirty_six_minus_one_one_one_one, 0.6, 72, 360 },
#endif  
#if defined _HONDA_RC51_WITH_CAM     
  { honda_rc51_with_cam_friendly_name, honda_rc51_with_cam, 0.2, 48, 720 },
#endif  
#if defined _THIRTY_SIX_MINUS_ONE_WITH_SECOND_TRIGGER     
  { thirty_six_minus_one_with_second_trigger_friendly_name, thirty_six_minus_one_with_second_trigger, 0.6, 144, 720 },
#endif  
#if defined _CHRYSLER_NGC_THIRTY_SIX_PLUS_TWO_MINUS_TWO_WITH_NGC4_CAM    
  { chrysler_ngc_thirty_six_plus_two_minus_two_with_ngc4_cam_friendly_name, chrysler_ngc_thirty_six_plus_two_minus_two_with_ngc4_cam, 3.0, 720, 720 },
#endif  
#if defined _CHRYSLER_NGC_THIRTY_SIX_MINUS_TWO_PLUS_TWO_WITH_NGC6_CAM    
  { chrysler_ngc_thirty_six_minus_two_plus_two_with_ngc6_cam_friendly_name, chrysler_ngc_thirty_six_minus_two_plus_two_with_ngc6_cam, 3.0, 720, 720 },
#endif  
#if defined _CHRYSLER_NGC_THIRTY_SIX_MINUS_TWO_PLUS_TWO_WITH_NGC8_CAM    
  { chrysler_ngc_thirty_six_minus_two_plus_two_with_ngc8_cam_friendly_name, chrysler_ngc_thirty_six_minus_two_plus_two_with_ngc8_cam, 3.0, 720, 720 },
#endif  
#if defined _WEBER_IAW_WITH_CAM     
  { weber_iaw_with_cam_friendly_name, weber_iaw_with_cam, 1.2, 144, 720 },
#endif  
#if defined _FIAT_ONE_POINT_EIGHT_SIXTEEN_VALVE_WITH_CAM     
  { fiat_one_point_eight_sixteen_valve_with_cam_friendly_name, fiat_one_point_eight_sixteen_valve_with_cam, 3.0, 720, 720 },
#endif  
#if defined _THREE_SIXTY_NISSAN_CAS    
  { three_sixty_nissan_cas_friendly_name, three_sixty_nissan_cas, 3.0, 720, 720 },
#endif  
#if defined _TWENTY_FOUR_MINUS_TWO_WITH_SECOND_TRIGGER    
  { twenty_four_minus_two_with_second_trigger_friendly_name, twenty_four_minus_two_with_second_trigger, 0.3, 72, 720 },
#endif  
#if defined _YAMAHA_EIGHT_TOOTH_WITH_CAM     
  { yamaha_eight_tooth_with_cam_friendly_name, yamaha_eight_tooth_with_cam, 0.26667, 64, 720 },
#endif  
#if defined _GM_FOUR_TOOTH_WITH_CAM    
  { gm_four_tooth_with_cam_friendly_name, gm_four_tooth_with_cam, 0.06666, 8, 720 },
#endif  
#if defined _GM_SIX_TOOTH_WITH_CAM     
  { gm_six_tooth_with_cam_friendly_name, gm_six_tooth_with_cam, 0.1, 12, 720 },
#endif  
#if defined _GM_EIGHT_TOOTH_WITH_CAM    
  { gm_eight_tooth_with_cam_friendly_name, gm_eight_tooth_with_cam, 0.13333, 16, 720 },
#endif  
#if defined _VOLVO_D12ACD_WITH_CAM    
  { volvo_d12acd_with_cam_friendly_name, volvo_d12acd_with_cam, 4.0, 480, 720 },
#endif  
#if defined _MAZDA_THIRTY_SIX_MINUS_TWO_TWO_TWO_WITH_SIX_TOOTH_CAM   
  { mazda_thirty_six_minus_two_two_two_with_six_tooth_cam_friendly_name, mazda_thirty_six_minus_two_two_two_with_six_tooth_cam, 1.5, 360, 720 },
#endif  
#if defined _MITSUBISH_4g63_4_2    
  { mitsubishi_4g63_4_2_friendly_name, mitsubishi_4g63_4_2, 0.6, 144, 720 },
#endif  
#if defined _AUDI_135_WITH_CAM    
  { audi_135_with_cam_friendly_name, audi_135_with_cam, 1.5, 1080, 720 },
#endif  
#if defined _HONDA_D17_NO_CAM   
  { honda_d17_no_cam_friendly_name, honda_d17_no_cam, 0.6, 144, 720 },
#endif  
#if defined _MAZDA_323_AU    
  { mazda_323_au_friendly_name, mazda_323_au, 1, 30, 720 },
#endif  
#if defined _DAIHATSU_3CYL    
  { daihatsu_3cyl_friendly_name, daihatsu_3cyl, 0.8, 144, 360 },
#endif  
#if defined _MIATA_9905    
  { miata_9905_friendly_name, miata_9905, 0.6, 144, 720 },
#endif  
#if defined _TWELVE_WITH_CAM     
  { twelve_with_cam_friendly_name, twelve_with_cam, 0.6, 144, 720 },
#endif  
#if defined _TWENTY_FOUR_WITH_CAM   
  { twenty_four_with_cam_friendly_name, twelve_with_cam, 0.6, 144, 720 },
#endif  
#if defined _SUBARU_SIX_SEVEN      
  { subaru_six_seven_name_friendly_name, subaru_six_seven, 3.0, 720, 720 },
#endif  
#if defined _GM_7X                 
  { gm_seven_x_friendly_name, gm_seven_x, 1.502, 180, 720 },
#endif  
#if defined _FOUR_TWENTY_A         
  { four_twenty_a_friendly_name, four_twenty_a, 0.6, 144, 720 },
#endif  
#if defined _FORD_ST170            
  { ford_st170_friendly_name, ford_st170, 3.0, 720, 720 },
#endif  
#if defined _MITSUBISHI_3A92       
  { mitsubishi_3A92_friendly_name, mitsubishi_3A92, 0.6, 144, 720 },
#endif  
#if defined _TOYOTA_4AGE_CAS       
  { Toyota_4AGE_CAS_friendly_name, toyota_4AGE_CAS, 0.333, 144, 720 },
#endif  
#if defined _TOYOTA_4AGZE    
  { Toyota_4AGZE_friendly_name, toyota_4AGZE, 0.333, 144, 720 },
#endif  
#if defined _SUZUKI_DRZ400
  { Suzuki_DRZ400_friendly_name, suzuki_DRZ400,0.6, 72, 360},
#endif  
};
/* Initialization */
void setup() {
  serialSetup();
  loadConfig();
  lcd.begin(16, 2);
  lcd.print(" Ardu-Stim LCD  ");
  
  lcd.setCursor(0,1);
  lcd.print("     RAS-77     ");	
  
  delay(2000);
  
  LCD_Update(0);
  LCD_Update(1);

  cli(); // stop interrupts

  /* Configuring TIMER1 (pattern generator) */
  // Set timer1 to generate pulses
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // Set compare register to sane default
  OCR1A = 1000;  /* 8000 RPM (60-2) */

  // Turn on CTC mode
  TCCR1B |= (1 << WGM12); // Normal mode (not PWM)
  // Set prescaler to 1
  TCCR1B |= (1 << CS10); /* Prescaler of 1 */
  // Enable output compare interrupt for timer channel 1 (16 bit)
  TIMSK1 |= (1 << OCIE1A);

  // Set timer2 to run sweeper routine
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  // Set compare register to sane default
  OCR2A = 249;  /* With prescale of x64 gives 1ms tick */

  // Turn on CTC mode
  TCCR2A |= (1 << WGM21); // Normal mode (not PWM)
  // Set prescaler to x64
  TCCR2B |= (1 << CS22); /* Prescaler of 64 */
  // Enable output compare interrupt for timer channel 2
  TIMSK2 |= (1 << OCIE2A);


  /* Configure ADC as per http://www.glennsweeney.com/tutorials/interrupt-driven-analog-conversion-with-an-atmega328p */
  // clear ADLAR in ADMUX (0x7C) to right-adjust the result
  // ADCL will contain lower 8 bits, ADCH upper 2 (in last two bits)
  //ADMUX &= B11011111; //M.M.
  
  // Set REFS1..0 in ADMUX (0x7C) to change reference voltage to the
  // proper source (01)
  //ADMUX |= B01000000;  //M.M.
  
  // Clear MUX3..0 in ADMUX (0x7C) in preparation for setting the analog
  // input
  //ADMUX &= B11110000;  //M.M.
  
  ADMUX = B01000000;  
  
  // Set MUX3..0 in ADMUX (0x7C) to read from AD8 (Internal temp)
  // Do not set above 15! You will overrun other parts of ADMUX. A full
  // list of possible inputs is available in Table 24-4 of the ATMega328
  // datasheet
  // ADMUX |= 8;
  // ADMUX |= B00001000; // Binary equivalent
  
  // Set ADEN in ADCSRA (0x7A) to enable the ADC.
  // Note, this instruction takes 12 ADC clocks to execute
  ADCSRA |= B10000000;
  
  // Set ADATE in ADCSRA (0x7A) to enable auto-triggering.
  ADCSRA |= B00100000;
  
  // Clear ADTS2..0 in ADCSRB (0x7B) to set trigger mode to free running.
  // This means that as soon as an ADC has finished, the next will be
  // immediately started.
  ADCSRB &= B11111000;
  
  // Set the Prescaler to 128 (16000KHz/128 = 125KHz)
  // Above 200KHz 10-bit results are not reliable.
  ADCSRA |= B00000111;
  
  // Set ADIE in ADCSRA (0x7A) to enable the ADC interrupt.
  // Without this, the internal interrupt will not trigger.
  ADCSRA |= B00001000;

//  pinMode(7, OUTPUT); /* Debug pin for Saleae to track sweep ISR execution speed */
  pinMode(8, OUTPUT); /* Primary (crank usually) output */
  pinMode(9, OUTPUT); /* Secondary (cam usually) output */
//  pinMode(10, OUTPUT); /* Knock signal for seank, ony on LS1 pattern, NOT IMPL YET */
  

  
  pinMode(10, INPUT);  /* LCD BL must be input*/
  
  pinMode(pri_out, OUTPUT); /* Primary Out */
  pinMode(pri_outN, OUTPUT); /* Primary Out inverted*/
  pinMode(sec_out, OUTPUT); /* Secondary Out */
  pinMode(sec_outN, OUTPUT); /* Secondary Out inverted*/
  
  pinMode(sync_out, OUTPUT); /* Sync Out */
  
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  pinMode(53, OUTPUT); 
  pinMode(52, OUTPUT); 
#endif

  sei(); // Enable interrupts
  // Set ADSC in ADCSRA (0x7A) to start the ADC conversion
  ADCSRA |= B01000000;
  /* Make sure we are using the DEFAULT RPM on startup */
  reset_new_OCR1A(wanted_rpm); 

} // End setup


//! ADC ISR for alternating between ADC pins 0 and 1
/*!
 * Reads ADC ports 0 and 1 alternately. Port 0 is for buttons , Port 1 is for RPM
 */
 
ISR(ADC_vect){
uint16_t   adc_conv;	
	
  adc_conv = ADCL | (ADCH << 8);	
	
  switch(analog_port)
  {
	  case 0:
		adc0 = adc_conv;
		ADMUX = B01000000;	
		analog_port = 1;
		adc0_read_complete = true;
	    break;
	 case 1:
		adc1 = adc_conv;
		ADMUX = B01000001;	
		analog_port = 0;
		adc1_read_complete = true;
	    break;		  
  }  
	ADCSRA |= B01000000;
	
}

/* This is the "low speed" 1000x/second sweeper interrupt routine
 * who's sole purpose in life is to reset the output compare value
 * for timer zero to change the output RPM.  In cases where the RPM
 * change per ISR is LESS than one LSB of the counter a set of modulus
 * variabels are used to handle fractional values.
 */
ISR(TIMER2_COMPA_vect) {
//  PORTD = (1 << 7);
  if ( mode != LINEAR_SWEPT_RPM)
  {
//    PORTD = (0 << 7);
    return;
  }
  if (sweep_lock)  // semaphore to protect around changes/critical sections
  {  
 //   PORTD = (0 << 7);
    return;
  }
  sweep_lock = true;
  if (sweep_reset_prescaler)
  {
    sweep_reset_prescaler = false;
    reset_prescaler = true;
    prescaler_bits = SweepSteps[sweep_stage].prescaler_bits;
    last_prescaler_bits = prescaler_bits;  
  }
  /* Sweep code */
  if (sweep_direction == ASCENDING)
  {
    oc_remainder += SweepSteps[sweep_stage].remainder_per_isr;
    /* IF the total is over the threshold we increment the TCNT factor
     * for each multiple it is over by
     */
    while (oc_remainder > FACTOR_THRESHOLD)
    {
      fraction++;
      oc_remainder -= FACTOR_THRESHOLD;
    }
    if (new_OCR1A > SweepSteps[sweep_stage].ending_ocr)
    {
      new_OCR1A -= (SweepSteps[sweep_stage].tcnt_per_isr + fraction);
      fraction = 0;
    }
    else /* END of the stage, find out where we are */
    {
      sweep_stage++;
      oc_remainder = 0;
      if (sweep_stage < total_sweep_stages)
      {
        /* Toggle  when changing stages */
        //PORTD &= ~(1<<7); /* turn DBG pin off */
        //PORTD |= (1<<7);  /* Turn DBG pin on */
        new_OCR1A = SweepSteps[sweep_stage].beginning_ocr;
        if (SweepSteps[sweep_stage].prescaler_bits != last_prescaler_bits)
          sweep_reset_prescaler = true;
      }
      else /* END of line, time to reverse direction */
      {
        sweep_stage--; /*Bring back within limits */
        sweep_direction = DESCENDING;
        new_OCR1A = SweepSteps[sweep_stage].ending_ocr;
        if (SweepSteps[sweep_stage].prescaler_bits != last_prescaler_bits)
          sweep_reset_prescaler = true;
        PORTD |= 1 << 7;  /* Debugginga, ascending */
      }
      /* Reset fractionals or next round */
    }
  }
  else /* Descending */
  {
    oc_remainder += SweepSteps[sweep_stage].remainder_per_isr;
    while (oc_remainder > FACTOR_THRESHOLD)
    {
      fraction++;
      oc_remainder -= FACTOR_THRESHOLD;
    }
    if (new_OCR1A < SweepSteps[sweep_stage].beginning_ocr)
    {
      new_OCR1A += (SweepSteps[sweep_stage].tcnt_per_isr + fraction);
      fraction = 0;
    }
    else /* End of stage */
    {
      sweep_stage--;
      oc_remainder = 0;
      if (sweep_stage >= 0)
      {
        new_OCR1A = SweepSteps[sweep_stage].ending_ocr;
        if (SweepSteps[sweep_stage].prescaler_bits != last_prescaler_bits)
          sweep_reset_prescaler = true;
      }
      else /*End of the line */
      {
        sweep_stage++; /*Bring back within limits */
        sweep_direction = ASCENDING;
        new_OCR1A = SweepSteps[sweep_stage].beginning_ocr;
        if (SweepSteps[sweep_stage].prescaler_bits != last_prescaler_bits)
          sweep_reset_prescaler = true;
        PORTD &= ~(1<<7);  /*Descending  turn pin off */
      }
    }
  }
  sweep_lock = false;
  //wanted_rpm = get_rpm_from_tcnt(&SweepSteps[sweep_stage].beginning_ocr, &SweepSteps[sweep_stage].prescaler_bits);
//  PORTD = (0 << 7);
}

/* Pumps the pattern out of flash to the port 
 * The rate at which this runs is dependent on what OCR1A is set to
 * the sweeper in timer2 alters this on the fly to alow changing of RPM
 * in a very nice way
 */
ISR(TIMER1_COMPA_vect) {
  /* This is VERY simple, just walk the array and wrap when we hit the limit */
  uint8_t aux;
  //PORTB = output_invert_mask ^ pgm_read_byte(&Wheels[selected_wheel].edge_states_ptr[edge_counter]);   /* Write it to the port */
   aux = output_invert_mask ^ pgm_read_byte(&Wheels[selected_wheel].edge_states_ptr[edge_counter]);  

  switch(aux)		
  {
		case 0:
			 digitalWrite(pri_out, LOW);
			 digitalWrite(pri_outN, HIGH);
			 digitalWrite(sec_out, LOW);
			 digitalWrite(sec_outN, HIGH);
			 break;
		case 1:
			 digitalWrite(pri_out, HIGH);
			 digitalWrite(pri_outN, LOW);
			 digitalWrite(sec_out, LOW);
			 digitalWrite(sec_outN, HIGH);
			 break;		 
		case 2:
			 digitalWrite(pri_out, LOW);
			 digitalWrite(pri_outN, HIGH);
			 digitalWrite(sec_out, HIGH);
			 digitalWrite(sec_outN, LOW);
			 break;		 	 
		case 3:
			 digitalWrite(pri_out, HIGH);
			 digitalWrite(pri_outN, LOW);
			 digitalWrite(sec_out, HIGH);
			 digitalWrite(sec_outN, LOW);
			 
			 break;		 	 	 
		default: break;	 
  }
  
  /* Normal direction  overflow handling */
  if (normal)
  {
    edge_counter++;
    if (edge_counter == Wheels[selected_wheel].wheel_max_edges) {
      edge_counter = 0;
    }
  }
  else
  {
    if (edge_counter == 0)
      edge_counter = Wheels[selected_wheel].wheel_max_edges;
    edge_counter--;
  }
  /* The tables are in flash so we need pgm_read_byte() */

  /* Reset Prescaler only if flag is set */
  if (reset_prescaler)
  {
    TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); /* Clear CS10, CS11 and CS12 */
    TCCR1B |= prescaler_bits;
    reset_prescaler = false;
  }
  
  // Sync pulse M.M.
  if(edge_counter<2)
		digitalWrite(sync_out, HIGH);
  else
	   digitalWrite(sync_out, LOW);
  
  /* Reset next compare value for RPM changes */
  OCR1A = new_OCR1A;  /* Apply new "RPM" from Timer2 ISR, i.e. speed up/down the virtual "wheel" */
}

void loop() 
{
  uint16_t tmp_rpm = 0;
  /* Just handle the Serial UI, everything else is in 
   * interrupt handlers or callbacks from SerialUI.
   */

 unsigned long currentMillis;
 char button;

  //if(Serial.available() > 0) { commandParser(); }

  if(mode == POT_RPM)
  {
    if (adc1_read_complete == true)
    {
      adc1_read_complete = false;
      tmp_rpm = adc1 << TMP_RPM_SHIFT;
      if (tmp_rpm > TMP_RPM_CAP) { tmp_rpm = TMP_RPM_CAP; }
      wanted_rpm = tmp_rpm;
      reset_new_OCR1A(tmp_rpm);
    }
  }
  
  // button read  ----------------------------------
    
  currentMillis = millis();	 
  
  if (currentMillis - previousMillis >= interval) 
  {	  previousMillis = currentMillis;
			
	 if (adc0_read_complete == true)
     {
		 adc0_read_complete = false;
		 
		 if(button = Button_Press(adc0))
		 {
			Proc_keys(button);
		 }			 
				 
	 }	 
	 if(mode == POT_RPM)  LCD_Update(1);
	 
	 if(serial_upd)
	 {
		 LCD_Update(0);
		 LCD_Update(1);
		 serial_upd = false;
	 }
  }
  
}

void serialEvent()
{
	
    commandParser();
	serial_upd = true;
	
}

void reset_new_OCR1A(uint32_t new_rpm)
{
  uint32_t tmp;
  uint8_t bitshift;
  uint8_t tmp_prescaler_bits;
  tmp = (uint32_t)(8000000.0/(Wheels[selected_wheel].rpm_scaler * (float)(new_rpm < 10 ? 10:new_rpm)));
/*  mySUI.print(F("new_OCR1a: "));
  mySUI.println(tmpl);
  */
  get_prescaler_bits(&tmp,&tmp_prescaler_bits,&bitshift);
  /*
  mySUI.print(F("new_OCR1a: "));
  mySUI.println(tmp2);
  */
  new_OCR1A = (uint16_t)(tmp >> bitshift); 
  prescaler_bits = tmp_prescaler_bits;
  reset_prescaler = true; 
}


uint8_t get_bitshift_from_prescaler(uint8_t *prescaler_bits)
{
  switch (*prescaler_bits)
  {
    case PRESCALE_1024:
    return 10;
    case PRESCALE_256:
    return 8;
    case PRESCALE_64:
    return 6;
    case PRESCALE_8:
    return 3;
    case PRESCALE_1:
    return 0;
  }
  return 0;
}


//! Gets RPM from the TCNT value
/*!
 * Gets the RPM value based on the passed TCNT and prescaler
 * \param tcnt pointer to Output Compare register value
 * \param prescaler_bits point to prescaler bits enum
 */
uint16_t get_rpm_from_tcnt(uint16_t *tcnt, uint8_t *prescaler_bits)
{
  bitshift = get_bitshift_from_prescaler(prescaler_bits);
  return (uint16_t)((float)(8000000 >> bitshift)/(Wheels[selected_wheel].rpm_scaler*(*tcnt)));
}


//! Gets prescaler enum and bitshift based on OC value
void get_prescaler_bits(uint32_t *potential_oc_value, uint8_t *prescaler, uint8_t *bitshift)
{
  if (*potential_oc_value >= 16777216)
  {
    *prescaler = PRESCALE_1024;
    *bitshift = 10;
  }
  else if (*potential_oc_value >= 4194304)
  {
    *prescaler = PRESCALE_256;
    *bitshift = 8;
  }
  else if (*potential_oc_value >= 524288)
  {
    *prescaler = PRESCALE_64;
    *bitshift = 6;
  }
  else if (*potential_oc_value >= 65536)
  {
    *prescaler = PRESCALE_8;
    *bitshift = 3;
  }
  else
  {
    *prescaler = PRESCALE_1;
    *bitshift = 0;
  }
}


//! Builds the SweepSteps[] structure
/*!
 * For sweeping we cannot just pick the TCNT value at the beginning and ending
 * and sweep linearily between them as it'll result in a VERY slow RPM change
 * at the low end and a VERY FAST change at the high end due to the inverse
 * relationship between RPM and TCNT. So we compromise and break up the RPM
 * range into octaves (doubles of RPM), and use a linear TCNT change between
 * those two points. It's not perfect, but computationally easy
 *
 * \param low_rpm_tcnt pointer to low rpm OC value, (not prescaled!)
 * \param high_rpm_tcnt pointer to low rpm OC value, (not prescaled!)
 * \param total_stages pointer to tell the number of structs to allocate
 * \returns pointer to array of structures for each sweep stage.
 */
sweep_step *build_sweep_steps(uint32_t *low_rpm_tcnt, uint32_t *high_rpm_tcnt, uint8_t *total_stages)
{
  sweep_step *steps;
  uint8_t prescaler_bits;
  uint8_t bitshift;
  uint32_t tmp = *low_rpm_tcnt;
  /* DEBUG
  mySUI.print(*low_rpm_tcnt);
  mySUI.print(F("<->"));
  mySUI.println(*high_rpm_tcnt);
   */

  steps = (sweep_step *)malloc(sizeof(sweep_step)*(*total_stages));

#ifdef MORE_LINEAR_SWEEP
  for (uint8_t i = 0; i < (*total_stages); i+=2)
#else
  for (uint8_t i = 0; i < (*total_stages); i++)
#endif
  {
    /* The low rpm value will ALWAYS have the highed TCNT value so use that
    to determine the prescaler value
    */
    get_prescaler_bits(&tmp, &steps[i].prescaler_bits, &bitshift);
    
    steps[i].beginning_ocr = (uint16_t)(tmp >> bitshift);
    if ((tmp >> 1) < (*high_rpm_tcnt))
      steps[i].ending_ocr = (uint16_t)((*high_rpm_tcnt) >> bitshift);
    else
      steps[i].ending_ocr = (uint16_t)(tmp >> (bitshift + 1)); // Half the begin value
    tmp = tmp >> 1; /* Divide by 2 */
    /* DEBUG
    mySUI.print(steps[i].beginning_ocr);
    mySUI.print(F("<->"));
    mySUI.println(steps[i].ending_ocr);
    */
  }
  return steps;
}

//--------------------------------------------------
void LCD_Update(bool line)
{
	char buf[16];	
	
	if(!line) //update line 0
	{
		strncpy_P(buf,Wheels[selected_wheel].decoder_name,16);
		lcd.setCursor(0,0);
		lcd.print("                ");		
		lcd.setCursor(0,1);
		lcd.print(buf);		
	}
	
	else //update line 1
	{
		switch(mode)	
		{
			case LINEAR_SWEPT_RPM:
				sprintf(buf,"SWP %5d  %5d",sweep_low_rpm,sweep_high_rpm);	
				break;
			case FIXED_RPM:
				sprintf(buf,"FIX  %5d RPM  ",wanted_rpm);	
				break;
			case POT_RPM:
				sprintf(buf,"POT  %5d RPM  ",wanted_rpm);		
				break;
		}	

		lcd.setCursor(0,0);
		lcd.print(buf);
	}
}

//void Proc_keys(uint16_t adc_but)
void Proc_keys(char but)
{
//char but;
	
	//but = Button_Press(adc_but);	
    
	if(debouce)
    {		
	  switch(but)
	  {	
           case 'L': //left button
		      if(mode == FIXED_RPM)
			  {
				  if(wanted_rpm>100)
				  {
					  wanted_rpm -= 100;   
					  setRPM(wanted_rpm);
					  LCD_Update(1);				  
				  }
			  }
		      break;
			  
			case 'R': //right button
		      if(mode == FIXED_RPM)
			  {
				  if(wanted_rpm<16000)
				  {
					  wanted_rpm += 100;   
					  setRPM(wanted_rpm);
					  LCD_Update(1);				  
				  }
			  }
		      break; 
	  
		   case 'D': //down button
				edge_counter = 0;
			 if (selected_wheel == 0)
				selected_wheel = MAX_WHEELS-1;
			else 
				selected_wheel--;
						
			display_new_wheel();
			debouce = false;	
			LCD_Update(0);
		    break;
						  
		  case 'U': //up button
				edge_counter = 0;
				if (selected_wheel == (MAX_WHEELS-1))
					selected_wheel = 0;
				else 
					selected_wheel++;
				display_new_wheel();
			    debouce = false;
				LCD_Update(0);
			    break;  
						  
		case 'S': //select button
			  mode++;
			  if(mode == MAX_MODES) 
				  mode = 0;
			  if(mode == FIXED_RPM)
			  {
				  wanted_rpm = 2500;   
				  setRPM(wanted_rpm);
			  } 
		
			  if(mode == LINEAR_SWEPT_RPM)
			  {				  
				 sweep_low_rpm = 100;
				 sweep_high_rpm = 4000;
				 compute_sweep_stages(&sweep_low_rpm, &sweep_high_rpm);		  
			  }
			  debouce = false;
			  LCD_Update(1);			  
			  break;  						  
			
      }
		  
	}	
		
}

char Button_Press(uint16_t x)
{
	if(x>750) {debouce = true; return 0;}
	if(x>500) return 'S';
	if(x>350) return 'L';
	if(x>200) return 'D';
	if(x>50)  return 'U';
	if(x<50)  return 'R';
}