/************************************************************
  Copyright (C), 2025, cerlink Tech. Co., Ltd.
  FileName: drv_adc.c
  Author:   dufresne     Version :  1.0        Date:2025.09.15
  Description:     // 模块描述      
  Version:         // 版本信息
  Function List:   // 主要函数及其功能
    1. -------
  History:         // 历史修改记录
      <author>  <time>   <version >   <desc>
      David    96/10/12     1.0     build this moudle  
***********************************************************/

#include "drv_adc.h"


#ifdef DMAX

/*全局声明区*/
uint16_t USER_ADC_DMA_DATA_BUFF[16] = {0};

#endif

float adc1_v[LB_V_TIMES];
float curadc1_out_v;
uint8_t adc1_v_num = 0;

float adc1_i[LB_I_TIMES];
float curadc1_out_i;
uint8_t adc1_i_num = 0;

float tmp75[LB_T_TIMES];
float curtmp75_out;
uint8_t tmp75_num;



bool filter_v_flag = 0;//滤波开启标志位，全局改变一次
bool filter_i_flag = 0;//滤波开启标志位，全局改变一次
bool filter_t_flag = 0;//滤波开启标志位，全局改变一次



/* ---------------------------------------------------配置---------------------------------------------------- */

void adc_init(void)
{
    adc_rcu_config();
    adc_gpio_config();
	
	#ifdef DMAX
    adc_dma_config();
	#endif

    adc_config();

	#ifdef ADCX_IRQn
	adc_interrupt_int();
	#endif

}
/* 时钟+GPIO */
void adc_rcu_config(void)
{
    /* 启用ADC1时钟 */
    rcu_periph_clock_enable(RCU_ADC1);
	#ifdef DMAX
    /* peripheral clock enable */
    rcu_periph_clock_enable(RCU_DMA1);//ADC只能用DMA1
	#endif
    /* 启用GPIOC时钟 */
    rcu_periph_clock_enable(RCU_GPIOC);
    adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
}

void adc_gpio_config(void)
{
    /* 配置ADC引脚为模拟输入模式 */
    gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, VOLTAGE_ADC_PIN);
    gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, CURRENT_ADC_PIN);
}

#ifdef DMAX

/* DMA配置 */
void adc_dma_config(void)
{
    /* ADC_DMA_channel configuration */
    dma_single_data_parameter_struct dma_single_data_parameter; //这里使用单数据模式，每次只传输一个数据单元，因为总数据量不大，且数据个数不固定

    /* ADC DMA_channel configuration */
    dma_deinit										  (DMA1, USER_DMA_ADC_CHANNEL);

    /* initialize DMA single data mode */
	dma_single_data_parameter.periph_addr			= (uint32_t)(&ADC_RDATA(ADCX));		//配置外设数据源地址，宏决定ADCx
	dma_single_data_parameter.periph_inc			= DMA_PERIPH_INCREASE_DISABLE;		//关闭外设增量，只需要每次对该数据源寄存器访问
	dma_single_data_parameter.memory0_addr			= (uint32_t)USER_ADC_DMA_DATA_BUFF;	//先写死，然后外部用的话需要extern
	dma_single_data_parameter.memory_inc			= DMA_MEMORY_INCREASE_ENABLE;		//开启内存增量，以存放不同通道的ADC数据
	dma_single_data_parameter.periph_memory_width	= DMA_PERIPH_WIDTH_16BIT;			//ADC数据为16位的寄存器，数据目的地为16位
	dma_single_data_parameter.circular_mode			= DMA_CIRCULAR_MODE_ENABLE;			//循环模式，由ADC通知DMA进行
	dma_single_data_parameter.direction				= DMA_PERIPH_TO_MEMORY;				//ADC外设到RAM全局变量 DMA外设到内存模式开启
	dma_single_data_parameter.number				= CHANNEL_LENGTH;					//宏决定传输个数
	dma_single_data_parameter.priority				= DMA_PRIORITY_ULTRA_HIGH;			//优先级设为超高，可改用宏
	dma_single_data_mode_init						  (DMAX, USER_DMA_ADC_CHANNEL, &dma_single_data_parameter);
    /* 配置 dma 子连接 */
	dma_channel_subperipheral_select				  (DMAX, USER_DMA_ADC_CHANNEL, USER_DMA_ADC_SUBPERI);
	
    /* enable DMA circulation mode */
    dma_circulation_enable							  (DMAX, USER_DMA_ADC_CHANNEL);// 开启循环模式

	 /* enable DMA it */
	dma_interrupt_enable							  (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_FTF);//DMA传输完成标志位 打开全部完成中断		4
	// dma_interrupt_enable							  (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_TAE);//传输错误中断使能位						2
	// dma_interrupt_enable							  (DMAX,USER_DMA_ADC_CHANNEL,DMA_INT_FEE);//										7
	nvic_irq_enable									  (DMA_ADC_IRQn, DMA_ADC_PRIORITY_PRE, DMA_ADC_PRIORITY_SUB);

    dma_channel_enable								  (DMAX, USER_DMA_ADC_CHANNEL);// enable DMA channel
}


