/******************************************************************************
 ******************************************************************************
  文 件 名   : usr_adc.c
  使用说明   : 替换原有文件中对应函数

  0~1.2V io  线性对应   0~1024   推荐使用0.2V~1.1V区间
2.2~3.6V vcc 线性对应 408~ 681   

推荐用户使用固定增益1 参考电平(VCM) 选择0.60
******************************************************************************/
#include "sdk_include.h"
#include "user_adc.h"
#include "app_trace.h"
#include "app_config.h"
#include "cpu_deepsleep.h"
#include "cpu_flash.h"
#include "misc.h"
void enter_standbye(deep_sleep_retain type);
typedef struct _adc_env{
	int adc_offset;
	int adc_inited;
	user_adc_cbk adc_cbk;
}adc_env;

adc_env adc_envir={
	.adc_offset = 0,
	.adc_inited = 0,	
	.adc_cbk = NULL
};


/*****************************************************************************
 函 数 名  : read_otp_adc_param
 功能描述  : 加载adc opt参数
 输入参数  : void  
 输出参数  : 无
 返 回 值  : 
*****************************************************************************/
void read_otp_adc_param(void)
{
	uint16_t adc_value = 0;
	uint8_t adc_buf[4];

	if( TRUE == flash_otp_read(2,0, 3, adc_buf) ){
		if(adc_buf[2] == 0xAA){
			adc_value 	= adc_buf[1];
			adc_value 	<<= 8;
			adc_value 	+= adc_buf[0];
			adc_envir.adc_offset = 626 - adc_value;
			return;
		}
	}

	adc_envir.adc_offset = 0;

}

uint32_t get_dac_value(void)
{
	uint32_t val;
	int32_t adc_val = GPADC->GPADC_DATA + adc_envir.adc_offset;
	if(adc_val < 0)
		adc_val = 0;
	if(adc_val > 1024)
		adc_val = 1024;
	
	val = adc_val;
	return val;
}

void gpio_dac_configuration(gpio_pin pin)
{
   gpadc_init_type_def GPADC_InitStructure;
   memset(&GPADC_InitStructure, 0, sizeof(GPADC_InitStructure));   		//GPADC初始化清0操作
   
	if(adc_envir.adc_inited == 0){
		read_otp_adc_param();
		adc_envir.adc_inited = 1;
	}
	
//增益参考电压请不要修改	
	GPADC_InitStructure.gpadc_gain			 	= GPADC_Gain_1;
	GPADC_InitStructure.gpadc_vcm_sel			= GPADC_VCM_060;
	
   if(pin == GPIO_INVAILD){
//		dbg_printf("Bat Service\n");
	   GPADC_InitStructure.gapdc_ch_sel 		= GPADC_CH_CELL;		//GPADC选择GPIO输入模式
//	   GPADC_InitStructure.gpadc_in_mode 		= GPADC_SINGLE_IN;		//设置GPIO为单端输入信号
//	   GPADC_InitStructure.gpadc_sel1			= (GPIO_Ch_Pin_TypeDef)pin;
//	   GPADC_InitStructure.gpadc_sel2			= (GPIO_Ch_Pin_TypeDef)pin;
	   GPADC_InitStructure.gpadc_sample_rate	= GPADC_SAMPLERATE_16k;	//设置采样率为8k
	   GPADC_InitStructure.gpadc_write_in_mode  = GPADC_MODE_MANY;	  	//设置模式为多次转换数据写入gpadc_data
	   GPADC_InitStructure.gpadc_en				= GPADC_OPEN;			//使能GPADC
	   gpadc_init(&GPADC_InitStructure);
   }else{
	   	if(pin > GPIOA_Pin7){
			dbg_printf("Only Surport GPIOA_0 ... GPIOA_7\n");
			return;
		}
		gpio_remap_config(pin,GPIO_Remap_ADC);
		GPADC_InitStructure.gapdc_ch_sel	 		= GPADC_CH_GPIO;		//GPADC选择GPIO输入模式
		GPADC_InitStructure.gpadc_in_mode	 		= GPADC_SINGLE_IN;	  	//设置GPIO为单端输入信号
		GPADC_InitStructure.gpadc_sel1				= pin;
		GPADC_InitStructure.gpadc_sel2				= pin;
		GPADC_InitStructure.gpadc_sample_rate		= GPADC_SAMPLERATE_4k;   //设置采样率为8k
		GPADC_InitStructure.gpadc_write_in_mode  	= GPADC_MODE_MANY;	   	//设置模式为多次转换数据写入gpadc_data
		GPADC_InitStructure.gpadc_en				= GPADC_OPEN; 		   	//使能GPADC
		gpadc_init(&GPADC_InitStructure);
   }
}


