//!
//! @file mc_drv.c,v
//!
//! Copyright (c) 2008 Atmel.
//!
//! @brief Motor Control Driver level :\n
//!     - Motor Control Init\n
//!     - PWM update\n
//!     - PWM -> output switching\n
//!     - interrupts\n
//!
//! @version 1.19
//!

//_____  I N C L U D E S ___________________________________________________

#include "config.h"

#include "mc_drv.h"
#include "mc_interface.h"
#include "mc_control.h"

#include "adc\adc_drv.h"
#include "dac\Dac_drv.h"
#include "pll\pll_drv.h"
#include "comparator\comparator_drv.h"
#include "awex_driver.h"
#include "TC_driver.h"
#include "hires_driver.h"
#include "adc_driver.h"
U8 count = 1;     // variable "count" is use for calculate the "average" speed on 'n' samples
U16 average = 0;
U8 ovf_timer = 0; // variable "ovf_timer" is use to simulate a 16 bits timer with 8 bits timer

Bool inrush_mask_flag = FALSE;
U16 inrush_delay = 0;

Bool g_mc_read_enable = FALSE;  // the speed can be read
Bool g_tick = FALSE;             //!< Use for control the sampling period value
Bool hall1_old_val = 1;

Bool current_EOC = FALSE; //End Of Conversion Flag

char State = CONV_INIT; // State of the ADC scheduler
char ADC_State = FREE;  // ADC State : running = BUSY not running = FREE

volatile char offset=0;
/******************************************************************************/
/******************************************************************************/
/*        Hardware Initialization                                             */
/******************************************************************************/
/******************************************************************************/
void ADC_Init()
{
  	/* Move stored calibration values to ADC B. */
	ADC_CalibrationValues_Set(&ADCB);

	/* Get offset value for ADC B.  */
 	offset = ADC_Offset_Get(&ADCB);

	/* Set up ADC B to have signed conversion mode and 12 bit resolution. */
  	ADC_ConvMode_and_Resolution_Config(&ADCB, true, ADC_RESOLUTION_12BIT_gc);

	/* Sample rate is CPUFREQ/16. Allow time for storing data. */
	ADC_Prescaler_Config(&ADCB, ADC_PRESCALER_DIV16_gc);

	/* Set referance voltage on ADC B to be VCC-0.6 V.*/
	ADC_Referance_Config(&ADCB, ADC_REFSEL_INTVCC_gc);

	/* Setup channel 0, 1, 2 and 3 to have single ended input. */
	ADC_Ch_InputMode_and_Gain_Config(&ADCB.CH0,
	                                 ADC_CH_INPUTMODE_SINGLEENDED_gc,
	                                 ADC_CH_GAIN_1X_gc);

	/* Set input to the channels in ADC B to be PIN 0. */
	ADC_Ch_InputMux_Config(&ADCB.CH0, ADC_CH_MUXPOS_PIN0_gc, ADC_CH_MUXNEG_PIN0_gc);

	/* Enable ADC B with free running mode, VCC reference and signed conversion.*/
	ADC_Enable(&ADCB);

	/* Wait until common mode voltage is stable. Default clk is 2MHz and
	 * therefore below the maximum frequency to use this function. */
	ADC_Wait_8MHz(&ADCB);
        
	/* Enable free running mode. */
	ADC_FreeRunning_Enable(&ADCB);

}

/**
* @brief init HW
* @pre none
* @post initialization of hardware
*/
void mc_init_HW(void)

{

  //Do not modify PSCOUT Configuration
  // PORT C :
  PORTC.DIRSET = (PIN0_bm | PIN1_bm | PIN2_bm | PIN3_bm | PIN4_bm | PIN5_bm);
  PORTC.OUTCLR = (PIN0_bm | PIN1_bm | PIN2_bm | PIN3_bm | PIN4_bm | PIN5_bm);

  // Hall sensor input pull up activation
  PORTE.DIRCLR = (PIN0_bm | PIN1_bm | PIN2_bm);

  //Initialization of AT90PWM3 External Interrupts
  PORTE.INTCTRL = PORT_INT0LVL_LO_gc;
  PORTE.INT0MASK = (PIN0_bm | PIN1_bm | PIN2_bm);
  PORTCFG.MPCMASK = (PIN0_bm | PIN1_bm | PIN2_bm);
  PORTE.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;


  // In Center Aligned Mode :
  // => PSCx_Init(Period_Half, Dutyx0_Half, Synchro, Dutyx1_Half)
  PWM_Init(255);

  mc_init_timer0();
  mc_init_timer1();

  ADC_Init();

}

