/**************************************************************************//**
 * @file     adc.c
 * @version  V1.00
 * $Revision: 3 $
 * $Date: 14/07/28 13:27p $
 * @brief    N572 ADC driver source file
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/

#include  "N572.h"

#define AD_ISR_TIMES	64	// for 48K sampling rate, around 93.75Hz


static void AccuPCMDataNx8Times(int32_t *pi32TotalAvePCMValue,uint16_t u16SampleCountX8)	//	i32TotalAvePCMValue will be reset firstly inside function
{
		uint16_t	u16CalibrationSampleCount = 0;	
		
		*pi32TotalAvePCMValue = 0 	;  // accumulate from zero.
		while(1)
		{	// get several PCM samples for checking later
			if (ADC_GET_INT_FLAG(ADC, ADC_ADF_INT))
			{
				ADC_CLR_INT_FLAG(ADC, ADC_ADF_INT);
				*pi32TotalAvePCMValue = (int16_t)ADC_GET_CONVERSION_DATA(ADC, 0)+	
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 1)+	
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 2)+
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 3)+
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 4)+
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 5)+
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 6)+	
																(int16_t)ADC_GET_CONVERSION_DATA(ADC, 7);

				u16CalibrationSampleCount++;
				if (u16CalibrationSampleCount >= u16SampleCountX8)
					break;
			}
		}

}

/** @addtogroup N572_Device_Driver N572 Device Driver
  @{
*/

/** @addtogroup N572_ADC_Driver ADC Driver
  @{
*/


/** @addtogroup N572_ADC_EXPORTED_FUNCTIONS ADC Exported Functions
  @{
*/

/**
  * @brief      ADC open function
  * @param[in]  adc Base address of ADC module
  * @return     None
  * @details    
  */
void ADC_Open(ADC_T *adc)
{

}

/**
  * @brief      ADC close function
  * @param[in]  adc Base address of ADC module
  * @return     None
  * @details   
  */
void ADC_Close(ADC_T *adc)
{   
      
}

/**
  * @brief      Enable ADC interrupts
  * @param[in]  adc Base address of ADC module
  * @param[in]  u32Mask is interrupt flags.
  *             - \ref ADC_ADF_INT 
  *             - \ref ADC_CMP0_INT 
  *             - \ref ADC_CMP1_INT
  * @return     None.
  * @details    ADC_ADF_INT is generated whenever A/D conversion end.
  *             ADC_CMP0_INT is generated whenever compared 0 matches
  *             the setting of count and condition.
  *             ADC_CMP1_INT is generated whenever compared 1 matches
  *             the setting of count and condition.             
  */
void ADC_EnableInt(ADC_T *adc, uint32_t u32Mask)
{
	if (u32Mask&ADC_ADF_INT)
	{
		adc->CTL |= ADC_CTL_ADCIE_Msk;
	}
	if (u32Mask&ADC_CMP0_INT)
	{
		adc->CMP[0] |= ADC_CMP_ADCMPIE_Msk;
	}
	if (u32Mask&ADC_CMP1_INT)
	{
		adc->CMP[1] |= ADC_CMP_ADCMPIE_Msk;
	}
}  

/**
  * @brief      Disable ADC interrupts
  * @param[in]  adc Base address of ADC module
  * @param[in]  u32Mask is interrupt flags.
  *             - \ref ADC_ADF_INT 
  *             - \ref ADC_CMP0_INT 
  *             - \ref ADC_CMP1_INT
	* @return     None.
  * @details    ADC_ADF_INT is generated whenever A/D conversion end.
  *             ADC_CMP0_INT is generated whenever compared 0 matches
  *             the setting of count and condition.
  *             ADC_CMP1_INT is generated whenever compared 1 matches
  *             the setting of count and condition.             
  */
void ADC_DisableInt(ADC_T *adc, uint32_t u32Mask)
{
	if (u32Mask&ADC_ADF_INT)
	{
		adc->CTL &= ~ADC_CTL_ADCIE_Msk;
	}
	if (u32Mask&ADC_CMP0_INT)
	{
		adc->CMP[0] &= ~ADC_CMP_ADCMPIE_Msk;
	}
	if (u32Mask&ADC_CMP1_INT)
	{
		adc->CMP[1] &= ~ADC_CMP_ADCMPIE_Msk;
	}
}

/**
  * @brief      This API will enable regulator for gain noiseless performance.
  * @param[in]  adc Base address of ADC module
  * @return     None
  * @details    Regulator is enabled: PGC power source is from regulator-> 0.85* AVDD.
  *                                   Microphone bias is from regulator-> 0.85*AVDD. 
  */
void ADC_EnableRegulator(ADC_T *adc)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	adc->PGCTL |= (ADC_PGCTL_MICE_Msk|ADC_PGCTL_APPS_Msk);
#endif
}

/**
  * @brief      This API will disable regulator.
  * @param[in]  adc Base address of ADC module
  * @return     None
  * @details    Regulator is disabled: PGC power source is from regulator->AVDD.
  *                                    Microphone bias is from regulator->AVDD. 
  */
