// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// File name    : adc_drv.c
// Version      : V0.1
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include "type.h"
#include "adc_drv.h"
#include "cpm_drv.h"
#include "debug.h"
#include "delay.h"

ADC_TypeDef *ADCx;

/*******************************************************************************
* Function Name  : adc_module_dis
* Description    : ADC模块禁用
* 				         注意：
* 				    None
* Input          : None
* Output         : None
* Return         : None
******************************************************************************/
static void adc_module_dis(void)
{
	ADC->CR = ADDIS;
	while (0 != ADC->CR);

}
/*******************************************************************************
* Function Name  : adc_module_en
* Description    : ADC模块使能
* 				         注意：
* 				    None
* Input          : None
* Output         : None
* Return         : None
******************************************************************************/
static void adc_module_en(void)
{
	ADC->CR = ADEN;
	while (ADRDY != (ADC->ISR& ADRDY));
}


/*******************************************************************************
* Function Name  : ADC_Cmd
* Description    : Enable or Disable ADC
* Input          :  - en：ADC_DIS----disable ADC   ADC_EN----enable ADC
*
* Output         : None
* Return         : None
******************************************************************************/
void ADC_Cmd(BOOL en)
{
	if (en == ADC_DIS)
	{
		adc_module_dis();
	}
	else
	{
		adc_module_en();
	}
}



/*******************************************************************************
* Function Name  : ADC_StartConv
* Description    : ADC 转换启动
* Input          : None
*
* Output         : None
* Return         : None
******************************************************************************/
void ADC_StartConv(void)
{
	UINT32 adccr;
	adccr = ADC->CR;
	adccr |= ADSTART;
	ADC->CR = adccr;
}

/*******************************************************************************
* Function Name  : ADC_StopConv
* Description    : ADC 转换停止
* Input          : None
*
* Output         : None
* Return         : None
******************************************************************************/
void ADC_StopConv(void)
{
	ADC->CR |= ADSTP;
	while (ADEN !=ADC->CR);
}

static void ADC_SetStructure(ADC_InitTypeDef *adc)
{
	adc->align       = ADC_RIGHT_ALIGN;
	adc->clk_div     = ADC_CLK_DIV_4;
	adc->conv        = ADC_CONTINUOUS_CONV_MODE;
	adc->overrun     = ADC_OVERRUN_MODE_LAST;
	adc->resolution  = ADC_RESOLUTION_12BIT;
	adc->smp_time    = 0x20;
	adc->stab_time   = 0x20;
	adc->vref        = ADC_INTERNAL_VREF;
	adc->int_kind    = 0;
}


/*******************************************************************************
* Function Name  : ADC_Init
* Description    : ADC initialize
* Input          :  - cfgr1  : ADC configuration register 1
*                   - cfgr2  : ADC configuration register 2
*                   - chselr1: ADC channel selection register 1
*                   - chselr2: ADC channel selection register 2
*                   - smpr   : ADC sampling time register
*
* Output         : None
* Return         : None
******************************************************************************/
void ADC_Init(ADC_TypeDef *ADCn,u8 channel)
{
	ADC_InitTypeDef adc;
	ADC_SetStructure(&adc);
	
	ADCx=ADCn;	// Do not remove
	
	_reg_modify(ADCx->CFGR2,0xFFFFF0FF,((adc.clk_div)&0x0F)<<8);
	
	_reg_modify(ADCx->CFGR2,0xFFFFFF00,((adc.stab_time))&0xFF);
	
	_reg_modify(ADCx->SMPR,0xFFFFFF00,(adc.smp_time)&0xFF);
	
	_reg_modify(ADCx->CFGR1,0xFFFFFCFF,((adc.resolution)&0x3)<<8);
	
	//ADC转换队列设置
	_reg_modify(ADCx->CFGR1,0xF8FFFFFF,((0)&0x7)<<24);
	
	_reg_modify(ADCx->CHSELR1,0xFFFFFFE0,(channel)&0x1F);
	
	if(adc.vref == ADC_EXTERNAL_VREF)
		_bit_set(ADCx->CFGR1,ADC_EXTERNAL_VREF_MASK);
	else
		_bit_clr(ADCx->CFGR1,ADC_EXTERNAL_VREF_MASK);
	
	if(adc.conv == ADC_CONTINUOUS_CONV_MODE)
		_bit_set(ADCx->CFGR1,ADC_CONTINUOUS_CONVERSION_MASK);
	else
		_bit_clr(ADCx->CFGR1,ADC_CONTINUOUS_CONVERSION_MASK);
	
	if(adc.overrun == ADC_OVERRUN_MODE_LAST)
		_bit_set(ADCx->CFGR1,ADC_OVERRUN_MODE_MASK);
	else
		_bit_clr(ADCx->CFGR1,ADC_OVERRUN_MODE_MASK);
	
	if(adc.align == ADC_RIGHT_ALIGN)
		_bit_clr(ADCx->CFGR1,ADC_LEFT_ALIGNMENT_MASK);
	else
		_bit_set(ADCx->CFGR1,ADC_LEFT_ALIGNMENT_MASK);
	
	// Select channel
	_reg_write(ADCx->CHSELR3,(channel)&0x0000FFFF);
	
	if(adc.int_kind != 0)
	{
		NVIC_Init(3, 3, QADC_IRQn, 2);
		_reg_modify(ADCx->IER,0xFFFFFF60,(adc.int_kind)&0x9F);
	}
	
	//adc_module_en();
	ADC_Cmd(1);
}
/*******************************************************************************
* Function Name  : ADC_GetConversionValue
* Description    : ADC 采样处理函数
* Input          :  - ADC_CHx：ADC采用通道  对应芯片引脚 ADC_INx (其中x取值0~7)
*
* Output         : None
* Return         : 对应引脚AD采集的值
******************************************************************************/

u16 ADC_GetValue(ADC_TypeDef *ADCx,u8 channel)
{
	UINT32 adcisr = 0,i;

	_reg_write(ADCx->CHSELR3,(channel)&0x0000FFFF);
	_reg_modify(ADCx->CHSELR1,0xFFFFFFE0,(channel)&0x1F);

	for (i = 0; i < 9; i++)
	{
		ADC_StartConv();
		while (EOSEQ !=(ADCx->ISR & EOSEQ));
		ADCx->ISR |= EOSEQ;
		ADC_StopConv();

		if(i == 0)
		{// Discard the 1'st char
			adcisr = (ADCx->uFIFO_DAT.FIFO)&0xFFFF;
			adcisr = 0;
			continue;
		}
		adcisr += (ADCx->uFIFO_DAT.FIFO)&0xFFFF;
	}

	return ((adcisr>>3)&0x0fff);
}
