/****************************************************************************************************/
/**
\file       adc.c
\author     Freescale Semiconductor
\version    1.1        
*/
/****************************************************************************************************/
/* Services performed by FREESCALE in this matter are performed AS IS and without any warranty.  	  */
/* CUSTOMER retains the final decision relative to the total design and functionality of the end 	  */
/* product.                                                                                      	  */
/* FREESCALE neither guarantees nor will be held liable by CUSTOMER for the success of this project.*/
/*                                                                                                  */
/* FREESCALE disclaims all warranties, express, implied or statutory including, but not limited to, */
/* implied warranty of merchantability or fitness for a particular purpose on any hardware,         */
/* software ore advise supplied to the project by FREESCALE, and or any product resulting from      */
/* FREESCALE services.                                                                              */
/* In no event shall FREESCALE be liable for incidental or consequential damages arising out of     */
/* this agreement. CUSTOMER agrees to hold FREESCALE harmless against any and all claims demands or */
/* actions by anyone on account of any damage,or injury, whether commercial, contractual, or        */
/* tortuous, rising directly or indirectly as a result of the advise or assistance supplied CUSTOMER*/ 
/* in connectionwith product, services or goods supplied under this Agreement.                      */
/*                                                                                                  */
/****************************************************************************************************/

/*****************************************************************************************************
* Include files
*****************************************************************************************************/
#include "adc.h"
#include "pdb.h"
#include "dma.h"
#include "nvic.h"
/*****************************************************************************************************
* Declaration of module wide FUNCTIONs - NOT for use in other modules
*****************************************************************************************************/
void adc0_isr (void);
void adc1_isr (void);

void vfnADCHWTrigger (uint8_t u8Index, uint8_t u8Trigger);

/*****************************************************************************************************
* Definition of module wide MACROs / #DEFINE-CONSTANTs - NOT for use in other modules
*****************************************************************************************************/

/*****************************************************************************************************
* Declaration of module wide TYPEs - NOT for use in other modules
*****************************************************************************************************/

/*****************************************************************************************************
* Definition of module wide VARIABLEs - NOT for use in other modules
*****************************************************************************************************/
tADC sADC[ADC_CONFIGURATIONS_USED];
tADCControl sADCControl;

/*****************************************************************************************************
* Definition of module wide (CONST-) CONSTANTs - NOT for use in other modules
*****************************************************************************************************/
 

/*****************************************************************************************************
* Code of project wide FUNCTIONS
*****************************************************************************************************/

