/***********************************************************************
 * Copyright (c)  2017 - 2022, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : adc.c
 * Description : adc source file
 * Author(s)   : Hackrtt
 * version     : V1.0
 * Modify date : 2022-07-10
 ***********************************************************************/
#include "adc.h"

void (*adcfunc_callback)(uint8_t ch_num) = { 0 };

/************************************************************************
 * Function   	: ADC_IRQHandler
 * Description	: ADC_IRQHandler ADC中断处理函数
 * Input 		: none
 * Output 		: none
 * Return		: none
 ************************************************************************/
void ADC_IRQHandler(void)
{
	uint16_t ch_num;
	ch_num = REG_ADC_ISR&0x3fff;
	if(ch_num)
	{
		if(adcfunc_callback != NULL)
		{
			(*adcfunc_callback)((ch_num));//调用中断回调，并导入通道	
		}
		REG_ADC_ICR = ch_num;    			//清除该通道的中断标志
	}
	else
	{
		REG_ADC_ICR = 0xFFFFFFFF; //清除所有中断标志
	}
}

/************************************************************************
 * Function   	: adc_init
 * Description	: adc_init 			ADC初始化
 * Input 		: uint8_t vref_sel 	ADC参考电压源选择
 *            	  uint32_t div 		ADC内部时钟分频器分频值
 *			  	  uint8_t work_mode ADC工作模式选择(0:单次模式 1：连续模式)
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_init(uint8_t vref_sel,uint32_t speed_div,uint8_t work_mode)
{
	REG_SCU_PERICLKEN |= ADC_CLKEN;			//开启ADC时钟
	REG_SCU_PERIRESET |= ADC_RESET;			//ADC复位释放
	
	REG_ADC_GCR &= ~ADC_PD_ON;    			//内部SAR ADC上电，复位值为1
	REG_ADC_GCR &= ~ADC_RST;      			//内部SAR ADC释放，复位值为1
	
	REG_ADC_GCR &= (~(1<<6));				//内部时钟分频器产生ADC时钟
	REG_ADC_CDR = speed_div;			    //设置分频参数
	
	REG_ADC_SPW = 3;						//ADC采样时间周期：4个ADC_Clk
	REG_ADC_COUNT = 4;	             		//通道切换时间（单位是ADC时钟周期）:0~255
	
		
	if(vref_sel == ADC_REF_VREFIO)
	{
		REG_SCU_IOCTRLPROTECT = 0xA5A55A5A;
		REG_SCU_PADADS |= (1<<5);			//使能PA5为模拟接口
		REG_SCU_IOCTRLPROTECT = 0xA5A55A5A;
	}
	else if(vref_sel == ADC_REF_VREF)
	{
		REG_SCU_VREFCFG |= (1<<4);									//使能VREF输出		
	}
	REG_ADC_TCRL = (REG_ADC_TCRL&(~(3<<1)))|(vref_sel<<1);			//ADC参考电压源选择
	REG_ADC_TCRL |= (1<<5);		
	
	if(work_mode)                			//0:ADC单次转换，1：ADC连续转换
	{
		REG_ADC_GCR |= (1 << 1) ;    		//ADC连续转换
	}
	else
	{
		REG_ADC_GCR &= ~(1 << 1);   		//ADC单次转换
	}
	
	REG_ADC_GCR |= (1<<5);                  //使能读数据寄存器清除
	REG_ADC_GCR |= (1<<0);					//启动ADC模块
	
}

/************************************************************************
 * Function   	: adc_close
 * Description	: adc_close ADC模块关闭
 * Input 		: none
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_close(void)
{
	REG_SCU_PERICLKEN &= ~ADC_CLKEN;		//关闭ADC时钟
	REG_SCU_PERIRESET &= ~ADC_RESET;		//ADC复位	
	
}

/************************************************************************
 * Function   	: adc_irq_init
 * Description	: adc_irq_init 		   ADC中断初始化
 * Input 		: uint8_t irq_enable   ADC中断使能选择
 *            	  uint32_t irq_type    ADC中断类型
 *			  	  void (*pfunc)()      ADC中断回调函数
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_irq_init(uint8_t irq_enable, uint32_t irq_type,void (*pfunc)(uint8_t ch))
{
	if(irq_enable == ADC_IRQ_ENABLE)
	{	
		NVIC_ClearPendingIRQ(ADC_IRQn);
		NVIC_EnableIRQ(ADC_IRQn);
		
		REG_ADC_ICR = 0xFFFF;				//清除所有中断标志	
		REG_ADC_IER |= irq_type;    		//设置中断源类型
		
		adcfunc_callback = pfunc;
	}
	else
	{
		REG_ADC_ICR |= irq_type;    		//清除设置中断源的中断标志
		REG_ADC_IER &= ~(irq_type); 		//去除中断源
		adcfunc_callback = NULL;			//移除回调
	}
}

/************************************************************************
 * Function   	: adc_channel_gpio_config
 * Description	: adc_channel_gpio_config 	ADC通道配置
 * Input 		: uint8_t channel   		ADC通道选择
 *            	  uint32_t gpio     		ADC通道对应IO管脚
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_channel_gpio_config(uint16_t channel, uint32_t gpio)
{
	REG_SCU_IOCTRLPROTECT = 0xA5A55A5A;
	if((gpio == ADC_PF0) || (gpio == ADC_PF1) || (gpio == ADC_PF2))
		REG_SCU_PADADS1 |= gpio;					//配置对应通道IO为模拟接口
	else
		REG_SCU_PADADS |= gpio;					//配置对应通道IO为模拟接口
	REG_SCU_IOCTRLPROTECT = 0xA5A55A5A;
	
	REG_ADC_GCR &= ~(0xff << 16);			
	REG_ADC_GCR |= (channel << 16);			//使能通道	

}

/************************************************************************
 * Function   	: adc_convert_start
 * Description	: adc_convert_start 使能ADC转换
 * Input 		: none
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_convert_start(void)
{
	REG_ADC_GCR &= ~(1<<10);    			//转换完成后不会自动清零，需手动写0
	REG_ADC_GCR |= (1<<10);					//开始ADC转换
}

/************************************************************************
 * Function   	: adc_convert_stop
 * Description	: adc_convert_stop 停止ADC转换
 * Input 		: none
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_convert_stop(void)
{
	REG_ADC_GCR &= ~(1<<10);    			//停止ADC转换
}

/************************************************************************
 * Function   	: adc_channel_cal
 * Description	: adc_channel_cal   通道转换，将当前通道的信息转换为实际的通道编号
 * Input 		: uint8_t ch_num    当前完成ADC采样的通道信息
 * Output 		: none				
 * Return		: uint8_t ret		实际的通道编号
 ************************************************************************/