void ADC_DisableRegulator(ADC_T *adc)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	adc->PGCTL &= ~(ADC_PGCTL_MICE_Msk|ADC_PGCTL_APPS_Msk);
#endif	
}

/**
  * @brief      This API set RC vlaue that will accelerate regulator wake up.
  * @param[in]  adc Base address of ADC module
  * @param[in]  u32ACC_R Resistance value for AVDD accelerating
  * @param[in]  u32WAKE_R RC value for regulator fast wake up
  * @return     None
  * @details    "u32ACC_R = ADC_CTRS_R10K" and "u32WAKE_R = ADC_FWU_R8K" is fast path for regulator. After 
  *	            fast path, user must reset "u32ACC_R = ADC_CTRS_R600K" and "u32WAKE_R = ADC_FWU_R400K" for 
  *		          regulator normal path, then ADC can get valid value.
  * @note       User must call it before mic-bias calculating and ADC setting if regulator is enabled.
  */
void ADC_SetRegulatorRC(ADC_T *adc, uint32_t u32ACC_R, uint32_t u32WAKE_R)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	adc->MIBSCTL = u32ACC_R|u32WAKE_R;
#endif
}

/**
  * @brief      Get current RC value of AVDD accelerating and regulator.
  * @param[in]  adc Base address of ADC module
  * @param[out] p32ACC_R save resistance value of AVDD accelerating
  * @param[out] pu32WAKE_R save RC value of regualtor for fast wake up
  * @return     None
  */
void ADC_GetRegulatorRC(ADC_T *adc,	uint32_t *p32ACC_R, uint32_t *pu32WAKE_R)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	*p32ACC_R = adc->MIBSCTL&0x00000030;
	*pu32WAKE_R = adc->MIBSCTL&0x00000003;
#endif
}

/**
  * @brief      Set PGA gain1
  * @param[in]  adc Base address of ADC module
  * @param[in]  i32PGAGain1IndB gain value
  * @return     Real gain value
  * @details    In F072 chip, it's range is from 20dB to 40dB in 1dB step size.
  *             In F065 chip, it's setting are -6, 0, 8, 14dB.
  */
int32_t ADC_SetPGAGain1dB(ADC_T *adc, int32_t i32PGAGain1IndB)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	if(i32PGAGain1IndB < 20)
		i32PGAGain1IndB = 20;
	else if(i32PGAGain1IndB > 40)
		i32PGAGain1IndB = 40;
	adc->PGCTL = (adc->PGCTL&~ADC_PGCTL_PAG_I_Msk)|((i32PGAGain1IndB-20) << ADC_PGCTL_PAG_I_Pos);
	
#else
	uint32_t u32Gain; 
	if(i32PGAGain1IndB <= -3)
	{
		i32PGAGain1IndB = -6;
		u32Gain = 0;
	}
	else if(i32PGAGain1IndB <= 4)
	{
		i32PGAGain1IndB = 0;
		u32Gain = 1;
	}
	else if(i32PGAGain1IndB <= 11)
	{
		i32PGAGain1IndB = 8;
		u32Gain = 2;
	}
	else 
	{
		i32PGAGain1IndB = 14;
		u32Gain = 3;
	}
	adc->PGCTL = (adc->PGCTL&~ADC_PGCTL_PAG_I_Msk)|(u32Gain << ADC_PGCTL_PAG_I_Pos);

#endif
	return i32PGAGain1IndB;
}

/**
  * @brief      Set PGA gain2
  * @param[in]  adc Base address of ADC module
  * @param[in]  i32PGAGain2IndB gain value  
  * @return     Real gain value
  * @details    In F072 chip, it's range is from 0dB to 30dB in 10dB step size.
  *             In F065 chip, it's range is from 16dB to 46dB in 1dB step size, except 0dB.
  */
int32_t ADC_SetPGAGain2dB(ADC_T *adc, int32_t i32PGAGain2IndB)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	if(i32PGAGain2IndB < 0)
		i32PGAGain2IndB = 0;
	else if(i32PGAGain2IndB > 30)
		i32PGAGain2IndB = 30;
	adc->PGCTL = (adc->PGCTL&~ADC_PGCTL_PAG_II_Msk)|((i32PGAGain2IndB/10) << ADC_PGCTL_PAG_II_Pos);
#else
	if ((i32PGAGain2IndB - 15) <= 0)
	{
		adc->PGCTL = (adc->PGCTL&~ADC_PGCTL_PAG_II_Msk);
		return 0;
	}
	else if (i32PGAGain2IndB > 46)
		i32PGAGain2IndB = 46;
	
	adc->PGCTL = (adc->PGCTL&~ADC_PGCTL_PAG_II_Msk)|((i32PGAGain2IndB-15) << ADC_PGCTL_PAG_II_Pos);
	
#endif	
	return i32PGAGain2IndB;
}

/**
  * @brief      Get PGA gain1
  * @param[in]  adc Base address of ADC module
  * @return     Real gain value
  * @details    In F072 chip, it's range is from 20dB to 40dB in 1dB step size.
  *             In F065 chip, it's setting are -6, 0, 8, 14dB.
  */