/****************************************************************************************************/
/**
* \brief 
* \param        
* \return   
* \todo  
*/
/****************************************************************************************************/
void vfnADCInit (void)
{       
       sADC[ADC_EKG_SIGNAL].pADCBase = ADC0;
       sADC[ADC_EKG_SIGNAL].u8ChannelA = ADC0_DIF0;
       sADC[ADC_EKG_SIGNAL].u8ClockResolutionLowpower = ADC_LOWPOWER_MODE | ADCK_BUSCLCK_DIVBY2 | ADCK_DIV_BY_2 | _16B_RESOLUTION | SHORT_SAMPLE;
       sADC[ADC_EKG_SIGNAL].u8HwAverage = ADC_HW_AVG_16SAMPLES;
       sADC[ADC_EKG_SIGNAL].u16HwCmpVrefTrgDMA = ADC_EXTERNAL_VREFH | ADC_SW_TRIGGER | ADC_DMA_DISABLE;
       sADC[ADC_EKG_SIGNAL].u8LowPower = ADC_LOWPOWER_MODE;
    
//       sADC[ADC_EKG_BASELINE].pADCBase = ADC1;
//       sADC[ADC_EKG_BASELINE].u8ClockResolutionLowpower = ADC_LOWPOWER_MODE | ADCK_BUSCLCK_DIVBY2 | ADCK_DIV_BY_2 | _16B_RESOLUTION | SHORT_SAMPLE;
//       sADC[ADC_EKG_BASELINE].u8ChannelA = ADC1_DP0_CH;
//       sADC[ADC_EKG_BASELINE].u16HwCmpVrefTrgDMA = ADC_EXTERNAL_VREFH | ADC_SW_TRIGGER | ADC_DMA_DISABLE;
//       sADC[ADC_EKG_BASELINE].u8HwAverage = ADC_HW_AVG_32SAMPLES;
//       sADC[ADC_EKG_BASELINE].u8LowPower = ADC_LOWPOWER_MODE;
        
	/** Disable NVIC IRQs */ 
	u8NvicDisableIrq(INT_ADC0 - 16);
	u8NvicDisableIrq(INT_ADC1 - 16);
	
}
/****************************************************************************************************/
/**
* \brief  
* \param        
* \return   
* \todo     
*/
/****************************************************************************************************/
void vfnADCConfigChannel (uint8_t u8Index)
{
  uint8_t u8AltHwTrigger;	
	
	sADCControl.gu8PrevConfig = sADCControl.gu8ActualConfig;
	sADCControl.gu8ActualConfig = u8Index;
	
	/**Turn on clock to the ADC module, and enable NVIC interrupts*/
	if (sADC[u8Index].pADCBase == ADC0)
	{
	  u8NvicEnableIrq(INT_ADC0 - 16); 
          SIM_SCGC6 |= (SIM_SCGC6_ADC0_MASK );
	}
		 
	if (sADC[u8Index].pADCBase == ADC1)
	{
	  u8NvicEnableIrq(INT_ADC1 - 16);	
	  SIM_SCGC3 |= (SIM_SCGC3_ADC1_MASK );	
	}
	
	/** If DMA enabled initialize DMA */
	if (sADC[u8Index].u16HwCmpVrefTrgDMA & ADC_SC2_DMAEN_MASK)
	{
          vfnADC2DMA_Init(u8Index);
	}
 
	
	ADC_CFG1_REG(sADC[u8Index].pADCBase) = sADC[u8Index].u8ClockResolutionLowpower;
	ADC_CV1_REG(sADC[u8Index].pADCBase) = (uint32_t)(sADC[u8Index].u16CompareValue1);
	ADC_CV2_REG(sADC[u8Index].pADCBase) = (uint32_t)(sADC[u8Index].u16CompareValue2);
	ADC_SC2_REG(sADC[u8Index].pADCBase) = (uint8_t)(sADC[u8Index].u16HwCmpVrefTrgDMA);
	ADC_SC3_REG(sADC[u8Index].pADCBase) = sADC[u8Index].u8HwAverage;
	
	/** ADC calibration */
	vfnADCCal(u8Index);
	
	/** If hw trigger is enabled configure SIM_SOPT7 register with the hw trigger selected */
	if (sADC[u8Index].u16HwCmpVrefTrgDMA & ADC_SC2_ADTRG_MASK)
	{
          /* Leave the trigger selection only */
          u8AltHwTrigger = (uint8_t)((sADC[u8Index].u16HwCmpVrefTrgDMA&0xF00)>>8);
          vfnADCHWTrigger(u8Index,u8AltHwTrigger);
	}
        
	
	/** Disable all channels */
	ADC_SC1_REG(sADC[u8Index].pADCBase,0) |= ADC_SC1_ADCH_MASK;
	ADC_SC1_REG(sADC[u8Index].pADCBase,1) |= ADC_SC1_ADCH_MASK;
       
}

/****************************************************************************************************/
/**
* \brief  
* \param        
* \return   
* \todo     
*/
/****************************************************************************************************/
void vfnADCPGAChangeGain (uint8_t u8Index, uint8_t u8Gain)
{
	/** Clear previous value */
	ADC_PGA_REG(sADC[u8Index].pADCBase) &= ~ADC_PGA_PGAG_MASK;
	/** Load new gain */
	ADC_PGA_REG(sADC[u8Index].pADCBase) |= ADC_PGA_PGAG(u8Gain);
}


void vfnPollChannel(uint8_t u8Index)
{
	//ADC1_SC3 |= ADC_SC3_ADCO_MASK;

	ADC_SC1_REG(sADC[u8Index].pADCBase,0) =  (unsigned char)(ADC_SC1_COCO_MASK | sADC[u8Index].u8ChannelA);			//Start conversion
	while (!(ADC_SC1_REG(sADC[u8Index].pADCBase,0) & ADC_SC1_COCO_MASK));		//Wait for COCO flag	
	ADC_SC1_REG(sADC[u8Index].pADCBase,0) |= 0x1Fu;								//Disable ADC Channel
	
	if (sADC[u8Index].pADCBase == ADC0)											//Read result to clear flag
		sADCControl.gu16LatestResult[0] = (uint16_t)ADC0_RA;
	
	if (sADC[u8Index].pADCBase == ADC1)											//Read result to clear flag
		sADCControl.gu16LatestResult[2] = (uint16_t)ADC1_RA;
}