/*****************************************************************************
 函 数 名  : gpadc_irq_configuration
 功能描述  : 中断模式处理ADC
 输入参数  : gpio_pin pin  
 输出参数  : 无
 返 回 值  : 
*****************************************************************************/
void gpadc_irq_configuration(gpio_pin pin)
{
	gpadc_init_type_def gpadc_init_structure;
	memset(&gpadc_init_structure, 0, sizeof(gpadc_init_structure));//GPADC初始化清0操作

	if(adc_envir.adc_inited == 0){
		read_otp_adc_param();
		adc_envir.adc_inited = 1;
	}

	gpadc_init_structure.gpadc_gain 			 = GPADC_Gain_1;		 	//采集电压内部增益必须设置为2
	gpadc_init_structure.gpadc_vcm_sel			 = GPADC_VCM_060;		 	//设置VCM为0.8
	
   if(pin == GPIO_INVAILD){
	   gpadc_init_structure.gapdc_ch_sel 			= GPADC_CH_CELL;		//GPADC选择GPIO输入模式
	   gpadc_init_structure.gpadc_sample_rate		= GPADC_SAMPLERATE_2k;	//设置采样率为8k
	   gpadc_init_structure.gpadc_write_in_mode  	= GPADC_MODE_CONTINU;	 //设置模式为多次转换数据写入gpadc_data
	   gpadc_init_structure.gpadc_fifo_threshold 	= 8;					//设置fifo深度为8
	   gpadc_init_structure.gpadc_en				= GPADC_OPEN;			//使能GPADC
	   gpadc_init(&gpadc_init_structure);
   }else{
	   	if(pin > GPIOA_Pin7){
			dbg_printf("Only Surport GPIOA_0 ... GPIOA_7\n");
			return;
		}
		gpio_remap_config(pin,GPIO_Remap_ADC);
		gpadc_init_structure.gapdc_ch_sel	 		= GPADC_CH_GPIO;			//GPADC选择GPIO输入模式
		gpadc_init_structure.gpadc_in_mode	 		= GPADC_SINGLE_IN;	  		//设置GPIO为单端输入信号
		gpadc_init_structure.gpadc_sel1				= pin; // ADC A +
		gpadc_init_structure.gpadc_sel2				= pin; // ADC A -
		gpadc_init_structure.gpadc_sample_rate		= GPADC_SAMPLERATE_2k;   	//设置采样率为8k
		gpadc_init_structure.gpadc_write_in_mode  	= GPADC_MODE_CONTINU;	   	//设置模式为多次转换数据写入gpadc_data
		gpadc_init_structure.gpadc_fifo_threshold	= 8;						//设置fifo深度为16
		gpadc_init_structure.gpadc_en				= GPADC_OPEN; 		   		//使能GPADC
		gpadc_init(&gpadc_init_structure);
   }
}



uint16_t get_meadian_adc(gpio_pin adc_pin,uint16_t *adc_buf,uint8_t n_dat)
{
    uint8_t bat_cnt = 0;
	uint16_t loop;
	uint16_t i;
	uint16_t bat_val = 0;
	loop = 0;		
	
	cpu_prevent_sleep_set(AMIC_ONGOING);
	for(bat_cnt=0;bat_cnt<n_dat;)
	{
		gpio_dac_configuration(adc_pin);
		for(i=0;i<100;i++);
		adc_buf[bat_cnt] = get_dac_value();
		if(adc_buf[bat_cnt] == 0){
//			dbg_printf("empty power %d\n",adc_buf[bat_cnt]);
			loop ++;
			if(loop < 5)		//连续5次值为0，那么实际adc值应该是0
				continue;
		}
#if DEBUG_ADC_ENABLE	
		dbg_printf("%d ",adc_buf[bat_cnt]);
#endif
		bat_cnt++;
		loop = 0;
	}
	cpu_prevent_sleep_clear(AMIC_ONGOING);
	
	bat_val 	= get_meadian(adc_buf,n_dat);
	return bat_val;
}


/*****************************************************************************
 函 数 名  : get_cell_data
 功能描述  : 获取电池 adc值
 输入参数  : void  
 输出参数  : 无
 返 回 值  : 
 调用函数  : 
*****************************************************************************/
uint16_t get_cell_data(void)
{
    uint16_t bat_adc_buf[13];
	uint16_t bat_val 				= 0;
	bat_val = get_meadian_adc(GPIO_INVAILD,bat_adc_buf,13);
	return bat_val;
}

/*****************************************************************************
 函 数 名  : get_adc_data
 功能描述  : 获取adc数据
 输入参数  : gpio_pin adc_pin  
 输出参数  : 无
 返 回 值  : 
 调用函数  : 
*****************************************************************************/
uint16_t get_adc_data(gpio_pin adc_pin)
{
	uint16_t bat_val 				= 0;
    uint16_t bat_adc_buf[13];
	bat_val = get_meadian_adc(adc_pin,bat_adc_buf,13);
	return bat_val;
}


/*****************************************************************************
 函 数 名  : get_cell_volum
 功能描述  :  //Vmc = 0.6V 增益为2	条件下	返回电压值
 输入参数  : void  
 输出参数  : 无
 返 回 值  : 
 调用函数  : 
 被调函数  : 
*****************************************************************************/
uint16_t get_cell_volum(void)
{
    uint16_t bat_volum 				= 0;
	uint16_t bat_val 				= 0;
	bat_val =  get_cell_data();
	bat_volum = bat_val*3 + 1216;
	return bat_volum;
}