/**
* @brief 根据转换序列的序列号，获取转换序列中的ADC测量值
* @param 配置ADC通道结构体中的成员序号
* @return 测量并转换后的值
*/

uint16_t BSP_ADCDataAcquire(uint8_t _index)
{
	return USER_ADC_DMA_DATA_BUFF[_index];
}

/*
*****************************************************************************
*  函数名 : DMA1_Channel0_IRQHandler
*  功能说明: DMA_ADC中断服务回调函数
*  形   参 : 无
*  返回值 :  无
*****************************************************************************
*/
__weak void DMA1_Channel0_IRQHandler(void)
{

}
#endif


void adc_config(void)
{
    adc_deinit();// 复位ADC配置（可选，但建议初始化时做一次）

    adc_special_function_config			(ADCX, ADC_SCAN_MODE, SCAN_STATUS);// 扫描模式
    adc_special_function_config			(ADCX, ADC_CONTINUOUS_MODE, CONTINUOUS_STATUS);// 连续模式

    adc_data_alignment_config			(ADCX, ADC_DATAALIGN_RIGHT);// 配置数据对齐方式为右对齐
    adc_resolution_config				(ADCX, ADC_RESOLUTION_12B);// 配置ADC分辨率：12位

    /* 设置转换通道序列 */
    adc_channel_length_config			(ADCX, SEQUENCE_CHANNEL, CHANNEL_LENGTH);// 配置规则序列的长度：2个通道

    if (SEQUENCE_CHANNEL == ADC_INSERTED_CHANNEL)
    {
        //通道注入顺序
        /* 配置规则序列：序号0是电压通道，序号1是电流通道 */
        adc_inserted_channel_config     (ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);//电压
        adc_inserted_channel_config     (ADCX, 1, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);//电流
    }
    #ifdef DMAX
    adc_regular_channel_config(ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);
    adc_regular_channel_config(ADCX, 1, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);
    #endif

    /* 使能外部触发：这里使用软件触发，所以先禁用硬件触发 */
    // adc_external_trigger_source_config(ADCX, SEQUENCE_CHANNEL, ADC_EXTTRIG_ROUTINE_T0_CH0);
    adc_external_trigger_config			(ADCX, SEQUENCE_CHANNEL, DISABLE);

	#ifdef DMAX
	/* ADC dma config */
	adc_dma_request_after_last_enable	(ADCX);
	adc_dma_mode_enable					(ADCX);
	#endif
    adc_enable							(ADCX);				// 开启ADC
    // rt_thread_mdelay(50);									// 等待ADC稳定后续接校准
    delay_ms(50);
    adc_calibration_enable				(ADCX);				// 执行ADC自校准

	#ifdef DMAX
    adc_software_trigger_enable			(ADCX, SEQUENCE_CHANNEL);	// 软件触发使能 后续接读取
	#endif
}

#ifdef ADCX_IRQn
void adc_interrupt_int(void)
{
    adc_interrupt_enable	(ADCX, ADC_INT_EOC);
    nvic_irq_enable			(ADCX_IRQn, ADCX_PRIORITY_PRE, ADCX_PRIORITY_SUB);

}
#endif



/* ---------------------------------------------------采集---------------------------------------------------- */

// adc电压采集
float ptz_Voltage_collect_adc1_task()
{
    uint16_t value_V = 0;

    #ifdef ADC_MODE_0
    /* 模式1需先配置 */
    adc_regular_channel_config(ADCX, 0, VOLTAGE_ADC_CHANNEL, ADC_SAMPLETIME_480);
    #endif

    #if (defined ADC_MODE_0) || (defined ADC_MODE_1)
    /* 模式1 + 2 */
    adc_software_trigger_enable(ADCX, SEQUENCE_CHANNEL);// 软件触发使能 后续接读取
    #endif

    #ifdef ADC_MODE_0
    while(adc_flag_get(ADCX, ADC_FLAG_EOC) == RESET); // 等待转换结束
    adc_flag_clear(ADCX, ADC_FLAG_EOC);
    value_V = adc_regular_data_read(ADCX); // 读取规则组数据寄存器
    #endif

    #ifdef ADC_MODE_1
    while(adc_flag_get(ADCX, ADC_FLAG_EOIC) == RESET); // 等待转换结束
    adc_flag_clear(ADCX, ADC_FLAG_EOIC);
    value_V = adc_inserted_data_read(ADCX, ADC_INSERTED_CHANNEL_0); // 读取注入组数据寄存器
    #endif

    #ifdef ADC_MODE_3
    value_V = BSP_ADCDataAcquire(0);
    #endif

    /* 间接测量，11倍分压/放大 */
    adc1_v[adc1_v_num] = (float)value_V / 4096.0 * 3.3 * 11;

    adc1_v_num++;
    if(adc1_v_num >= LB_V_TIMES)
    {
        adc1_v_num = 0;
		filter_v_flag = 1;
    }

	if (filter_v_flag)//等读满一组开始滤波
	{
		return Filtering(adc1_v, LB_V_TIMES, LB_V_DEL);
	}

    return 0;
}