/**
* @brief init Amplifier 1
* @pre none
* @post initialization of hardware
*/
void init_amplifier_1(void)
{
//   AMP1CSR =   (1<<AMP1EN) /* amplifier enable */ \
//            | (1<<AMP1G1)|(0<<AMP1G0) /* gain = 20 */ \
 //            | (0<<AMP1TS1)|(1<<AMP1TS0) /* clock from PSC0 */ ;
}

// PSC initialization depend on the PSC mode
//  0- One ramp Mode
//  1- Two ramp Mode
//  2- Four ramp Mode
//  3- Center Aligned Mode
void ConfigDTI( U8 deadTime )
{
	/* Configure dead time insertion. */
	AWEX_EnableDeadTimeInsertion( &AWEXC, AWEX_DTICCAEN_bm |AWEX_DTICCBEN_bm|AWEX_DTICCCEN_bm );
	AWEX_SetOutputOverrideValue( AWEXC, 0xFF );
	AWEX_SetDeadTimesSymmetricalUnbuffered( AWEXC, deadTime );  
}

/**
* @brief Initialization of PWM generator PSC0
*/
void PWM_Init ( unsigned int OCRnRB)
{  
	ConfigDTI( 3 );
	TCC0.PER = OCRnRB;
	TCC0.CTRLB = TC0_CCAEN_bm | TC0_CCBEN_bm | TC0_CCCEN_bm | TC0_CCDEN_bm| TC_WGMODE_DS_T_gc;
	TCC0.INTCTRLA = TC_OVFINTLVL_LO_gc;
	TCC0.CTRLA = TC_CLKSEL_DIV4_gc;

        AWEX_SetOutputOverrideValue( AWEXC, 0 );   
        AWEX_EnableCommonWaveformChannelMode(AWEXC);  	
}


/***************************************************************************/
/***************************************************************************/
/*     All functions for motor's phases commutation                        */
/***************************************************************************/
/***************************************************************************/

/**
* @brief Get the value of hall sensors (1 to 6)
* @param return an unsigned char
*  value of hall sensor
* @pre configuration of port PB and PD
* @post new value of position
*/
U8 mc_get_hall(void)
{
  return HALL_SENSOR_VALUE();
}

/**
 * @brief External interruption
 *                 Sensor (A) mode toggle
 * @pre configuration of external interruption (initialization)
 * @post New value in Hall variable
 */
#pragma vector = HALL_A()
__interrupt void mc_hall_a(void)
{
  mc_switch_commutation(HALL_SENSOR_VALUE());

  //estimation speed on rising edge of Hall_A
  if ((hall1_old_val == 0) && (PORTE.IN & PIN0_bm))
  {
    mc_estimation_speed();
    g_mc_read_enable=FALSE; // Wait 1 period
  }
  else
  {
    g_mc_read_enable=TRUE;
  }
  hall1_old_val = (PORTE.IN & PIN0_bm);
}


/**
* @brief Set the duty cycle values in the PSC according to the value calculate by the regulation loop
*/
void mc_duty_cycle(U8 level)
{
  /* Output new compare value. */
  TC_SetCompareA( &TCC0, level );   
  TC_SetCompareB( &TCC0, level );                
  TC_SetCompareC( &TCC0, level ); 
}

/**
* @brief Set the Switching Commutation value on outputs
*   according to sensor or estimation position
*
* @param position (1 to 6) and direction (FORWARD or BACKWARD)
*/
void mc_switch_commutation(U8 position)
{
  // get the motor direction to commute the right switches.
  char direction = mc_get_motor_direction();

  // Switches are commuted only if the user start the motor and
  // the speed consign is different from 0.
  if ((mc_motor_is_running()) && (mc_get_motor_speed()!=0))
  {
    mc_duty_cycle(mc_get_Duty_Cycle());
    switch(position)
    {
    // cases according to rotor position
      case HS_001:  if (direction==CCW)  {Set_Q1Q6();}
                    else                      {Set_Q5Q2();}
                    break;

      case HS_101:  if (direction==CCW)  {Set_Q3Q6();}
                    else                      {Set_Q5Q4();}
                    break;

      case HS_100:  if (direction==CCW)  {Set_Q3Q2();}
                    else                      {Set_Q1Q4();}
                    break;

      case HS_110:  if (direction==CCW)  {Set_Q5Q2();}
                    else                      {Set_Q1Q6();}
                    break;

      case HS_010:  if (direction==CCW)  {Set_Q5Q4();}
                    else                      {Set_Q3Q6();}
                    break;

      case HS_011:  if (direction==CCW)  {Set_Q1Q4();}
                    else                      {Set_Q3Q2();}
                    break;
      default : break;
      }
  }
  else
  {
    Set_none(); // all switches are switched OFF
  }
}

/******************************************************************************/
/******************************************************************************/
/*    TIMER 1 :  generate the g_tick                                          */
/******************************************************************************/
/******************************************************************************/