/****************************************************************************************************/
/**
* \brief  Enable hardware trigger
* \param  uint8_t lu8Trigger: possible hardware triggers    
* 							ADC_HWTRG_PDB --> Programmable Delay Block 
*  							ADC_HWTRG_TOD --> Time of Day
* \return   
* \todo     
*/

/****************************************************************************************************/
void vfnADCHWTrigger (uint8_t u8Index, uint8_t u8Trigger)
{
	if (sADC[u8Index].pADCBase == ADC0)
	{
          /** Selects PDB not ALT */
          SIM_SOPT7 &= ~SIM_SOPT7_ADC0ALTTRGEN_MASK;	
          SIM_SOPT7 &= ~SIM_SOPT7_ADC0TRGSEL_MASK;
          
          /** If ALT trigger is selects the one previously chosen at .u8HwCmpVrefTrg */
          if (u8Trigger)
            SIM_SOPT7 |= SIM_SOPT7_ADC0ALTTRGEN_MASK | SIM_SOPT7_ADC0TRGSEL(u8Trigger);	
	}
	
	if (sADC[u8Index].pADCBase == ADC1)
	{
          /** Selects PDB not ALT */
          SIM_SOPT7 &= ~SIM_SOPT7_ADC1ALTTRGEN_MASK;	
          SIM_SOPT7 &= ~SIM_SOPT7_ADC1TRGSEL_MASK;
          
          /** If ALT trigger is selects the one previously chosen at .u8HwCmpVrefTrg */
          if (u8Trigger)
            SIM_SOPT7 |= SIM_SOPT7_ADC1ALTTRGEN_MASK | SIM_SOPT7_ADC1TRGSEL(u8Trigger);	
	}
}
/****************************************************************************************************/
/**
* \brief  Calibrates ADC -- NOTE: Run this algorithm after vfnADCConfig
* \param  uint8_tIndex  --> Configuration used 
* \return none  
* \todo     
*/

/****************************************************************************************************/
void vfnADCCal (uint8_t u8Index)
{
  uint16_t u16CalVar = 0;
  
  /** Enable software conversion */
  ADC_SC2_REG(sADC[u8Index].pADCBase) &=  ~ADC_SC2_ADTRG_MASK;
  
  /** Configuration for best calibration results set hw average to maximum */
  ADC_SC3_REG(sADC[u8Index].pADCBase) &= ( ~ADC_SC3_ADCO_MASK & ~ADC_SC3_AVGS_MASK );
  ADC_SC3_REG(sADC[u8Index].pADCBase) |= ADC_HW_AVG_32SAMPLES;
    
  /** 1 -> Clear any existing calibration failed flag */
  ADC_SC3_REG(sADC[u8Index].pADCBase) |= ADC_SC3_CALF_MASK ;  
  
  /** 2 -> Initialize hardware calibration */
  ADC_SC3_REG(sADC[u8Index].pADCBase) |= ADC_SC3_CAL_MASK ;
  
  /** 3 -> Wait until calibration ends */
  while ( ADC_SC3_REG(sADC[u8Index].pADCBase) & ADC_SC3_CAL_MASK );
   
  /** 4 -> Check for calibration errors */
   if (ADC_SC3_REG(sADC[u8Index].pADCBase) & ADC_SC3_CAL_MASK)
   {
//      printf ("Calibration error ");
   }
   else
   {
      /** 5 -> Generate gain calibration values */     
      /** plus-side calibration values */
      u16CalVar = 0;                                                    // Clear calibration value 
      u16CalVar += ADC_CLP0_REG(sADC[u8Index].pADCBase);                // Add plus-side calibration values
      u16CalVar += ADC_CLP1_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLP2_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLP3_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLP4_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLPS_REG(sADC[u8Index].pADCBase);       
      u16CalVar  =  u16CalVar/2;                                        // Divide the calibration variable by 2
      u16CalVar |=  0x8000;                                             // Set the MSB 
      ADC_PG_REG(sADC[u8Index].pADCBase) = ADC_PG_PG(u16CalVar);        // Store the value in the plus-side gain 
      /** minus-side calibration values */
      u16CalVar = 0;                                                    // Clear calibration value 
      u16CalVar += ADC_CLM0_REG(sADC[u8Index].pADCBase);                // Add minus-side calibration values
      u16CalVar += ADC_CLM1_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLM2_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLM3_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLM4_REG(sADC[u8Index].pADCBase);
      u16CalVar += ADC_CLMS_REG(sADC[u8Index].pADCBase);       
      u16CalVar  =  u16CalVar >> 1;                                     // Divide the calibration variable by 2
      u16CalVar |=  0x8000;                                             // Set the MSB 
      ADC_MG_REG(sADC[u8Index].pADCBase) = ADC_MG_MG(u16CalVar);        // Store the value in the minus-side gain 

      /** 6 --> Clear the calibration bit */
      ADC_SC3_REG(sADC[u8Index].pADCBase) &= ~ADC_SC3_CAL_MASK ;
   }
   
   ADC_SC3_REG(sADC[u8Index].pADCBase) = sADC[u8Index].u8HwAverage;
   ADC_SC2_REG(sADC[u8Index].pADCBase) = (uint8_t)(sADC[u8Index].u16HwCmpVrefTrgDMA);
}