// adc电流采集
float ptz_Current_collect_adc1_task()
{
    uint16_t value_I = 0;

    #ifdef ADC_MODE_0
    /* 模式1需先配置 */
    adc_regular_channel_config(ADCX, 0, CURRENT_ADC_CHANNEL, ADC_SAMPLETIME_480);
    #endif

    #if (defined ADC_MODE_0) || (defined ADC_MODE_1)
    /* 模式1 + 2 */
    adc_software_trigger_enable(ADCX, SEQUENCE_CHANNEL);// 软件触发使能 后续接读取
    #endif

    #ifdef ADC_MODE_0
    while(adc_flag_get(ADCX, ADC_FLAG_EOC) == RESET); // 等待转换结束
    adc_flag_clear(ADCX, ADC_FLAG_EOC);
    value_I = adc_regular_data_read(ADCX); // 读取规则组数据寄存器
    #endif

    #ifdef ADC_MODE_1
    while(adc_flag_get(ADCX, ADC_FLAG_EOIC) == RESET); // 等待转换结束
    adc_flag_clear(ADCX, ADC_FLAG_EOIC);
    value_I = adc_inserted_data_read(ADCX, ADC_INSERTED_CHANNEL_1); // 读取注入组数据寄存器
    #endif

    #ifdef ADC_MODE_3
    value_I = BSP_ADCDataAcquire(1);
    #endif

    /* 间接测量 */
    adc1_i[adc1_i_num] = ((float)value_I / 4096.0  * 3.3) * 10;

    adc1_i_num++;
    if(adc1_i_num >= LB_I_TIMES)
    {
        adc1_i_num = 0;
		filter_i_flag = 1;
    }
	
	if (filter_i_flag)
	{
		return Filtering(adc1_i, LB_I_TIMES, LB_I_DEL);
	}
	return 0;
}


/* 温度采集 */
float ptz_temperature_collect_tmp75_task()
{
    #ifdef  HARDWARE_I2C
	uint8_t tempHL[2];
	uint16_t tempCode = 0;
	float temp = 0;
    
    uint8_t temp_addr[1] = {TEMP_REGISTER_ADDRESS};

    i2c_write(temp_addr, 1, TMP75_ADDRESS);
	i2c_read(tempHL, 2, TMP75_ADDRESS);

	tempCode = (tempHL[0] << 8) | tempHL[1];
	tempCode = tempCode >> 6;

	if (tempCode & 0x200) // 负温度
	{
		tempCode &= 0x1ff;
		temp = ((float)tempCode - 512) / 4;
	}
	else
	{
		temp = (float)tempCode / 4;
	}

	tmp75[tmp75_num] = temp;
    #endif

    #ifdef  SOFTWARE_I2C

	tmp75[tmp75_num] = tmp75_read_temp();
    
    #endif
    tmp75_num ++;

    if(tmp75_num >= LB_T_TIMES)
    {
        tmp75_num = 0;
		filter_t_flag = 1;
    }
	if (filter_t_flag)
	{
		return Filtering(tmp75, LB_T_TIMES, LB_T_DEL);
	}
    return 0;
}

/* 滤波函数 */
float Filtering(float *filter, uint8_t filterlens, uint8_t filterdel)
{
    uint8_t j,k;
    float tem;
	float curadc = 0;

	for(j = 0; j < filterlens - 1; j++)// 采样值由小到大排列
	{
		for(k = 0; k < filterlens - j - 1; k++)
		{
			if(filter[k] > filter[k + 1])
			{
				tem				= filter[k];
				filter[k]		= filter[k + 1];
				filter[k + 1]	= tem;
			}
		}
	}
	for(uint8_t i = filterdel; i < filterlens - filterdel; i++)
	{
		curadc = curadc + filter[i];
	}
	curadc = curadc / ((float)(filterlens - filterdel * 2));// 去掉一个最大值和一个最小值求平均值
	return curadc;
}

