/************************************************************************************************/
/**
* @file               adc_bsp.c
* @author             MCU Ecosystem Development Team
* @brief              ADC BSP驱动函数。
*
*
**************************************************************************************************
* @attention
* Copyright (c) CEC Huada Electronic Design Co.,Ltd. All rights reserved.
*
**************************************************************************************************
*/

/*------------------------------------------includes--------------------------------------------*/
#include "adc_bsp.h"

/*--------------------------------------------define--------------------------------------------*/
/* ADC采样次数，根据实际需求可调整此值 */
#define ADC_SAMPLE_NUM                      (10U)

/* ADC校准次数，根据实际需求可调整此值 */
#define ADC_CALIBRATION_NUM                 (8U)

/*------------------------------------------functions-------------------------------------------*/

/**
* @brief  转换ADC通道采样值为实际电压值
* @note   此函数先利用内部基准电压VBGR来计算ADC参考电压VDDA的实际电压，
*         然后再计算通道采样电压，单位mV
* @retval uint32_t 转换结果
*/
uint32_t bsp_adc_get_channel_voltage(void)
{
    uint8_t i = 0;
    uint32_t get_vbgr_sample_sum = 0;
    uint32_t get_vbgr_sample = 0;
    uint32_t get_channel_voltage = 0;
    
     /* ADC多次采样取平均，提升采样精度 */
    for(i = 0; i < ADC_SAMPLE_NUM; i++)
    {
        /* 启动转换 */
        std_adc_start_conversion();
        
        /* 等待ADC转换完成 */
        while(std_adc_get_flag(ADC_FLAG_EOC) == 0);

        /* 清除EOC标志 */
        std_adc_clear_flag(ADC_FLAG_EOC);
        
        /* 获取外部通道采样值 */
        get_vbgr_sample_sum += std_adc_get_conversion_value();
    }
    
    /* 计算平均值 */
    get_vbgr_sample = get_vbgr_sample_sum / ADC_SAMPLE_NUM;
    
    /* 校准参考电压 */
    if(get_vbgr_sample != 0) 
    {
        get_channel_voltage = (VBGR_CAL_VREF * (*VBGR_CAL_ADDR)) / get_vbgr_sample;
    }
    return get_channel_voltage;
}

/**
* @brief  ADC配置并校准
* @note   此函数会更改ADC配置，调用该函数后根据应用需求重新配置ADC。ADC校准要修改的配置有：转换模式配置为单次扫描，
*         触发方式配置为软件触发，采样时间选择3个ADC_CK时钟周期，并禁止ADC的EOC/EOS/EOSAMP中断
* @retval 无
*/
void bsp_adc_calibrate(void)
{  
    std_adc_init_t adc_config = {0};
    
    /* 使能ADC时钟 */
    std_rcc_apb2_clk_enable(RCC_PERIPH_CLK_ADC);

    /* 禁止ADC */
    std_adc_disable();
    
    /* ADC校准配置 */
    adc_config.clock_presc    = ADC_CLK_DIV3;                         /* ADC时钟, 3分频 */
    adc_config.conver_mode    = ADC_SINGLE_CONVER_MODE;               /* 单次转换模式 */
    adc_config.overrun_mode   = ADC_OVRN_MODE_OVERWRITTEN;            /* 数据溢出模式: 数据覆盖 */
    adc_config.trigger_edge   = ADC_TRIG_SW;                          /* 软件触发 */
    adc_config.sampt          = ADC_SAMPTIME_3CYCLES;                 /* 采样时间设置为3CLK */
    adc_config.scan_dir       = ADC_SCAN_DIR_FORWARD;                 /* 正向扫描 */
    std_adc_init(&adc_config);
 
    /* ADC选择间歇工作模式 */
    std_adc_mode_config(ADC_MODE_INTERVAL);
    
    /* 使能ADC */
    std_adc_enable();

    /* 等待ADC使能状态稳定 */
    std_delayus(ADC_EN_DELAY);
    
    /* ADC校准前,需要禁止ADC的EOC/EOS/EOSAMP中断 */
    std_adc_interrupt_disable(ADC_INTERRUPT_EOC | ADC_INTERRUPT_EOS |ADC_INTERRUPT_EOSAMP);
    
    /* 启动ADC校准 */
    std_adc_calibrate(ADC_CALIBRATION_NUM);
    
    /* 禁止ADC 
        注意: ADC应用时，如果工作时钟频率不更改，ADC不用被禁止，即下述语句可以被注释掉，不用再重新使能ADC和等待稳定，提高ADC配置效率
    */
    std_adc_disable();
}