/**
 * @brief timer 1 Configuration
 * Use to generate a 250us activation  for sampling speed regulation
 * @pre None
 * @post An interrupt all 256us
*/
void mc_init_timer1(void)
{
  TCD1.PER = 8000;
  TCD1.INTCTRLA = TC_OVFINTLVL_LO_gc;
  TCD1.CTRLA = TC_CLKSEL_DIV1_gc;
}

/**
  * @brief Launch the regulation loop (see main.c) .
  * @pre configuration of timer 1 registers
  * @post g_tick use in main.c for regulation loop
*/
#pragma vector = TCD1_OVF_vect
__interrupt void launch_sampling_period(void)
{
  g_tick = TRUE;
}

/******************************************************************************/
/******************************************************************************/
/*         TIMER 0 : Speed Measurement                                        */
/******************************************************************************/
/******************************************************************************/

/**
 * @brief Timer 0 Configuration
 * The timer 0 is used to generate an IT when an overflow occurs
 * @pre None
 * @post Timer0 initialized.
*/
void mc_init_timer0(void)
{
  TCD0.PER = 0xFFFF;
  TCD0.INTCTRLA = TC_OVFINTLVL_LO_gc;
  TCD0.CTRLA = TC_CLKSEL_DIV256_gc;
}

/**
  * @brief Timer0 Overflow for speed measurement
  * @pre configuration of timer 0
  * @post generate an overflow when the motor turns too slowly
*/

#pragma vector = TCD0_OVF_vect
__interrupt void ovfl_timer0(void)
{
    mc_set_motor_measured_speed(0);
    //if the motor was turning and no stop order
    // was given, motor run automatically.
    if(mc_motor_is_running())mci_retry_run();

}

/**
* @brief estimation speed
* @pre configuration of timer 0 \n
*           and define or not AVERAGE_SPEED_MEASURE in config_motor.h
* @post new value for real speed
*/
void mc_estimation_speed(void)
{
  U16 timer_value;
  U32 new_measured_speed;

  if (g_mc_read_enable==TRUE)
  {
    // Two 8 bits variables are use to simulate a 16 bits timers
    timer_value = TCD0.CNT/2;

    if (timer_value == 0) {timer_value += 1 ;} // warning DIV by 0
    new_measured_speed = K_SPEED / timer_value;
    if(new_measured_speed > 255) new_measured_speed = 255; // Variable saturation


    #ifdef AVERAGE_SPEED_MEASURE
      // To avoid noise an average is realized on 8 samples
      average += new_measured_speed;
      if(count >= n_SAMPLE)
      {
        count = 1;
        mc_set_motor_measured_speed(average >> 3);
        average = 0;
      }
      else count++;
    #else
      // else get the real speed
      mc_set_motor_measured_speed(new_measured_speed);
    #endif

    // Reset Timer 0 register and variables
    TCD0.CNT = 0x00;
    g_mc_read_enable=FALSE;
  }
}

/******************************************************************************/
/******************************************************************************/
/*       ADC use for current and potentiometer measurement                    */
/******************************************************************************/
/******************************************************************************/
/**
* @brief Launch the scheduler for the ADC
* @pre none
* @post Get Channel 6 and 12 results for Potentiometer and current values.
*/
void mc_ADC_Scheduler(void)
{
    do{
            /* If the conversion on the ADCB channel 0 never is
             * complete this will be a deadlock. */
    }while(!ADC_Ch_Conversion_Complete(&ADCB.CH0));
   unsigned int value;
   value = ADC_ResultCh_GetWord(&ADCB.CH0,offset); 

   value = ((value)>>4);      
   value = value + 140; 
   if ( (value&0xFF) < 150) value = 0;

   //value = ((value>>2)&0x00FF);
   //value = (0xFF - value)*2;
   mc_set_potentiometer_value(value);
}

/******************************************************************************/
/******************************************************************************/
/*       Over Current Configuration                                           */
/******************************************************************************/
/******************************************************************************/

//! @brief Set the Over Current threshold
//! @pre DAC initialization
//! @post the Over Current threshold is set.
void mc_set_Over_Current(U8 Level)
{
  //Set_dac_8_bits(Level);
}

//! @brief the purpose of this function is to disable \n
//!   the overcurrent detection during startup (inrush current) \n
void mc_disable_during_inrush(void)
{
  inrush_delay = (U16) 1000;
  inrush_mask_flag = TRUE;
  //Disable_over_current();
}

//! @brief the purpose of this function is to manage the delay \n
//!   used when the overcurrent detection is disabled \n
void mc_inrush_task(void)
{
  if (inrush_mask_flag == TRUE)
  {
    if (inrush_delay-- == 0)
    {
      inrush_mask_flag = FALSE;
     // Enable_over_current();
    }
  }
}