/*****************************************************************************
 函 数 名  : get_adc_volum
 功能描述  : 获取adc电压值
 输入参数  : gpio_pin pin          				检测io GPIOA_Pin0 ~ GPIOA_Pin7
             uint32_t up_rigister  				上电阻系数
             uint32_t down_rigister  			下电阻系数
             电压超过1.2V必须用电阻分压，
            	如		检测3.7V Li电池，分压电阻设为1M 3M，系数即为 
            		up_rigister 	= 1
            		down_rigister 	= 3;
 输出参数  : 无
*****************************************************************************/
uint16_t get_adc_volum(gpio_pin adc_pin,uint32_t up_rigister,uint32_t down_rigister)
{
    uint16_t adc_volum 	= 0;
	uint16_t bat_val = 0;
	uint32_t bat_tmp = 0;
#if DEBUG_ADC_ENABLE	
	dbg_printf("\n");
#endif
	bat_val = get_adc_data(adc_pin);
//	dbg_block_printf("bat_val %d\n",bat_val);
	if(adc_pin == GPIO_INVAILD){
		
// 1.8V:320
// 2.2V:408
// 3.6V:680
		bat_tmp = (bat_val-320)*1800/(680-320);
		adc_volum = bat_tmp + 1800;

	}else{
		if(down_rigister){
			adc_volum = ((bat_val*1200)/1024)*((down_rigister+up_rigister)/down_rigister);
		}else{
			adc_volum = (bat_val*1200)/1024;
		}
	}
	return adc_volum;
}

void adc_example_isr(void)
{
	uint32_t sum = 0;
	uint8_t i;
	int16_t buf[8];
	
	uint32_t fifo_level = gpadc_receive_fifo_level();
	if(fifo_level >= 8)	// 和初始化深度匹配即可
	{
		for(i=0;i<8;i++){
			buf[0] = GPADC->GPADC_FIFODATA;
			buf[1] = GPADC->GPADC_FIFODATA ;
			buf[2] = GPADC->GPADC_FIFODATA;
			buf[3] = GPADC->GPADC_FIFODATA;
			buf[4] = GPADC->GPADC_FIFODATA;
			buf[5] = GPADC->GPADC_FIFODATA;
			buf[6] = GPADC->GPADC_FIFODATA;
			buf[7] = GPADC->GPADC_FIFODATA;
		}
		
		for(i=0;i<8;i++){
			sum += buf[i];
		}
		
		dbg_block_printf("%d\n",sum >> 3);
	}
}
/*****************************************************************************
 函 数 名  : GPADC_IRQHandler
 功能描述  : adc转换完成中断函数
 输入参数  : 无
 输出参数  : 无
*****************************************************************************/
void GPADC_IRQHandler()
{
//	uint16_t gpadc_data;
	if(adc_envir.adc_cbk){
		adc_envir.adc_cbk();
	}
	
	gpadc_clear_it_pending_bit(GPADC_IT_ALERT_CLEAR);//清除中断标志位
}

void adc_register_handler(user_adc_cbk adc_cbk)
{
	adc_envir.adc_cbk = adc_cbk;
}

/*****************************************************************************
 函 数 名  : user_int_adc_example
 功能描述  : adc 中断模式示例代码
 输入参数  : void  
*****************************************************************************/
void user_int_adc_example(void)
{
//【1】 开启ADC 时钟 
	system_clock_config(ws_adc,ENABLE);

//【2】 开启ADC 中断
	nvic_init_type_def nvic_init_structure;
	NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
	nvic_init_structure.nvic_irq_channel_preemption_priority	= 1;
	nvic_init_structure.nvic_irq_channel_subpriority			= 0;
	nvic_init_structure.nvic_irq_channel_cmd					= ENABLE; 
	nvic_init_structure.nvic_irq_channel 						= GPADC_IRQn;
	nvic_init(&nvic_init_structure);
	adc_register_handler(adc_example_isr);
//【3】中断模式配置
	gpadc_irq_configuration(GPIOA_Pin0);

//【4】 使能ADC 转换完成中断
	gpadc_it_config(GPADC_IT_ALERT_EN,ENABLE);

//【5】一旦温度校准重新配置了ADC后，用户需要再次配置，因此此处用while(1) 来阻止
	while(1){

	}
}

// 推荐使用方式
/*****************************************************************************
 函 数 名  : user_adc_inquire_example
 功能描述  : adc查询模式示例代码
 输入参数  : void  
 输出参数  : 无
*****************************************************************************/
void user_adc_inquire_example(void)
{
	int i;
//【1】 开启ADC 时钟 
	system_clock_config(ws_adc,ENABLE);

//【2】 查询模式
	while(1){
		dbg_block_printf("gpio A1:: ");
		get_adc_volum(GPIOA_Pin1,0,0);
		
		dbg_block_printf("batt_cc:: ");
		get_adc_volum(GPIO_INVAILD,0,0);

		for(i=0;i<2000000;i++);
	}
}