uint8_t adc_channel_cal(uint16_t ch_num)
{
	uint8_t ret;
	switch (ch_num)   							//根据通道寄存器地址，得出对应的通道数
	{
		case 0x01	: ret = 0; break;
		case 0x02	: ret = 1; break;
		case 0x04	: ret = 2; break;
		case 0x08	: ret = 3; break;
		case 0x10	: ret = 4; break;
		case 0x20	: ret = 5; break;
		case 0x40	: ret = 6; break;
		case 0x80	: ret = 7; break;
		case 0x100	: ret = 8; break;
		case 0x200	: ret = 9; break;
		case 0x400	: ret = 10; break;
		case 0x800	: ret = 11; break;
		case 0x1000	: ret = 12; break;
		case 0x2000	: ret = 13; break;
		default		: ret = 13; break;       
	}
	return ret;
}
/************************************************************************
 * Function   	: adc_irq_get_data
 * Description	: adc_irq_get_data			获取ADC通道数据//中断专用
 * Input 		: uint8_t ch_num   		 	ADC通道选择
 * Output 		: none
 * Return		: uint16_t ret    			ADC通道有效数据及通道编号信息（高4位：通道信息、低12位：有效数据）
 ************************************************************************/
uint16_t adc_irq_get_data(uint8_t ch)
{
	uint16_t ret;
	
	ret = REG_ADC_DR(ch);
	
	ret = ret&0x0fff;  	//获得对应通道的有效数据
	
	
	return ret;
}

/************************************************************************
 * Function   	: adc_get_data
 * Description	: adc_get_data   		 获取ADC通道数据//查询专用
 * Input 		: uint8_t ch_num   		 ADC通道选择
 * Output 		: none
 * Return		: uint8_t ret    		 ADC通道有效数据
 ************************************************************************/
uint16_t adc_get_data(uint8_t ch_num)
{
	uint16_t ret ;
	
	while(!(REG_ADC_ISR&(1<<ch_num)));
	ret = REG_ADC_DR(ch_num);
	ret = ret&0x0fff;  	//获得对应通道的有效数据
	
	REG_ADC_ICR = 1 << ch_num;  

	return ret;
}

/************************************************************************
 * Function   	: adc_rxfifo_config
 * Description	: adc_rxfifo_config			ADC接收缓存配置
 * Input 		: uint8_t rxfigo_trig_level	ADC的rxfifo中断触发数量控制（ADC_RXFIFO_LEAST_8DATA/ADC_RXFIFO_LEAST_1DATA）
 *            	  uint8_t dma_access		ADC的rxfifo访问模式（ADC_DMA_ACCESS_RXFIFO/ADC_CPU_ACCESS_RXFIFO）
 *			  	  uint8_t state				使能状态（1：ENABLE  0：DISABLE）
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_rxfifo_config(uint8_t rxfigo_trig_level, uint8_t dma_access, uint8_t state)
{
	if(state == ENABLE)
	{
		
		REG_ADC_GCR = ((REG_ADC_GCR & (~(1<<2))) | (dma_access<<2));
		REG_ADC_GCR = ((REG_ADC_GCR & (~(1<<4))) | (rxfigo_trig_level<<4));
		REG_ADC_GCR |= (1<<3);		
	}
	else
	{
		REG_ADC_GCR &= ~(1<<3);
	}
}

/************************************************************************
 * Function   	: adc_hardware_trig_config
 * Description	: adc_hardware_trig_config	ADC硬件触发配置
 * Input 		: uint8_t hardware_type		ADC的硬件触发类型选择
 *            	  uint8_t polarity_type		ADC的硬件触发极性选择
 *			  	  uint8_t state				使能状态（1：ENABLE  0：DISABLE）
 * Output 		: none
 * Return		: none
 ************************************************************************/
void adc_hardware_trig_config(uint8_t hardware_type, uint8_t polarity_type, uint8_t state)
{
	if(state == ENABLE)
	{
		REG_ADC_HDT	|= (1<<hardware_type);
		if(hardware_type>15)
			REG_ADC_HDSET1 = ((REG_ADC_HDSET1 & (~(3<<((hardware_type-16)*2)))) | (polarity_type<<((hardware_type-16)*2)));
		else
			REG_ADC_HDSET0 = ((REG_ADC_HDSET0 & (~(3<<(hardware_type*2)))) | (polarity_type<<(hardware_type*2)));
	}
	else
	{
		REG_ADC_HDT	&= ~(1<<hardware_type);
	}
}