/****************************************************************************************************/
/**
* \brief  ADC0 interrupt service routine
* \param  none
* \return none  
* \todo     
*/

void vfnAdc0Isr (void)
{
	if (ADC0_SC2 & ADC_SC2_DMAEN_MASK)
	{
          if (ADC0_SC1A & ADC_SC1_COCO_MASK)
                {
                  (void)(uint16_t)ADC0_RA;	
                  ADC0_SC1A ^= ADC_SC1_COCO_MASK;
		}
                        
		if (ADC0_SC1B & ADC_SC1_COCO_MASK)
                {
                  (void)(uint16_t)ADC0_RB;
                  ADC0_SC1B ^= ADC_SC1_COCO_MASK;
                } 
	}
	else
	{
          if (ADC0_SC1A & ADC_SC1_COCO_MASK)
          {
            sADCControl.gu16LatestResult[0] = (uint16_t)ADC0_RA;	
            ADC0_SC1A ^= ADC_SC1_COCO_MASK;
          }
				
          if (ADC0_SC1B & ADC_SC1_COCO_MASK)
          {
            sADCControl.gu16LatestResult[1] = (uint16_t)ADC0_RB; 	
            ADC0_SC1B ^= ADC_SC1_COCO_MASK;
          } 
	}

  ADC0_COCO_EVENT;
}

/****************************************************************************************************/
/**
* \brief  ADC1 interrupt service routine
* \param  none
* \return none  
* \todo     
*/

void vfnAdc1Isr (void)
{
	/** If DMA is enable do not store the result */
	if (ADC1_SC2 & ADC_SC2_DMAEN_MASK)
	{
          if (ADC1_SC1A & ADC_SC1_COCO_MASK)
                {
                  (void)(uint16_t)ADC1_RA;	
                  ADC1_SC1A ^= ADC_SC1_COCO_MASK;
		}
                        
		if (ADC1_SC1B & ADC_SC1_COCO_MASK)
                {
                  (void)(uint16_t)ADC1_RB;
                  ADC1_SC1B ^= ADC_SC1_COCO_MASK;
                } 
	}
	else
	{
          if (ADC1_SC1A & ADC_SC1_COCO_MASK)
          {
            sADCControl.gu16LatestResult[2] = (uint16_t)ADC1_RA;	
            ADC1_SC1A ^= ADC_SC1_COCO_MASK;
          }
				
          if (ADC1_SC1B & ADC_SC1_COCO_MASK)
          {
            sADCControl.gu16LatestResult[3] = (uint16_t)ADC1_RB; 	
            ADC1_SC1B ^= ADC_SC1_COCO_MASK;
          } 
	}

  ADC1_COCO_EVENT;
 
  return;
}