int32_t ADC_GetPGAGain1dB(ADC_T *adc)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	return ((adc->PGCTL >> ADC_PGCTL_PAG_I_Pos) +20);
#else
	int8_t i8Gain[4] = {-6, 0, 8, 14};
	
	return (int32_t)i8Gain[(adc->PGCTL >> ADC_PGCTL_PAG_I_Pos)];
#endif
}

/**
  * @brief      Get PGA gain2
  * @param[in]  adc Base address of ADC module
  * @return     Real gain value
  * @details    In F072 chip, it's range is from 0dB to 30dB in 10dB step size.
  * @details    In F072 chip, it's range is from 0dB to 30dB in 10dB step size.
  *             In F065 chip, it's range is from 16dB to 46dB in 1dB step size, except 0dB.
  */
int32_t ADC_GetPGAGain2dB(ADC_T *adc)
{
#if (__CHIP_SERIES__ == __N572F072_SERIES__)
	return ((adc->PGCTL&0xc) >> ADC_PGCTL_PAG_II_Pos)*10;
#else
	uint8_t u8Gain;
	u8Gain = (adc->PGCTL&0x3e) >> ADC_PGCTL_PAG_II_Pos;
	if (u8Gain == 0)
		return 0;
	else
		return (u8Gain+15);
#endif
}

void ADC_AnalysisCalibration(ADC_T *adc)
{
	int32_t i32TotalAvePCMValue,i32TotalAvePCMValueLSB1;
	uint16_t u16Offset,u16MaskBit;
	int16_t	count;
	
	
	ADC_MUTEON_PGA(adc); //mute

#if (__CHIP_SERIES__ == __N572F065_SERIES__)
	u16Offset = 0x10	;
#else
	u16Offset = 0x100	;
#endif
	u16MaskBit = u16Offset	;
	ADC_SET_BIASCOMPENSATION(adc, u16Offset);
#if (__CHIP_SERIES__ == __N572F065_SERIES__)
	for(count = 5 ; count > 0; )
#else
	for(count = 9 ; count > 0; )
#endif
	{
		// get average DC "i32TotalAvePCMValue" which is stable
		while(1)	
		{	// get average PCM data.  "64" base on 48K sampling rate
			AccuPCMDataNx8Times(&i32TotalAvePCMValue,AD_ISR_TIMES/2)	;//	i32TotalAvePCMValue will be reset firstly inside function
			AccuPCMDataNx8Times(&i32TotalAvePCMValueLSB1,AD_ISR_TIMES/2)	;//	i32TotalAvePCMValue will be reset firstly inside function
//			if(((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) < ((AD_ISR_TIMES/2) * 0x00000008))&&((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) > ((AD_ISR_TIMES/2) * 0xffffff08))			 )
			if(((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) < ((AD_ISR_TIMES/2) * 32))&&((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) > ((AD_ISR_TIMES/2) * (-32)))			 )// if stable
//			if(((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) < ((AD_ISR_TIMES/2) * 0x00000080))&&((i32TotalAvePCMValueLSB1 - i32TotalAvePCMValue) > ((AD_ISR_TIMES/2) * 0xffffff80))			 )
			{	i32TotalAvePCMValue += i32TotalAvePCMValueLSB1	;
				break	;	
			}
		}
		// check if offset is over or not enough
		if(i32TotalAvePCMValue < 0)
		{	u16Offset &= (~u16MaskBit)	;// too much, reset current bit as 0
			i32TotalAvePCMValue *= (-1)	; // get abs value
		}
		// set next bit of offset as "1"
		u16MaskBit >>= 1 	;
		u16Offset |= u16MaskBit	;
		if(count == 1)	// LSB, need to check case 0
		{
			i32TotalAvePCMValueLSB1 = i32TotalAvePCMValue; //Keep abs value of LSB is "1"
			ADC_SET_BIASCOMPENSATION(adc, u16Offset & 0xfffe); // now to check AvePCM when LSB is "0"
			// get average PCM data.  "64" base on 48K sampling rate
			AccuPCMDataNx8Times(&i32TotalAvePCMValue,AD_ISR_TIMES);//get accumulated PCM data value.	i32TotalAvePCMValue will be reset firstly inside function
			if(i32TotalAvePCMValue < 0)
				i32TotalAvePCMValue *= (-1); // abs value of LSB is "0"
			if( i32TotalAvePCMValueLSB1 < i32TotalAvePCMValue) // if LSB is "1" closer to zero PCM
				ADC_SET_BIASCOMPENSATION(adc, u16Offset | 0x0001);// else keep: ADC.PGCR.OS = (u16Offset & 0xfffe);
		}	
		else 	
			ADC_SET_BIASCOMPENSATION(adc, u16Offset);

		count--	;
	}
	ADC_MUTEOFF_PGA(adc);
}

/*@}*/ /* end of group N572_ADC_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group N572_ADC_Driver */

/*@}*/ /* end of group N572_Device_Driver */

/*** (C) COPYRIGHT 2014 Nuvoton Technology Corp. ***/
